2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7316 if(typeof c.dataIndex == "undefined"){
7319 if(typeof c.renderer == "string"){
7320 c.renderer = Roo.util.Format[c.renderer];
7322 if(typeof c.id == "undefined"){
7325 if(c.editor && c.editor.xtype){
7326 c.editor = Roo.factory(c.editor, Roo.grid);
7328 if(c.editor && c.editor.isFormField){
7329 c.editor = new Roo.grid.GridEditor(c.editor);
7331 this.lookup[c.id] = c;
7335 * The width of columns which have no width specified (defaults to 100)
7338 this.defaultWidth = 100;
7341 * Default sortable of columns which have no sortable specified (defaults to false)
7344 this.defaultSortable = false;
7348 * @event widthchange
7349 * Fires when the width of a column changes.
7350 * @param {ColumnModel} this
7351 * @param {Number} columnIndex The column index
7352 * @param {Number} newWidth The new width
7354 "widthchange": true,
7356 * @event headerchange
7357 * Fires when the text of a header changes.
7358 * @param {ColumnModel} this
7359 * @param {Number} columnIndex The column index
7360 * @param {Number} newText The new header text
7362 "headerchange": true,
7364 * @event hiddenchange
7365 * Fires when a column is hidden or "unhidden".
7366 * @param {ColumnModel} this
7367 * @param {Number} columnIndex The column index
7368 * @param {Boolean} hidden true if hidden, false otherwise
7370 "hiddenchange": true,
7372 * @event columnmoved
7373 * Fires when a column is moved.
7374 * @param {ColumnModel} this
7375 * @param {Number} oldIndex
7376 * @param {Number} newIndex
7378 "columnmoved" : true,
7380 * @event columlockchange
7381 * Fires when a column's locked state is changed
7382 * @param {ColumnModel} this
7383 * @param {Number} colIndex
7384 * @param {Boolean} locked true if locked
7386 "columnlockchange" : true
7388 Roo.grid.ColumnModel.superclass.constructor.call(this);
7390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7392 * @cfg {String} header The header text to display in the Grid view.
7395 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7396 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7397 * specified, the column's index is used as an index into the Record's data Array.
7400 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7401 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7404 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7405 * Defaults to the value of the {@link #defaultSortable} property.
7406 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7409 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7412 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7415 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7418 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7421 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7422 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7423 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7424 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7427 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7430 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7433 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7436 * @cfg {String} cursor (Optional)
7439 * @cfg {String} tooltip (Optional)
7442 * @cfg {Number} xs (Optional)
7445 * @cfg {Number} sm (Optional)
7448 * @cfg {Number} md (Optional)
7451 * @cfg {Number} lg (Optional)
7454 * Returns the id of the column at the specified index.
7455 * @param {Number} index The column index
7456 * @return {String} the id
7458 getColumnId : function(index){
7459 return this.config[index].id;
7463 * Returns the column for a specified id.
7464 * @param {String} id The column id
7465 * @return {Object} the column
7467 getColumnById : function(id){
7468 return this.lookup[id];
7473 * Returns the column for a specified dataIndex.
7474 * @param {String} dataIndex The column dataIndex
7475 * @return {Object|Boolean} the column or false if not found
7477 getColumnByDataIndex: function(dataIndex){
7478 var index = this.findColumnIndex(dataIndex);
7479 return index > -1 ? this.config[index] : false;
7483 * Returns the index for a specified column id.
7484 * @param {String} id The column id
7485 * @return {Number} the index, or -1 if not found
7487 getIndexById : function(id){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].id == id){
7497 * Returns the index for a specified column dataIndex.
7498 * @param {String} dataIndex The column dataIndex
7499 * @return {Number} the index, or -1 if not found
7502 findColumnIndex : function(dataIndex){
7503 for(var i = 0, len = this.config.length; i < len; i++){
7504 if(this.config[i].dataIndex == dataIndex){
7512 moveColumn : function(oldIndex, newIndex){
7513 var c = this.config[oldIndex];
7514 this.config.splice(oldIndex, 1);
7515 this.config.splice(newIndex, 0, c);
7516 this.dataMap = null;
7517 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7520 isLocked : function(colIndex){
7521 return this.config[colIndex].locked === true;
7524 setLocked : function(colIndex, value, suppressEvent){
7525 if(this.isLocked(colIndex) == value){
7528 this.config[colIndex].locked = value;
7530 this.fireEvent("columnlockchange", this, colIndex, value);
7534 getTotalLockedWidth : function(){
7536 for(var i = 0; i < this.config.length; i++){
7537 if(this.isLocked(i) && !this.isHidden(i)){
7538 this.totalWidth += this.getColumnWidth(i);
7544 getLockedCount : function(){
7545 for(var i = 0, len = this.config.length; i < len; i++){
7546 if(!this.isLocked(i)){
7551 return this.config.length;
7555 * Returns the number of columns.
7558 getColumnCount : function(visibleOnly){
7559 if(visibleOnly === true){
7561 for(var i = 0, len = this.config.length; i < len; i++){
7562 if(!this.isHidden(i)){
7568 return this.config.length;
7572 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7573 * @param {Function} fn
7574 * @param {Object} scope (optional)
7575 * @return {Array} result
7577 getColumnsBy : function(fn, scope){
7579 for(var i = 0, len = this.config.length; i < len; i++){
7580 var c = this.config[i];
7581 if(fn.call(scope||this, c, i) === true){
7589 * Returns true if the specified column is sortable.
7590 * @param {Number} col The column index
7593 isSortable : function(col){
7594 if(typeof this.config[col].sortable == "undefined"){
7595 return this.defaultSortable;
7597 return this.config[col].sortable;
7601 * Returns the rendering (formatting) function defined for the column.
7602 * @param {Number} col The column index.
7603 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7605 getRenderer : function(col){
7606 if(!this.config[col].renderer){
7607 return Roo.grid.ColumnModel.defaultRenderer;
7609 return this.config[col].renderer;
7613 * Sets the rendering (formatting) function for a column.
7614 * @param {Number} col The column index
7615 * @param {Function} fn The function to use to process the cell's raw data
7616 * to return HTML markup for the grid view. The render function is called with
7617 * the following parameters:<ul>
7618 * <li>Data value.</li>
7619 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7620 * <li>css A CSS style string to apply to the table cell.</li>
7621 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7622 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7623 * <li>Row index</li>
7624 * <li>Column index</li>
7625 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7627 setRenderer : function(col, fn){
7628 this.config[col].renderer = fn;
7632 * Returns the width for the specified column.
7633 * @param {Number} col The column index
7636 getColumnWidth : function(col){
7637 return this.config[col].width * 1 || this.defaultWidth;
7641 * Sets the width for a column.
7642 * @param {Number} col The column index
7643 * @param {Number} width The new width
7645 setColumnWidth : function(col, width, suppressEvent){
7646 this.config[col].width = width;
7647 this.totalWidth = null;
7649 this.fireEvent("widthchange", this, col, width);
7654 * Returns the total width of all columns.
7655 * @param {Boolean} includeHidden True to include hidden column widths
7658 getTotalWidth : function(includeHidden){
7659 if(!this.totalWidth){
7660 this.totalWidth = 0;
7661 for(var i = 0, len = this.config.length; i < len; i++){
7662 if(includeHidden || !this.isHidden(i)){
7663 this.totalWidth += this.getColumnWidth(i);
7667 return this.totalWidth;
7671 * Returns the header for the specified column.
7672 * @param {Number} col The column index
7675 getColumnHeader : function(col){
7676 return this.config[col].header;
7680 * Sets the header for a column.
7681 * @param {Number} col The column index
7682 * @param {String} header The new header
7684 setColumnHeader : function(col, header){
7685 this.config[col].header = header;
7686 this.fireEvent("headerchange", this, col, header);
7690 * Returns the tooltip for the specified column.
7691 * @param {Number} col The column index
7694 getColumnTooltip : function(col){
7695 return this.config[col].tooltip;
7698 * Sets the tooltip for a column.
7699 * @param {Number} col The column index
7700 * @param {String} tooltip The new tooltip
7702 setColumnTooltip : function(col, tooltip){
7703 this.config[col].tooltip = tooltip;
7707 * Returns the dataIndex for the specified column.
7708 * @param {Number} col The column index
7711 getDataIndex : function(col){
7712 return this.config[col].dataIndex;
7716 * Sets the dataIndex for a column.
7717 * @param {Number} col The column index
7718 * @param {Number} dataIndex The new dataIndex
7720 setDataIndex : function(col, dataIndex){
7721 this.config[col].dataIndex = dataIndex;
7727 * Returns true if the cell is editable.
7728 * @param {Number} colIndex The column index
7729 * @param {Number} rowIndex The row index - this is nto actually used..?
7732 isCellEditable : function(colIndex, rowIndex){
7733 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7737 * Returns the editor defined for the cell/column.
7738 * return false or null to disable editing.
7739 * @param {Number} colIndex The column index
7740 * @param {Number} rowIndex The row index
7743 getCellEditor : function(colIndex, rowIndex){
7744 return this.config[colIndex].editor;
7748 * Sets if a column is editable.
7749 * @param {Number} col The column index
7750 * @param {Boolean} editable True if the column is editable
7752 setEditable : function(col, editable){
7753 this.config[col].editable = editable;
7758 * Returns true if the column is hidden.
7759 * @param {Number} colIndex The column index
7762 isHidden : function(colIndex){
7763 return this.config[colIndex].hidden;
7768 * Returns true if the column width cannot be changed
7770 isFixed : function(colIndex){
7771 return this.config[colIndex].fixed;
7775 * Returns true if the column can be resized
7778 isResizable : function(colIndex){
7779 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7782 * Sets if a column is hidden.
7783 * @param {Number} colIndex The column index
7784 * @param {Boolean} hidden True if the column is hidden
7786 setHidden : function(colIndex, hidden){
7787 this.config[colIndex].hidden = hidden;
7788 this.totalWidth = null;
7789 this.fireEvent("hiddenchange", this, colIndex, hidden);
7793 * Sets the editor for a column.
7794 * @param {Number} col The column index
7795 * @param {Object} editor The editor object
7797 setEditor : function(col, editor){
7798 this.config[col].editor = editor;
7802 Roo.grid.ColumnModel.defaultRenderer = function(value)
7804 if(typeof value == "object") {
7807 if(typeof value == "string" && value.length < 1){
7811 return String.format("{0}", value);
7814 // Alias for backwards compatibility
7815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7818 * Ext JS Library 1.1.1
7819 * Copyright(c) 2006-2007, Ext JS, LLC.
7821 * Originally Released Under LGPL - original licence link has changed is not relivant.
7824 * <script type="text/javascript">
7828 * @class Roo.LoadMask
7829 * A simple utility class for generically masking elements while loading data. If the element being masked has
7830 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7831 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7832 * element's UpdateManager load indicator and will be destroyed after the initial load.
7834 * Create a new LoadMask
7835 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7836 * @param {Object} config The config object
7838 Roo.LoadMask = function(el, config){
7839 this.el = Roo.get(el);
7840 Roo.apply(this, config);
7842 this.store.on('beforeload', this.onBeforeLoad, this);
7843 this.store.on('load', this.onLoad, this);
7844 this.store.on('loadexception', this.onLoadException, this);
7845 this.removeMask = false;
7847 var um = this.el.getUpdateManager();
7848 um.showLoadIndicator = false; // disable the default indicator
7849 um.on('beforeupdate', this.onBeforeLoad, this);
7850 um.on('update', this.onLoad, this);
7851 um.on('failure', this.onLoad, this);
7852 this.removeMask = true;
7856 Roo.LoadMask.prototype = {
7858 * @cfg {Boolean} removeMask
7859 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7860 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7864 * The text to display in a centered loading message box (defaults to 'Loading...')
7868 * @cfg {String} msgCls
7869 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7871 msgCls : 'x-mask-loading',
7874 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7880 * Disables the mask to prevent it from being displayed
7882 disable : function(){
7883 this.disabled = true;
7887 * Enables the mask so that it can be displayed
7889 enable : function(){
7890 this.disabled = false;
7893 onLoadException : function()
7897 if (typeof(arguments[3]) != 'undefined') {
7898 Roo.MessageBox.alert("Error loading",arguments[3]);
7902 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7903 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7910 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7915 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7919 onBeforeLoad : function(){
7921 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7926 destroy : function(){
7928 this.store.un('beforeload', this.onBeforeLoad, this);
7929 this.store.un('load', this.onLoad, this);
7930 this.store.un('loadexception', this.onLoadException, this);
7932 var um = this.el.getUpdateManager();
7933 um.un('beforeupdate', this.onBeforeLoad, this);
7934 um.un('update', this.onLoad, this);
7935 um.un('failure', this.onLoad, this);
7946 * @class Roo.bootstrap.Table
7947 * @extends Roo.bootstrap.Component
7948 * Bootstrap Table class
7949 * @cfg {String} cls table class
7950 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7951 * @cfg {String} bgcolor Specifies the background color for a table
7952 * @cfg {Number} border Specifies whether the table cells should have borders or not
7953 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7954 * @cfg {Number} cellspacing Specifies the space between cells
7955 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7956 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7957 * @cfg {String} sortable Specifies that the table should be sortable
7958 * @cfg {String} summary Specifies a summary of the content of a table
7959 * @cfg {Number} width Specifies the width of a table
7960 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7962 * @cfg {boolean} striped Should the rows be alternative striped
7963 * @cfg {boolean} bordered Add borders to the table
7964 * @cfg {boolean} hover Add hover highlighting
7965 * @cfg {boolean} condensed Format condensed
7966 * @cfg {boolean} responsive Format condensed
7967 * @cfg {Boolean} loadMask (true|false) default false
7968 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7969 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7970 * @cfg {Boolean} rowSelection (true|false) default false
7971 * @cfg {Boolean} cellSelection (true|false) default false
7972 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7973 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7974 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7975 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7979 * Create a new Table
7980 * @param {Object} config The config object
7983 Roo.bootstrap.Table = function(config){
7984 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7989 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7990 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7991 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7992 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7994 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7996 this.sm.grid = this;
7997 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7998 this.sm = this.selModel;
7999 this.sm.xmodule = this.xmodule || false;
8002 if (this.cm && typeof(this.cm.config) == 'undefined') {
8003 this.colModel = new Roo.grid.ColumnModel(this.cm);
8004 this.cm = this.colModel;
8005 this.cm.xmodule = this.xmodule || false;
8008 this.store= Roo.factory(this.store, Roo.data);
8009 this.ds = this.store;
8010 this.ds.xmodule = this.xmodule || false;
8013 if (this.footer && this.store) {
8014 this.footer.dataSource = this.ds;
8015 this.footer = Roo.factory(this.footer);
8022 * Fires when a cell is clicked
8023 * @param {Roo.bootstrap.Table} this
8024 * @param {Roo.Element} el
8025 * @param {Number} rowIndex
8026 * @param {Number} columnIndex
8027 * @param {Roo.EventObject} e
8031 * @event celldblclick
8032 * Fires when a cell is double clicked
8033 * @param {Roo.bootstrap.Table} this
8034 * @param {Roo.Element} el
8035 * @param {Number} rowIndex
8036 * @param {Number} columnIndex
8037 * @param {Roo.EventObject} e
8039 "celldblclick" : true,
8042 * Fires when a row is clicked
8043 * @param {Roo.bootstrap.Table} this
8044 * @param {Roo.Element} el
8045 * @param {Number} rowIndex
8046 * @param {Roo.EventObject} e
8050 * @event rowdblclick
8051 * Fires when a row is double clicked
8052 * @param {Roo.bootstrap.Table} this
8053 * @param {Roo.Element} el
8054 * @param {Number} rowIndex
8055 * @param {Roo.EventObject} e
8057 "rowdblclick" : true,
8060 * Fires when a mouseover occur
8061 * @param {Roo.bootstrap.Table} this
8062 * @param {Roo.Element} el
8063 * @param {Number} rowIndex
8064 * @param {Number} columnIndex
8065 * @param {Roo.EventObject} e
8070 * Fires when a mouseout occur
8071 * @param {Roo.bootstrap.Table} this
8072 * @param {Roo.Element} el
8073 * @param {Number} rowIndex
8074 * @param {Number} columnIndex
8075 * @param {Roo.EventObject} e
8080 * Fires when a row is rendered, so you can change add a style to it.
8081 * @param {Roo.bootstrap.Table} this
8082 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8086 * @event rowsrendered
8087 * Fires when all the rows have been rendered
8088 * @param {Roo.bootstrap.Table} this
8090 'rowsrendered' : true,
8092 * @event contextmenu
8093 * The raw contextmenu event for the entire grid.
8094 * @param {Roo.EventObject} e
8096 "contextmenu" : true,
8098 * @event rowcontextmenu
8099 * Fires when a row is right clicked
8100 * @param {Roo.bootstrap.Table} this
8101 * @param {Number} rowIndex
8102 * @param {Roo.EventObject} e
8104 "rowcontextmenu" : true,
8106 * @event cellcontextmenu
8107 * Fires when a cell is right clicked
8108 * @param {Roo.bootstrap.Table} this
8109 * @param {Number} rowIndex
8110 * @param {Number} cellIndex
8111 * @param {Roo.EventObject} e
8113 "cellcontextmenu" : true,
8115 * @event headercontextmenu
8116 * Fires when a header is right clicked
8117 * @param {Roo.bootstrap.Table} this
8118 * @param {Number} columnIndex
8119 * @param {Roo.EventObject} e
8121 "headercontextmenu" : true
8125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8151 rowSelection : false,
8152 cellSelection : false,
8155 // Roo.Element - the tbody
8157 // Roo.Element - thead element
8160 container: false, // used by gridpanel...
8166 auto_hide_footer : false,
8168 getAutoCreate : function()
8170 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8177 if (this.scrollBody) {
8178 cfg.cls += ' table-body-fixed';
8181 cfg.cls += ' table-striped';
8185 cfg.cls += ' table-hover';
8187 if (this.bordered) {
8188 cfg.cls += ' table-bordered';
8190 if (this.condensed) {
8191 cfg.cls += ' table-condensed';
8193 if (this.responsive) {
8194 cfg.cls += ' table-responsive';
8198 cfg.cls+= ' ' +this.cls;
8201 // this lot should be simplifed...
8214 ].forEach(function(k) {
8222 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8225 if(this.store || this.cm){
8226 if(this.headerShow){
8227 cfg.cn.push(this.renderHeader());
8230 cfg.cn.push(this.renderBody());
8232 if(this.footerShow){
8233 cfg.cn.push(this.renderFooter());
8235 // where does this come from?
8236 //cfg.cls+= ' TableGrid';
8239 return { cn : [ cfg ] };
8242 initEvents : function()
8244 if(!this.store || !this.cm){
8247 if (this.selModel) {
8248 this.selModel.initEvents();
8252 //Roo.log('initEvents with ds!!!!');
8254 this.mainBody = this.el.select('tbody', true).first();
8255 this.mainHead = this.el.select('thead', true).first();
8256 this.mainFoot = this.el.select('tfoot', true).first();
8262 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8263 e.on('click', _this.sort, _this);
8266 this.mainBody.on("click", this.onClick, this);
8267 this.mainBody.on("dblclick", this.onDblClick, this);
8269 // why is this done????? = it breaks dialogs??
8270 //this.parent().el.setStyle('position', 'relative');
8274 this.footer.parentId = this.id;
8275 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8278 this.el.select('tfoot tr td').first().addClass('hide');
8283 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8286 this.store.on('load', this.onLoad, this);
8287 this.store.on('beforeload', this.onBeforeLoad, this);
8288 this.store.on('update', this.onUpdate, this);
8289 this.store.on('add', this.onAdd, this);
8290 this.store.on("clear", this.clear, this);
8292 this.el.on("contextmenu", this.onContextMenu, this);
8294 this.mainBody.on('scroll', this.onBodyScroll, this);
8296 this.cm.on("headerchange", this.onHeaderChange, this);
8298 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8302 onContextMenu : function(e, t)
8304 this.processEvent("contextmenu", e);
8307 processEvent : function(name, e)
8309 if (name != 'touchstart' ) {
8310 this.fireEvent(name, e);
8313 var t = e.getTarget();
8315 var cell = Roo.get(t);
8321 if(cell.findParent('tfoot', false, true)){
8325 if(cell.findParent('thead', false, true)){
8327 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8328 cell = Roo.get(t).findParent('th', false, true);
8330 Roo.log("failed to find th in thead?");
8331 Roo.log(e.getTarget());
8336 var cellIndex = cell.dom.cellIndex;
8338 var ename = name == 'touchstart' ? 'click' : name;
8339 this.fireEvent("header" + ename, this, cellIndex, e);
8344 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345 cell = Roo.get(t).findParent('td', false, true);
8347 Roo.log("failed to find th in tbody?");
8348 Roo.log(e.getTarget());
8353 var row = cell.findParent('tr', false, true);
8354 var cellIndex = cell.dom.cellIndex;
8355 var rowIndex = row.dom.rowIndex - 1;
8359 this.fireEvent("row" + name, this, rowIndex, e);
8363 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8369 onMouseover : function(e, el)
8371 var cell = Roo.get(el);
8377 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8378 cell = cell.findParent('td', false, true);
8381 var row = cell.findParent('tr', false, true);
8382 var cellIndex = cell.dom.cellIndex;
8383 var rowIndex = row.dom.rowIndex - 1; // start from 0
8385 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8389 onMouseout : function(e, el)
8391 var cell = Roo.get(el);
8397 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8398 cell = cell.findParent('td', false, true);
8401 var row = cell.findParent('tr', false, true);
8402 var cellIndex = cell.dom.cellIndex;
8403 var rowIndex = row.dom.rowIndex - 1; // start from 0
8405 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8409 onClick : function(e, el)
8411 var cell = Roo.get(el);
8413 if(!cell || (!this.cellSelection && !this.rowSelection)){
8417 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418 cell = cell.findParent('td', false, true);
8421 if(!cell || typeof(cell) == 'undefined'){
8425 var row = cell.findParent('tr', false, true);
8427 if(!row || typeof(row) == 'undefined'){
8431 var cellIndex = cell.dom.cellIndex;
8432 var rowIndex = this.getRowIndex(row);
8434 // why??? - should these not be based on SelectionModel?
8435 if(this.cellSelection){
8436 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8439 if(this.rowSelection){
8440 this.fireEvent('rowclick', this, row, rowIndex, e);
8446 onDblClick : function(e,el)
8448 var cell = Roo.get(el);
8450 if(!cell || (!this.cellSelection && !this.rowSelection)){
8454 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8455 cell = cell.findParent('td', false, true);
8458 if(!cell || typeof(cell) == 'undefined'){
8462 var row = cell.findParent('tr', false, true);
8464 if(!row || typeof(row) == 'undefined'){
8468 var cellIndex = cell.dom.cellIndex;
8469 var rowIndex = this.getRowIndex(row);
8471 if(this.cellSelection){
8472 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8475 if(this.rowSelection){
8476 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8480 sort : function(e,el)
8482 var col = Roo.get(el);
8484 if(!col.hasClass('sortable')){
8488 var sort = col.attr('sort');
8491 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8495 this.store.sortInfo = {field : sort, direction : dir};
8498 Roo.log("calling footer first");
8499 this.footer.onClick('first');
8502 this.store.load({ params : { start : 0 } });
8506 renderHeader : function()
8514 this.totalWidth = 0;
8516 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8518 var config = cm.config[i];
8522 cls : 'x-hcol-' + i,
8524 html: cm.getColumnHeader(i)
8529 if(typeof(config.sortable) != 'undefined' && config.sortable){
8531 c.html = '<i class="glyphicon"></i>' + c.html;
8534 // could use BS4 hidden-..-down
8536 if(typeof(config.lgHeader) != 'undefined'){
8537 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8540 if(typeof(config.mdHeader) != 'undefined'){
8541 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8544 if(typeof(config.smHeader) != 'undefined'){
8545 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8548 if(typeof(config.xsHeader) != 'undefined'){
8549 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8556 if(typeof(config.tooltip) != 'undefined'){
8557 c.tooltip = config.tooltip;
8560 if(typeof(config.colspan) != 'undefined'){
8561 c.colspan = config.colspan;
8564 if(typeof(config.hidden) != 'undefined' && config.hidden){
8565 c.style += ' display:none;';
8568 if(typeof(config.dataIndex) != 'undefined'){
8569 c.sort = config.dataIndex;
8574 if(typeof(config.align) != 'undefined' && config.align.length){
8575 c.style += ' text-align:' + config.align + ';';
8578 if(typeof(config.width) != 'undefined'){
8579 c.style += ' width:' + config.width + 'px;';
8580 this.totalWidth += config.width;
8582 this.totalWidth += 100; // assume minimum of 100 per column?
8585 if(typeof(config.cls) != 'undefined'){
8586 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8589 ['xs','sm','md','lg'].map(function(size){
8591 if(typeof(config[size]) == 'undefined'){
8595 if (!config[size]) { // 0 = hidden
8596 // BS 4 '0' is treated as hide that column and below.
8597 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8601 c.cls += ' col-' + size + '-' + config[size] + (
8602 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8614 renderBody : function()
8624 colspan : this.cm.getColumnCount()
8634 renderFooter : function()
8644 colspan : this.cm.getColumnCount()
8658 // Roo.log('ds onload');
8663 var ds = this.store;
8665 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8666 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8667 if (_this.store.sortInfo) {
8669 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8670 e.select('i', true).addClass(['glyphicon-arrow-up']);
8673 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8674 e.select('i', true).addClass(['glyphicon-arrow-down']);
8679 var tbody = this.mainBody;
8681 if(ds.getCount() > 0){
8682 ds.data.each(function(d,rowIndex){
8683 var row = this.renderRow(cm, ds, rowIndex);
8685 tbody.createChild(row);
8689 if(row.cellObjects.length){
8690 Roo.each(row.cellObjects, function(r){
8691 _this.renderCellObject(r);
8698 var tfoot = this.el.select('tfoot', true).first();
8700 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8702 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8704 var total = this.ds.getTotalCount();
8706 if(this.footer.pageSize < total){
8707 this.mainFoot.show();
8711 Roo.each(this.el.select('tbody td', true).elements, function(e){
8712 e.on('mouseover', _this.onMouseover, _this);
8715 Roo.each(this.el.select('tbody td', true).elements, function(e){
8716 e.on('mouseout', _this.onMouseout, _this);
8718 this.fireEvent('rowsrendered', this);
8724 onUpdate : function(ds,record)
8726 this.refreshRow(record);
8730 onRemove : function(ds, record, index, isUpdate){
8731 if(isUpdate !== true){
8732 this.fireEvent("beforerowremoved", this, index, record);
8734 var bt = this.mainBody.dom;
8736 var rows = this.el.select('tbody > tr', true).elements;
8738 if(typeof(rows[index]) != 'undefined'){
8739 bt.removeChild(rows[index].dom);
8742 // if(bt.rows[index]){
8743 // bt.removeChild(bt.rows[index]);
8746 if(isUpdate !== true){
8747 //this.stripeRows(index);
8748 //this.syncRowHeights(index, index);
8750 this.fireEvent("rowremoved", this, index, record);
8754 onAdd : function(ds, records, rowIndex)
8756 //Roo.log('on Add called');
8757 // - note this does not handle multiple adding very well..
8758 var bt = this.mainBody.dom;
8759 for (var i =0 ; i < records.length;i++) {
8760 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8761 //Roo.log(records[i]);
8762 //Roo.log(this.store.getAt(rowIndex+i));
8763 this.insertRow(this.store, rowIndex + i, false);
8770 refreshRow : function(record){
8771 var ds = this.store, index;
8772 if(typeof record == 'number'){
8774 record = ds.getAt(index);
8776 index = ds.indexOf(record);
8778 return; // should not happen - but seems to
8781 this.insertRow(ds, index, true);
8783 this.onRemove(ds, record, index+1, true);
8785 //this.syncRowHeights(index, index);
8787 this.fireEvent("rowupdated", this, index, record);
8790 insertRow : function(dm, rowIndex, isUpdate){
8793 this.fireEvent("beforerowsinserted", this, rowIndex);
8795 //var s = this.getScrollState();
8796 var row = this.renderRow(this.cm, this.store, rowIndex);
8797 // insert before rowIndex..
8798 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8802 if(row.cellObjects.length){
8803 Roo.each(row.cellObjects, function(r){
8804 _this.renderCellObject(r);
8809 this.fireEvent("rowsinserted", this, rowIndex);
8810 //this.syncRowHeights(firstRow, lastRow);
8811 //this.stripeRows(firstRow);
8818 getRowDom : function(rowIndex)
8820 var rows = this.el.select('tbody > tr', true).elements;
8822 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8825 // returns the object tree for a tr..
8828 renderRow : function(cm, ds, rowIndex)
8830 var d = ds.getAt(rowIndex);
8834 cls : 'x-row-' + rowIndex,
8838 var cellObjects = [];
8840 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8841 var config = cm.config[i];
8843 var renderer = cm.getRenderer(i);
8847 if(typeof(renderer) !== 'undefined'){
8848 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8850 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8851 // and are rendered into the cells after the row is rendered - using the id for the element.
8853 if(typeof(value) === 'object'){
8863 rowIndex : rowIndex,
8868 this.fireEvent('rowclass', this, rowcfg);
8872 cls : rowcfg.rowClass + ' x-col-' + i,
8874 html: (typeof(value) === 'object') ? '' : value
8881 if(typeof(config.colspan) != 'undefined'){
8882 td.colspan = config.colspan;
8885 if(typeof(config.hidden) != 'undefined' && config.hidden){
8886 td.style += ' display:none;';
8889 if(typeof(config.align) != 'undefined' && config.align.length){
8890 td.style += ' text-align:' + config.align + ';';
8892 if(typeof(config.valign) != 'undefined' && config.valign.length){
8893 td.style += ' vertical-align:' + config.valign + ';';
8896 if(typeof(config.width) != 'undefined'){
8897 td.style += ' width:' + config.width + 'px;';
8900 if(typeof(config.cursor) != 'undefined'){
8901 td.style += ' cursor:' + config.cursor + ';';
8904 if(typeof(config.cls) != 'undefined'){
8905 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8908 ['xs','sm','md','lg'].map(function(size){
8910 if(typeof(config[size]) == 'undefined'){
8916 if (!config[size]) { // 0 = hidden
8917 // BS 4 '0' is treated as hide that column and below.
8918 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8922 td.cls += ' col-' + size + '-' + config[size] + (
8923 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8933 row.cellObjects = cellObjects;
8941 onBeforeLoad : function()
8950 this.el.select('tbody', true).first().dom.innerHTML = '';
8953 * Show or hide a row.
8954 * @param {Number} rowIndex to show or hide
8955 * @param {Boolean} state hide
8957 setRowVisibility : function(rowIndex, state)
8959 var bt = this.mainBody.dom;
8961 var rows = this.el.select('tbody > tr', true).elements;
8963 if(typeof(rows[rowIndex]) == 'undefined'){
8966 rows[rowIndex].dom.style.display = state ? '' : 'none';
8970 getSelectionModel : function(){
8972 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8974 return this.selModel;
8977 * Render the Roo.bootstrap object from renderder
8979 renderCellObject : function(r)
8983 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8985 var t = r.cfg.render(r.container);
8988 Roo.each(r.cfg.cn, function(c){
8990 container: t.getChildContainer(),
8993 _this.renderCellObject(child);
8998 getRowIndex : function(row)
9002 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9013 * Returns the grid's underlying element = used by panel.Grid
9014 * @return {Element} The element
9016 getGridEl : function(){
9020 * Forces a resize - used by panel.Grid
9021 * @return {Element} The element
9023 autoSize : function()
9025 //var ctr = Roo.get(this.container.dom.parentElement);
9026 var ctr = Roo.get(this.el.dom);
9028 var thd = this.getGridEl().select('thead',true).first();
9029 var tbd = this.getGridEl().select('tbody', true).first();
9030 var tfd = this.getGridEl().select('tfoot', true).first();
9032 var cw = ctr.getWidth();
9033 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9037 tbd.setWidth(ctr.getWidth());
9038 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9039 // this needs fixing for various usage - currently only hydra job advers I think..
9041 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9043 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9046 cw = Math.max(cw, this.totalWidth);
9047 this.getGridEl().select('tbody tr',true).setWidth(cw);
9049 // resize 'expandable coloumn?
9051 return; // we doe not have a view in this design..
9054 onBodyScroll: function()
9056 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9058 this.mainHead.setStyle({
9059 'position' : 'relative',
9060 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9066 var scrollHeight = this.mainBody.dom.scrollHeight;
9068 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9070 var height = this.mainBody.getHeight();
9072 if(scrollHeight - height == scrollTop) {
9074 var total = this.ds.getTotalCount();
9076 if(this.footer.cursor + this.footer.pageSize < total){
9078 this.footer.ds.load({
9080 start : this.footer.cursor + this.footer.pageSize,
9081 limit : this.footer.pageSize
9091 onHeaderChange : function()
9093 var header = this.renderHeader();
9094 var table = this.el.select('table', true).first();
9096 this.mainHead.remove();
9097 this.mainHead = table.createChild(header, this.mainBody, false);
9100 onHiddenChange : function(colModel, colIndex, hidden)
9102 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9103 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9105 this.CSS.updateRule(thSelector, "display", "");
9106 this.CSS.updateRule(tdSelector, "display", "");
9109 this.CSS.updateRule(thSelector, "display", "none");
9110 this.CSS.updateRule(tdSelector, "display", "none");
9113 this.onHeaderChange();
9117 setColumnWidth: function(col_index, width)
9119 // width = "md-2 xs-2..."
9120 if(!this.colModel.config[col_index]) {
9124 var w = width.split(" ");
9126 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9128 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9131 for(var j = 0; j < w.length; j++) {
9137 var size_cls = w[j].split("-");
9139 if(!Number.isInteger(size_cls[1] * 1)) {
9143 if(!this.colModel.config[col_index][size_cls[0]]) {
9147 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9151 h_row[0].classList.replace(
9152 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9153 "col-"+size_cls[0]+"-"+size_cls[1]
9156 for(var i = 0; i < rows.length; i++) {
9158 var size_cls = w[j].split("-");
9160 if(!Number.isInteger(size_cls[1] * 1)) {
9164 if(!this.colModel.config[col_index][size_cls[0]]) {
9168 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9172 rows[i].classList.replace(
9173 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174 "col-"+size_cls[0]+"-"+size_cls[1]
9178 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9193 * @class Roo.bootstrap.TableCell
9194 * @extends Roo.bootstrap.Component
9195 * Bootstrap TableCell class
9196 * @cfg {String} html cell contain text
9197 * @cfg {String} cls cell class
9198 * @cfg {String} tag cell tag (td|th) default td
9199 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9200 * @cfg {String} align Aligns the content in a cell
9201 * @cfg {String} axis Categorizes cells
9202 * @cfg {String} bgcolor Specifies the background color of a cell
9203 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9204 * @cfg {Number} colspan Specifies the number of columns a cell should span
9205 * @cfg {String} headers Specifies one or more header cells a cell is related to
9206 * @cfg {Number} height Sets the height of a cell
9207 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9208 * @cfg {Number} rowspan Sets the number of rows a cell should span
9209 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9210 * @cfg {String} valign Vertical aligns the content in a cell
9211 * @cfg {Number} width Specifies the width of a cell
9214 * Create a new TableCell
9215 * @param {Object} config The config object
9218 Roo.bootstrap.TableCell = function(config){
9219 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9222 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9242 getAutoCreate : function(){
9243 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9263 cfg.align=this.align
9269 cfg.bgcolor=this.bgcolor
9272 cfg.charoff=this.charoff
9275 cfg.colspan=this.colspan
9278 cfg.headers=this.headers
9281 cfg.height=this.height
9284 cfg.nowrap=this.nowrap
9287 cfg.rowspan=this.rowspan
9290 cfg.scope=this.scope
9293 cfg.valign=this.valign
9296 cfg.width=this.width
9315 * @class Roo.bootstrap.TableRow
9316 * @extends Roo.bootstrap.Component
9317 * Bootstrap TableRow class
9318 * @cfg {String} cls row class
9319 * @cfg {String} align Aligns the content in a table row
9320 * @cfg {String} bgcolor Specifies a background color for a table row
9321 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9322 * @cfg {String} valign Vertical aligns the content in a table row
9325 * Create a new TableRow
9326 * @param {Object} config The config object
9329 Roo.bootstrap.TableRow = function(config){
9330 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9333 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9341 getAutoCreate : function(){
9342 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9352 cfg.align = this.align;
9355 cfg.bgcolor = this.bgcolor;
9358 cfg.charoff = this.charoff;
9361 cfg.valign = this.valign;
9379 * @class Roo.bootstrap.TableBody
9380 * @extends Roo.bootstrap.Component
9381 * Bootstrap TableBody class
9382 * @cfg {String} cls element class
9383 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9384 * @cfg {String} align Aligns the content inside the element
9385 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9386 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9389 * Create a new TableBody
9390 * @param {Object} config The config object
9393 Roo.bootstrap.TableBody = function(config){
9394 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9397 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9405 getAutoCreate : function(){
9406 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9420 cfg.align = this.align;
9423 cfg.charoff = this.charoff;
9426 cfg.valign = this.valign;
9433 // initEvents : function()
9440 // this.store = Roo.factory(this.store, Roo.data);
9441 // this.store.on('load', this.onLoad, this);
9443 // this.store.load();
9447 // onLoad: function ()
9449 // this.fireEvent('load', this);
9459 * Ext JS Library 1.1.1
9460 * Copyright(c) 2006-2007, Ext JS, LLC.
9462 * Originally Released Under LGPL - original licence link has changed is not relivant.
9465 * <script type="text/javascript">
9468 // as we use this in bootstrap.
9469 Roo.namespace('Roo.form');
9471 * @class Roo.form.Action
9472 * Internal Class used to handle form actions
9474 * @param {Roo.form.BasicForm} el The form element or its id
9475 * @param {Object} config Configuration options
9480 // define the action interface
9481 Roo.form.Action = function(form, options){
9483 this.options = options || {};
9486 * Client Validation Failed
9489 Roo.form.Action.CLIENT_INVALID = 'client';
9491 * Server Validation Failed
9494 Roo.form.Action.SERVER_INVALID = 'server';
9496 * Connect to Server Failed
9499 Roo.form.Action.CONNECT_FAILURE = 'connect';
9501 * Reading Data from Server Failed
9504 Roo.form.Action.LOAD_FAILURE = 'load';
9506 Roo.form.Action.prototype = {
9508 failureType : undefined,
9509 response : undefined,
9513 run : function(options){
9518 success : function(response){
9523 handleResponse : function(response){
9527 // default connection failure
9528 failure : function(response){
9530 this.response = response;
9531 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9532 this.form.afterAction(this, false);
9535 processResponse : function(response){
9536 this.response = response;
9537 if(!response.responseText){
9540 this.result = this.handleResponse(response);
9544 // utility functions used internally
9545 getUrl : function(appendParams){
9546 var url = this.options.url || this.form.url || this.form.el.dom.action;
9548 var p = this.getParams();
9550 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9556 getMethod : function(){
9557 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9560 getParams : function(){
9561 var bp = this.form.baseParams;
9562 var p = this.options.params;
9564 if(typeof p == "object"){
9565 p = Roo.urlEncode(Roo.applyIf(p, bp));
9566 }else if(typeof p == 'string' && bp){
9567 p += '&' + Roo.urlEncode(bp);
9570 p = Roo.urlEncode(bp);
9575 createCallback : function(){
9577 success: this.success,
9578 failure: this.failure,
9580 timeout: (this.form.timeout*1000),
9581 upload: this.form.fileUpload ? this.success : undefined
9586 Roo.form.Action.Submit = function(form, options){
9587 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9593 haveProgress : false,
9594 uploadComplete : false,
9596 // uploadProgress indicator.
9597 uploadProgress : function()
9599 if (!this.form.progressUrl) {
9603 if (!this.haveProgress) {
9604 Roo.MessageBox.progress("Uploading", "Uploading");
9606 if (this.uploadComplete) {
9607 Roo.MessageBox.hide();
9611 this.haveProgress = true;
9613 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9615 var c = new Roo.data.Connection();
9617 url : this.form.progressUrl,
9622 success : function(req){
9623 //console.log(data);
9627 rdata = Roo.decode(req.responseText)
9629 Roo.log("Invalid data from server..");
9633 if (!rdata || !rdata.success) {
9635 Roo.MessageBox.alert(Roo.encode(rdata));
9638 var data = rdata.data;
9640 if (this.uploadComplete) {
9641 Roo.MessageBox.hide();
9646 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9647 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9650 this.uploadProgress.defer(2000,this);
9653 failure: function(data) {
9654 Roo.log('progress url failed ');
9665 // run get Values on the form, so it syncs any secondary forms.
9666 this.form.getValues();
9668 var o = this.options;
9669 var method = this.getMethod();
9670 var isPost = method == 'POST';
9671 if(o.clientValidation === false || this.form.isValid()){
9673 if (this.form.progressUrl) {
9674 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9675 (new Date() * 1) + '' + Math.random());
9680 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9681 form:this.form.el.dom,
9682 url:this.getUrl(!isPost),
9684 params:isPost ? this.getParams() : null,
9685 isUpload: this.form.fileUpload,
9686 formData : this.form.formData
9689 this.uploadProgress();
9691 }else if (o.clientValidation !== false){ // client validation failed
9692 this.failureType = Roo.form.Action.CLIENT_INVALID;
9693 this.form.afterAction(this, false);
9697 success : function(response)
9699 this.uploadComplete= true;
9700 if (this.haveProgress) {
9701 Roo.MessageBox.hide();
9705 var result = this.processResponse(response);
9706 if(result === true || result.success){
9707 this.form.afterAction(this, true);
9711 this.form.markInvalid(result.errors);
9712 this.failureType = Roo.form.Action.SERVER_INVALID;
9714 this.form.afterAction(this, false);
9716 failure : function(response)
9718 this.uploadComplete= true;
9719 if (this.haveProgress) {
9720 Roo.MessageBox.hide();
9723 this.response = response;
9724 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9725 this.form.afterAction(this, false);
9728 handleResponse : function(response){
9729 if(this.form.errorReader){
9730 var rs = this.form.errorReader.read(response);
9733 for(var i = 0, len = rs.records.length; i < len; i++) {
9734 var r = rs.records[i];
9738 if(errors.length < 1){
9742 success : rs.success,
9748 ret = Roo.decode(response.responseText);
9752 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9762 Roo.form.Action.Load = function(form, options){
9763 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9764 this.reader = this.form.reader;
9767 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9772 Roo.Ajax.request(Roo.apply(
9773 this.createCallback(), {
9774 method:this.getMethod(),
9775 url:this.getUrl(false),
9776 params:this.getParams()
9780 success : function(response){
9782 var result = this.processResponse(response);
9783 if(result === true || !result.success || !result.data){
9784 this.failureType = Roo.form.Action.LOAD_FAILURE;
9785 this.form.afterAction(this, false);
9788 this.form.clearInvalid();
9789 this.form.setValues(result.data);
9790 this.form.afterAction(this, true);
9793 handleResponse : function(response){
9794 if(this.form.reader){
9795 var rs = this.form.reader.read(response);
9796 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9798 success : rs.success,
9802 return Roo.decode(response.responseText);
9806 Roo.form.Action.ACTION_TYPES = {
9807 'load' : Roo.form.Action.Load,
9808 'submit' : Roo.form.Action.Submit
9817 * @class Roo.bootstrap.Form
9818 * @extends Roo.bootstrap.Component
9819 * Bootstrap Form class
9820 * @cfg {String} method GET | POST (default POST)
9821 * @cfg {String} labelAlign top | left (default top)
9822 * @cfg {String} align left | right - for navbars
9823 * @cfg {Boolean} loadMask load mask when submit (default true)
9828 * @param {Object} config The config object
9832 Roo.bootstrap.Form = function(config){
9834 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9836 Roo.bootstrap.Form.popover.apply();
9840 * @event clientvalidation
9841 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9842 * @param {Form} this
9843 * @param {Boolean} valid true if the form has passed client-side validation
9845 clientvalidation: true,
9847 * @event beforeaction
9848 * Fires before any action is performed. Return false to cancel the action.
9849 * @param {Form} this
9850 * @param {Action} action The action to be performed
9854 * @event actionfailed
9855 * Fires when an action fails.
9856 * @param {Form} this
9857 * @param {Action} action The action that failed
9859 actionfailed : true,
9861 * @event actioncomplete
9862 * Fires when an action is completed.
9863 * @param {Form} this
9864 * @param {Action} action The action that completed
9866 actioncomplete : true
9870 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9873 * @cfg {String} method
9874 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9879 * The URL to use for form actions if one isn't supplied in the action options.
9882 * @cfg {Boolean} fileUpload
9883 * Set to true if this form is a file upload.
9887 * @cfg {Object} baseParams
9888 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9892 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9896 * @cfg {Sting} align (left|right) for navbar forms
9901 activeAction : null,
9904 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9905 * element by passing it or its id or mask the form itself by passing in true.
9908 waitMsgTarget : false,
9913 * @cfg {Boolean} errorMask (true|false) default false
9918 * @cfg {Number} maskOffset Default 100
9923 * @cfg {Boolean} maskBody
9927 getAutoCreate : function(){
9931 method : this.method || 'POST',
9932 id : this.id || Roo.id(),
9935 if (this.parent().xtype.match(/^Nav/)) {
9936 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9940 if (this.labelAlign == 'left' ) {
9941 cfg.cls += ' form-horizontal';
9947 initEvents : function()
9949 this.el.on('submit', this.onSubmit, this);
9950 // this was added as random key presses on the form where triggering form submit.
9951 this.el.on('keypress', function(e) {
9952 if (e.getCharCode() != 13) {
9955 // we might need to allow it for textareas.. and some other items.
9956 // check e.getTarget().
9958 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9962 Roo.log("keypress blocked");
9970 onSubmit : function(e){
9975 * Returns true if client-side validation on the form is successful.
9978 isValid : function(){
9979 var items = this.getItems();
9983 items.each(function(f){
9989 Roo.log('invalid field: ' + f.name);
9993 if(!target && f.el.isVisible(true)){
9999 if(this.errorMask && !valid){
10000 Roo.bootstrap.Form.popover.mask(this, target);
10007 * Returns true if any fields in this form have changed since their original load.
10010 isDirty : function(){
10012 var items = this.getItems();
10013 items.each(function(f){
10023 * Performs a predefined action (submit or load) or custom actions you define on this form.
10024 * @param {String} actionName The name of the action type
10025 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10026 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10027 * accept other config options):
10029 Property Type Description
10030 ---------------- --------------- ----------------------------------------------------------------------------------
10031 url String The url for the action (defaults to the form's url)
10032 method String The form method to use (defaults to the form's method, or POST if not defined)
10033 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10034 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10035 validate the form on the client (defaults to false)
10037 * @return {BasicForm} this
10039 doAction : function(action, options){
10040 if(typeof action == 'string'){
10041 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10043 if(this.fireEvent('beforeaction', this, action) !== false){
10044 this.beforeAction(action);
10045 action.run.defer(100, action);
10051 beforeAction : function(action){
10052 var o = action.options;
10057 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10059 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10062 // not really supported yet.. ??
10064 //if(this.waitMsgTarget === true){
10065 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066 //}else if(this.waitMsgTarget){
10067 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10068 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10070 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10076 afterAction : function(action, success){
10077 this.activeAction = null;
10078 var o = action.options;
10083 Roo.get(document.body).unmask();
10089 //if(this.waitMsgTarget === true){
10090 // this.el.unmask();
10091 //}else if(this.waitMsgTarget){
10092 // this.waitMsgTarget.unmask();
10094 // Roo.MessageBox.updateProgress(1);
10095 // Roo.MessageBox.hide();
10102 Roo.callback(o.success, o.scope, [this, action]);
10103 this.fireEvent('actioncomplete', this, action);
10107 // failure condition..
10108 // we have a scenario where updates need confirming.
10109 // eg. if a locking scenario exists..
10110 // we look for { errors : { needs_confirm : true }} in the response.
10112 (typeof(action.result) != 'undefined') &&
10113 (typeof(action.result.errors) != 'undefined') &&
10114 (typeof(action.result.errors.needs_confirm) != 'undefined')
10117 Roo.log("not supported yet");
10120 Roo.MessageBox.confirm(
10121 "Change requires confirmation",
10122 action.result.errorMsg,
10127 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10137 Roo.callback(o.failure, o.scope, [this, action]);
10138 // show an error message if no failed handler is set..
10139 if (!this.hasListener('actionfailed')) {
10140 Roo.log("need to add dialog support");
10142 Roo.MessageBox.alert("Error",
10143 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10144 action.result.errorMsg :
10145 "Saving Failed, please check your entries or try again"
10150 this.fireEvent('actionfailed', this, action);
10155 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10156 * @param {String} id The value to search for
10159 findField : function(id){
10160 var items = this.getItems();
10161 var field = items.get(id);
10163 items.each(function(f){
10164 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10171 return field || null;
10174 * Mark fields in this form invalid in bulk.
10175 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10176 * @return {BasicForm} this
10178 markInvalid : function(errors){
10179 if(errors instanceof Array){
10180 for(var i = 0, len = errors.length; i < len; i++){
10181 var fieldError = errors[i];
10182 var f = this.findField(fieldError.id);
10184 f.markInvalid(fieldError.msg);
10190 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10191 field.markInvalid(errors[id]);
10195 //Roo.each(this.childForms || [], function (f) {
10196 // f.markInvalid(errors);
10203 * Set values for fields in this form in bulk.
10204 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10205 * @return {BasicForm} this
10207 setValues : function(values){
10208 if(values instanceof Array){ // array of objects
10209 for(var i = 0, len = values.length; i < len; i++){
10211 var f = this.findField(v.id);
10213 f.setValue(v.value);
10214 if(this.trackResetOnLoad){
10215 f.originalValue = f.getValue();
10219 }else{ // object hash
10222 if(typeof values[id] != 'function' && (field = this.findField(id))){
10224 if (field.setFromData &&
10225 field.valueField &&
10226 field.displayField &&
10227 // combos' with local stores can
10228 // be queried via setValue()
10229 // to set their value..
10230 (field.store && !field.store.isLocal)
10234 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10235 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10236 field.setFromData(sd);
10238 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10240 field.setFromData(values);
10243 field.setValue(values[id]);
10247 if(this.trackResetOnLoad){
10248 field.originalValue = field.getValue();
10254 //Roo.each(this.childForms || [], function (f) {
10255 // f.setValues(values);
10262 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10263 * they are returned as an array.
10264 * @param {Boolean} asString
10267 getValues : function(asString){
10268 //if (this.childForms) {
10269 // copy values from the child forms
10270 // Roo.each(this.childForms, function (f) {
10271 // this.setValues(f.getValues());
10277 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10278 if(asString === true){
10281 return Roo.urlDecode(fs);
10285 * Returns the fields in this form as an object with key/value pairs.
10286 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10289 getFieldValues : function(with_hidden)
10291 var items = this.getItems();
10293 items.each(function(f){
10295 if (!f.getName()) {
10299 var v = f.getValue();
10301 if (f.inputType =='radio') {
10302 if (typeof(ret[f.getName()]) == 'undefined') {
10303 ret[f.getName()] = ''; // empty..
10306 if (!f.el.dom.checked) {
10310 v = f.el.dom.value;
10314 if(f.xtype == 'MoneyField'){
10315 ret[f.currencyName] = f.getCurrency();
10318 // not sure if this supported any more..
10319 if ((typeof(v) == 'object') && f.getRawValue) {
10320 v = f.getRawValue() ; // dates..
10322 // combo boxes where name != hiddenName...
10323 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10324 ret[f.name] = f.getRawValue();
10326 ret[f.getName()] = v;
10333 * Clears all invalid messages in this form.
10334 * @return {BasicForm} this
10336 clearInvalid : function(){
10337 var items = this.getItems();
10339 items.each(function(f){
10347 * Resets this form.
10348 * @return {BasicForm} this
10350 reset : function(){
10351 var items = this.getItems();
10352 items.each(function(f){
10356 Roo.each(this.childForms || [], function (f) {
10364 getItems : function()
10366 var r=new Roo.util.MixedCollection(false, function(o){
10367 return o.id || (o.id = Roo.id());
10369 var iter = function(el) {
10376 Roo.each(el.items,function(e) {
10385 hideFields : function(items)
10387 Roo.each(items, function(i){
10389 var f = this.findField(i);
10400 showFields : function(items)
10402 Roo.each(items, function(i){
10404 var f = this.findField(i);
10417 Roo.apply(Roo.bootstrap.Form, {
10433 intervalID : false,
10439 if(this.isApplied){
10444 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10445 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10446 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10447 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10450 this.maskEl.top.enableDisplayMode("block");
10451 this.maskEl.left.enableDisplayMode("block");
10452 this.maskEl.bottom.enableDisplayMode("block");
10453 this.maskEl.right.enableDisplayMode("block");
10455 this.toolTip = new Roo.bootstrap.Tooltip({
10456 cls : 'roo-form-error-popover',
10458 'left' : ['r-l', [-2,0], 'right'],
10459 'right' : ['l-r', [2,0], 'left'],
10460 'bottom' : ['tl-bl', [0,2], 'top'],
10461 'top' : [ 'bl-tl', [0,-2], 'bottom']
10465 this.toolTip.render(Roo.get(document.body));
10467 this.toolTip.el.enableDisplayMode("block");
10469 Roo.get(document.body).on('click', function(){
10473 Roo.get(document.body).on('touchstart', function(){
10477 this.isApplied = true
10480 mask : function(form, target)
10484 this.target = target;
10486 if(!this.form.errorMask || !target.el){
10490 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10492 Roo.log(scrollable);
10494 var ot = this.target.el.calcOffsetsTo(scrollable);
10496 var scrollTo = ot[1] - this.form.maskOffset;
10498 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10500 scrollable.scrollTo('top', scrollTo);
10502 var box = this.target.el.getBox();
10504 var zIndex = Roo.bootstrap.Modal.zIndex++;
10507 this.maskEl.top.setStyle('position', 'absolute');
10508 this.maskEl.top.setStyle('z-index', zIndex);
10509 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10510 this.maskEl.top.setLeft(0);
10511 this.maskEl.top.setTop(0);
10512 this.maskEl.top.show();
10514 this.maskEl.left.setStyle('position', 'absolute');
10515 this.maskEl.left.setStyle('z-index', zIndex);
10516 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10517 this.maskEl.left.setLeft(0);
10518 this.maskEl.left.setTop(box.y - this.padding);
10519 this.maskEl.left.show();
10521 this.maskEl.bottom.setStyle('position', 'absolute');
10522 this.maskEl.bottom.setStyle('z-index', zIndex);
10523 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10524 this.maskEl.bottom.setLeft(0);
10525 this.maskEl.bottom.setTop(box.bottom + this.padding);
10526 this.maskEl.bottom.show();
10528 this.maskEl.right.setStyle('position', 'absolute');
10529 this.maskEl.right.setStyle('z-index', zIndex);
10530 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10531 this.maskEl.right.setLeft(box.right + this.padding);
10532 this.maskEl.right.setTop(box.y - this.padding);
10533 this.maskEl.right.show();
10535 this.toolTip.bindEl = this.target.el;
10537 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10539 var tip = this.target.blankText;
10541 if(this.target.getValue() !== '' ) {
10543 if (this.target.invalidText.length) {
10544 tip = this.target.invalidText;
10545 } else if (this.target.regexText.length){
10546 tip = this.target.regexText;
10550 this.toolTip.show(tip);
10552 this.intervalID = window.setInterval(function() {
10553 Roo.bootstrap.Form.popover.unmask();
10556 window.onwheel = function(){ return false;};
10558 (function(){ this.isMasked = true; }).defer(500, this);
10562 unmask : function()
10564 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10568 this.maskEl.top.setStyle('position', 'absolute');
10569 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10570 this.maskEl.top.hide();
10572 this.maskEl.left.setStyle('position', 'absolute');
10573 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10574 this.maskEl.left.hide();
10576 this.maskEl.bottom.setStyle('position', 'absolute');
10577 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10578 this.maskEl.bottom.hide();
10580 this.maskEl.right.setStyle('position', 'absolute');
10581 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10582 this.maskEl.right.hide();
10584 this.toolTip.hide();
10586 this.toolTip.el.hide();
10588 window.onwheel = function(){ return true;};
10590 if(this.intervalID){
10591 window.clearInterval(this.intervalID);
10592 this.intervalID = false;
10595 this.isMasked = false;
10605 * Ext JS Library 1.1.1
10606 * Copyright(c) 2006-2007, Ext JS, LLC.
10608 * Originally Released Under LGPL - original licence link has changed is not relivant.
10611 * <script type="text/javascript">
10614 * @class Roo.form.VTypes
10615 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10618 Roo.form.VTypes = function(){
10619 // closure these in so they are only created once.
10620 var alpha = /^[a-zA-Z_]+$/;
10621 var alphanum = /^[a-zA-Z0-9_]+$/;
10622 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10623 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10625 // All these messages and functions are configurable
10628 * The function used to validate email addresses
10629 * @param {String} value The email address
10631 'email' : function(v){
10632 return email.test(v);
10635 * The error text to display when the email validation function returns false
10638 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10640 * The keystroke filter mask to be applied on email input
10643 'emailMask' : /[a-z0-9_\.\-@]/i,
10646 * The function used to validate URLs
10647 * @param {String} value The URL
10649 'url' : function(v){
10650 return url.test(v);
10653 * The error text to display when the url validation function returns false
10656 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10659 * The function used to validate alpha values
10660 * @param {String} value The value
10662 'alpha' : function(v){
10663 return alpha.test(v);
10666 * The error text to display when the alpha validation function returns false
10669 'alphaText' : 'This field should only contain letters and _',
10671 * The keystroke filter mask to be applied on alpha input
10674 'alphaMask' : /[a-z_]/i,
10677 * The function used to validate alphanumeric values
10678 * @param {String} value The value
10680 'alphanum' : function(v){
10681 return alphanum.test(v);
10684 * The error text to display when the alphanumeric validation function returns false
10687 'alphanumText' : 'This field should only contain letters, numbers and _',
10689 * The keystroke filter mask to be applied on alphanumeric input
10692 'alphanumMask' : /[a-z0-9_]/i
10702 * @class Roo.bootstrap.Input
10703 * @extends Roo.bootstrap.Component
10704 * Bootstrap Input class
10705 * @cfg {Boolean} disabled is it disabled
10706 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10707 * @cfg {String} name name of the input
10708 * @cfg {string} fieldLabel - the label associated
10709 * @cfg {string} placeholder - placeholder to put in text.
10710 * @cfg {string} before - input group add on before
10711 * @cfg {string} after - input group add on after
10712 * @cfg {string} size - (lg|sm) or leave empty..
10713 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10714 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10715 * @cfg {Number} md colspan out of 12 for computer-sized screens
10716 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10717 * @cfg {string} value default value of the input
10718 * @cfg {Number} labelWidth set the width of label
10719 * @cfg {Number} labellg set the width of label (1-12)
10720 * @cfg {Number} labelmd set the width of label (1-12)
10721 * @cfg {Number} labelsm set the width of label (1-12)
10722 * @cfg {Number} labelxs set the width of label (1-12)
10723 * @cfg {String} labelAlign (top|left)
10724 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10725 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10726 * @cfg {String} indicatorpos (left|right) default left
10727 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10728 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10729 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10731 * @cfg {String} align (left|center|right) Default left
10732 * @cfg {Boolean} forceFeedback (true|false) Default false
10735 * Create a new Input
10736 * @param {Object} config The config object
10739 Roo.bootstrap.Input = function(config){
10741 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10746 * Fires when this field receives input focus.
10747 * @param {Roo.form.Field} this
10752 * Fires when this field loses input focus.
10753 * @param {Roo.form.Field} this
10757 * @event specialkey
10758 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10759 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10760 * @param {Roo.form.Field} this
10761 * @param {Roo.EventObject} e The event object
10766 * Fires just before the field blurs if the field value has changed.
10767 * @param {Roo.form.Field} this
10768 * @param {Mixed} newValue The new value
10769 * @param {Mixed} oldValue The original value
10774 * Fires after the field has been marked as invalid.
10775 * @param {Roo.form.Field} this
10776 * @param {String} msg The validation message
10781 * Fires after the field has been validated with no errors.
10782 * @param {Roo.form.Field} this
10787 * Fires after the key up
10788 * @param {Roo.form.Field} this
10789 * @param {Roo.EventObject} e The event Object
10794 * Fires after the user pastes into input
10795 * @param {Roo.form.Field} this
10796 * @param {Roo.EventObject} e The event Object
10802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10804 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10805 automatic validation (defaults to "keyup").
10807 validationEvent : "keyup",
10809 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10811 validateOnBlur : true,
10813 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10815 validationDelay : 250,
10817 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10819 focusClass : "x-form-focus", // not needed???
10823 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10825 invalidClass : "has-warning",
10828 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10830 validClass : "has-success",
10833 * @cfg {Boolean} hasFeedback (true|false) default true
10835 hasFeedback : true,
10838 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10840 invalidFeedbackClass : "glyphicon-warning-sign",
10843 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10845 validFeedbackClass : "glyphicon-ok",
10848 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10850 selectOnFocus : false,
10853 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10857 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10862 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10864 disableKeyFilter : false,
10867 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10871 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10875 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10877 blankText : "Please complete this mandatory field",
10880 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10884 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10886 maxLength : Number.MAX_VALUE,
10888 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10890 minLengthText : "The minimum length for this field is {0}",
10892 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10894 maxLengthText : "The maximum length for this field is {0}",
10898 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10899 * If available, this function will be called only after the basic validators all return true, and will be passed the
10900 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10904 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10905 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10906 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10910 * @cfg {String} regexText -- Depricated - use Invalid Text
10915 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10921 autocomplete: false,
10925 inputType : 'text',
10928 placeholder: false,
10933 preventMark: false,
10934 isFormField : true,
10937 labelAlign : false,
10940 formatedValue : false,
10941 forceFeedback : false,
10943 indicatorpos : 'left',
10953 parentLabelAlign : function()
10956 while (parent.parent()) {
10957 parent = parent.parent();
10958 if (typeof(parent.labelAlign) !='undefined') {
10959 return parent.labelAlign;
10966 getAutoCreate : function()
10968 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10974 if(this.inputType != 'hidden'){
10975 cfg.cls = 'form-group' //input-group
10981 type : this.inputType,
10982 value : this.value,
10983 cls : 'form-control',
10984 placeholder : this.placeholder || '',
10985 autocomplete : this.autocomplete || 'new-password'
10987 if (this.inputType == 'file') {
10988 input.style = 'overflow:hidden'; // why not in CSS?
10991 if(this.capture.length){
10992 input.capture = this.capture;
10995 if(this.accept.length){
10996 input.accept = this.accept + "/*";
11000 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11003 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11004 input.maxLength = this.maxLength;
11007 if (this.disabled) {
11008 input.disabled=true;
11011 if (this.readOnly) {
11012 input.readonly=true;
11016 input.name = this.name;
11020 input.cls += ' input-' + this.size;
11024 ['xs','sm','md','lg'].map(function(size){
11025 if (settings[size]) {
11026 cfg.cls += ' col-' + size + '-' + settings[size];
11030 var inputblock = input;
11034 cls: 'glyphicon form-control-feedback'
11037 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11040 cls : 'has-feedback',
11048 if (this.before || this.after) {
11051 cls : 'input-group',
11055 if (this.before && typeof(this.before) == 'string') {
11057 inputblock.cn.push({
11059 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11063 if (this.before && typeof(this.before) == 'object') {
11064 this.before = Roo.factory(this.before);
11066 inputblock.cn.push({
11068 cls : 'roo-input-before input-group-prepend input-group-' +
11069 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11073 inputblock.cn.push(input);
11075 if (this.after && typeof(this.after) == 'string') {
11076 inputblock.cn.push({
11078 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11082 if (this.after && typeof(this.after) == 'object') {
11083 this.after = Roo.factory(this.after);
11085 inputblock.cn.push({
11087 cls : 'roo-input-after input-group-append input-group-' +
11088 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11092 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11093 inputblock.cls += ' has-feedback';
11094 inputblock.cn.push(feedback);
11099 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11100 tooltip : 'This field is required'
11102 if (this.allowBlank ) {
11103 indicator.style = this.allowBlank ? ' display:none' : '';
11105 if (align ==='left' && this.fieldLabel.length) {
11107 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11114 cls : 'control-label col-form-label',
11115 html : this.fieldLabel
11126 var labelCfg = cfg.cn[1];
11127 var contentCfg = cfg.cn[2];
11129 if(this.indicatorpos == 'right'){
11134 cls : 'control-label col-form-label',
11138 html : this.fieldLabel
11152 labelCfg = cfg.cn[0];
11153 contentCfg = cfg.cn[1];
11157 if(this.labelWidth > 12){
11158 labelCfg.style = "width: " + this.labelWidth + 'px';
11161 if(this.labelWidth < 13 && this.labelmd == 0){
11162 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11165 if(this.labellg > 0){
11166 labelCfg.cls += ' col-lg-' + this.labellg;
11167 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11170 if(this.labelmd > 0){
11171 labelCfg.cls += ' col-md-' + this.labelmd;
11172 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11175 if(this.labelsm > 0){
11176 labelCfg.cls += ' col-sm-' + this.labelsm;
11177 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11180 if(this.labelxs > 0){
11181 labelCfg.cls += ' col-xs-' + this.labelxs;
11182 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11186 } else if ( this.fieldLabel.length) {
11193 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11194 tooltip : 'This field is required',
11195 style : this.allowBlank ? ' display:none' : ''
11199 //cls : 'input-group-addon',
11200 html : this.fieldLabel
11208 if(this.indicatorpos == 'right'){
11213 //cls : 'input-group-addon',
11214 html : this.fieldLabel
11219 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11220 tooltip : 'This field is required',
11221 style : this.allowBlank ? ' display:none' : ''
11241 if (this.parentType === 'Navbar' && this.parent().bar) {
11242 cfg.cls += ' navbar-form';
11245 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11246 // on BS4 we do this only if not form
11247 cfg.cls += ' navbar-form';
11255 * return the real input element.
11257 inputEl: function ()
11259 return this.el.select('input.form-control',true).first();
11262 tooltipEl : function()
11264 return this.inputEl();
11267 indicatorEl : function()
11269 if (Roo.bootstrap.version == 4) {
11270 return false; // not enabled in v4 yet.
11273 var indicator = this.el.select('i.roo-required-indicator',true).first();
11283 setDisabled : function(v)
11285 var i = this.inputEl().dom;
11287 i.removeAttribute('disabled');
11291 i.setAttribute('disabled','true');
11293 initEvents : function()
11296 this.inputEl().on("keydown" , this.fireKey, this);
11297 this.inputEl().on("focus", this.onFocus, this);
11298 this.inputEl().on("blur", this.onBlur, this);
11300 this.inputEl().relayEvent('keyup', this);
11301 this.inputEl().relayEvent('paste', this);
11303 this.indicator = this.indicatorEl();
11305 if(this.indicator){
11306 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11309 // reference to original value for reset
11310 this.originalValue = this.getValue();
11311 //Roo.form.TextField.superclass.initEvents.call(this);
11312 if(this.validationEvent == 'keyup'){
11313 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11314 this.inputEl().on('keyup', this.filterValidation, this);
11316 else if(this.validationEvent !== false){
11317 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11320 if(this.selectOnFocus){
11321 this.on("focus", this.preFocus, this);
11324 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11325 this.inputEl().on("keypress", this.filterKeys, this);
11327 this.inputEl().relayEvent('keypress', this);
11330 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11331 this.el.on("click", this.autoSize, this);
11334 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11335 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11338 if (typeof(this.before) == 'object') {
11339 this.before.render(this.el.select('.roo-input-before',true).first());
11341 if (typeof(this.after) == 'object') {
11342 this.after.render(this.el.select('.roo-input-after',true).first());
11345 this.inputEl().on('change', this.onChange, this);
11348 filterValidation : function(e){
11349 if(!e.isNavKeyPress()){
11350 this.validationTask.delay(this.validationDelay);
11354 * Validates the field value
11355 * @return {Boolean} True if the value is valid, else false
11357 validate : function(){
11358 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11359 if(this.disabled || this.validateValue(this.getRawValue())){
11364 this.markInvalid();
11370 * Validates a value according to the field's validation rules and marks the field as invalid
11371 * if the validation fails
11372 * @param {Mixed} value The value to validate
11373 * @return {Boolean} True if the value is valid, else false
11375 validateValue : function(value)
11377 if(this.getVisibilityEl().hasClass('hidden')){
11381 if(value.length < 1) { // if it's blank
11382 if(this.allowBlank){
11388 if(value.length < this.minLength){
11391 if(value.length > this.maxLength){
11395 var vt = Roo.form.VTypes;
11396 if(!vt[this.vtype](value, this)){
11400 if(typeof this.validator == "function"){
11401 var msg = this.validator(value);
11405 if (typeof(msg) == 'string') {
11406 this.invalidText = msg;
11410 if(this.regex && !this.regex.test(value)){
11418 fireKey : function(e){
11419 //Roo.log('field ' + e.getKey());
11420 if(e.isNavKeyPress()){
11421 this.fireEvent("specialkey", this, e);
11424 focus : function (selectText){
11426 this.inputEl().focus();
11427 if(selectText === true){
11428 this.inputEl().dom.select();
11434 onFocus : function(){
11435 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436 // this.el.addClass(this.focusClass);
11438 if(!this.hasFocus){
11439 this.hasFocus = true;
11440 this.startValue = this.getValue();
11441 this.fireEvent("focus", this);
11445 beforeBlur : Roo.emptyFn,
11449 onBlur : function(){
11451 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11452 //this.el.removeClass(this.focusClass);
11454 this.hasFocus = false;
11455 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11458 var v = this.getValue();
11459 if(String(v) !== String(this.startValue)){
11460 this.fireEvent('change', this, v, this.startValue);
11462 this.fireEvent("blur", this);
11465 onChange : function(e)
11467 var v = this.getValue();
11468 if(String(v) !== String(this.startValue)){
11469 this.fireEvent('change', this, v, this.startValue);
11475 * Resets the current field value to the originally loaded value and clears any validation messages
11477 reset : function(){
11478 this.setValue(this.originalValue);
11482 * Returns the name of the field
11483 * @return {Mixed} name The name field
11485 getName: function(){
11489 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11490 * @return {Mixed} value The field value
11492 getValue : function(){
11494 var v = this.inputEl().getValue();
11499 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11500 * @return {Mixed} value The field value
11502 getRawValue : function(){
11503 var v = this.inputEl().getValue();
11509 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11510 * @param {Mixed} value The value to set
11512 setRawValue : function(v){
11513 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11516 selectText : function(start, end){
11517 var v = this.getRawValue();
11519 start = start === undefined ? 0 : start;
11520 end = end === undefined ? v.length : end;
11521 var d = this.inputEl().dom;
11522 if(d.setSelectionRange){
11523 d.setSelectionRange(start, end);
11524 }else if(d.createTextRange){
11525 var range = d.createTextRange();
11526 range.moveStart("character", start);
11527 range.moveEnd("character", v.length-end);
11534 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11535 * @param {Mixed} value The value to set
11537 setValue : function(v){
11540 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11546 processValue : function(value){
11547 if(this.stripCharsRe){
11548 var newValue = value.replace(this.stripCharsRe, '');
11549 if(newValue !== value){
11550 this.setRawValue(newValue);
11557 preFocus : function(){
11559 if(this.selectOnFocus){
11560 this.inputEl().dom.select();
11563 filterKeys : function(e){
11564 var k = e.getKey();
11565 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11568 var c = e.getCharCode(), cc = String.fromCharCode(c);
11569 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11572 if(!this.maskRe.test(cc)){
11577 * Clear any invalid styles/messages for this field
11579 clearInvalid : function(){
11581 if(!this.el || this.preventMark){ // not rendered
11586 this.el.removeClass([this.invalidClass, 'is-invalid']);
11588 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11590 var feedback = this.el.select('.form-control-feedback', true).first();
11593 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11598 if(this.indicator){
11599 this.indicator.removeClass('visible');
11600 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11603 this.fireEvent('valid', this);
11607 * Mark this field as valid
11609 markValid : function()
11611 if(!this.el || this.preventMark){ // not rendered...
11615 this.el.removeClass([this.invalidClass, this.validClass]);
11616 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11624 if(this.indicator){
11625 this.indicator.removeClass('visible');
11626 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11634 if(this.allowBlank && !this.getRawValue().length){
11637 if (Roo.bootstrap.version == 3) {
11638 this.el.addClass(this.validClass);
11640 this.inputEl().addClass('is-valid');
11643 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11645 var feedback = this.el.select('.form-control-feedback', true).first();
11648 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11654 this.fireEvent('valid', this);
11658 * Mark this field as invalid
11659 * @param {String} msg The validation message
11661 markInvalid : function(msg)
11663 if(!this.el || this.preventMark){ // not rendered
11667 this.el.removeClass([this.invalidClass, this.validClass]);
11668 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11670 var feedback = this.el.select('.form-control-feedback', true).first();
11673 this.el.select('.form-control-feedback', true).first().removeClass(
11674 [this.invalidFeedbackClass, this.validFeedbackClass]);
11681 if(this.allowBlank && !this.getRawValue().length){
11685 if(this.indicator){
11686 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11687 this.indicator.addClass('visible');
11689 if (Roo.bootstrap.version == 3) {
11690 this.el.addClass(this.invalidClass);
11692 this.inputEl().addClass('is-invalid');
11697 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11699 var feedback = this.el.select('.form-control-feedback', true).first();
11702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11704 if(this.getValue().length || this.forceFeedback){
11705 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11712 this.fireEvent('invalid', this, msg);
11715 SafariOnKeyDown : function(event)
11717 // this is a workaround for a password hang bug on chrome/ webkit.
11718 if (this.inputEl().dom.type != 'password') {
11722 var isSelectAll = false;
11724 if(this.inputEl().dom.selectionEnd > 0){
11725 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11727 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11728 event.preventDefault();
11733 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11735 event.preventDefault();
11736 // this is very hacky as keydown always get's upper case.
11738 var cc = String.fromCharCode(event.getCharCode());
11739 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11743 adjustWidth : function(tag, w){
11744 tag = tag.toLowerCase();
11745 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11746 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11747 if(tag == 'input'){
11750 if(tag == 'textarea'){
11753 }else if(Roo.isOpera){
11754 if(tag == 'input'){
11757 if(tag == 'textarea'){
11765 setFieldLabel : function(v)
11767 if(!this.rendered){
11771 if(this.indicatorEl()){
11772 var ar = this.el.select('label > span',true);
11774 if (ar.elements.length) {
11775 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11776 this.fieldLabel = v;
11780 var br = this.el.select('label',true);
11782 if(br.elements.length) {
11783 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784 this.fieldLabel = v;
11788 Roo.log('Cannot Found any of label > span || label in input');
11792 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11793 this.fieldLabel = v;
11808 * @class Roo.bootstrap.TextArea
11809 * @extends Roo.bootstrap.Input
11810 * Bootstrap TextArea class
11811 * @cfg {Number} cols Specifies the visible width of a text area
11812 * @cfg {Number} rows Specifies the visible number of lines in a text area
11813 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11814 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11815 * @cfg {string} html text
11818 * Create a new TextArea
11819 * @param {Object} config The config object
11822 Roo.bootstrap.TextArea = function(config){
11823 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11827 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11837 getAutoCreate : function(){
11839 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11845 if(this.inputType != 'hidden'){
11846 cfg.cls = 'form-group' //input-group
11854 value : this.value || '',
11855 html: this.html || '',
11856 cls : 'form-control',
11857 placeholder : this.placeholder || ''
11861 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11862 input.maxLength = this.maxLength;
11866 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11870 input.cols = this.cols;
11873 if (this.readOnly) {
11874 input.readonly = true;
11878 input.name = this.name;
11882 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11886 ['xs','sm','md','lg'].map(function(size){
11887 if (settings[size]) {
11888 cfg.cls += ' col-' + size + '-' + settings[size];
11892 var inputblock = input;
11894 if(this.hasFeedback && !this.allowBlank){
11898 cls: 'glyphicon form-control-feedback'
11902 cls : 'has-feedback',
11911 if (this.before || this.after) {
11914 cls : 'input-group',
11918 inputblock.cn.push({
11920 cls : 'input-group-addon',
11925 inputblock.cn.push(input);
11927 if(this.hasFeedback && !this.allowBlank){
11928 inputblock.cls += ' has-feedback';
11929 inputblock.cn.push(feedback);
11933 inputblock.cn.push({
11935 cls : 'input-group-addon',
11942 if (align ==='left' && this.fieldLabel.length) {
11947 cls : 'control-label',
11948 html : this.fieldLabel
11959 if(this.labelWidth > 12){
11960 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11963 if(this.labelWidth < 13 && this.labelmd == 0){
11964 this.labelmd = this.labelWidth;
11967 if(this.labellg > 0){
11968 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11969 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11972 if(this.labelmd > 0){
11973 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11974 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11977 if(this.labelsm > 0){
11978 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11979 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11982 if(this.labelxs > 0){
11983 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11984 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11987 } else if ( this.fieldLabel.length) {
11992 //cls : 'input-group-addon',
11993 html : this.fieldLabel
12011 if (this.disabled) {
12012 input.disabled=true;
12019 * return the real textarea element.
12021 inputEl: function ()
12023 return this.el.select('textarea.form-control',true).first();
12027 * Clear any invalid styles/messages for this field
12029 clearInvalid : function()
12032 if(!this.el || this.preventMark){ // not rendered
12036 var label = this.el.select('label', true).first();
12037 var icon = this.el.select('i.fa-star', true).first();
12042 this.el.removeClass( this.validClass);
12043 this.inputEl().removeClass('is-invalid');
12045 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12047 var feedback = this.el.select('.form-control-feedback', true).first();
12050 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12055 this.fireEvent('valid', this);
12059 * Mark this field as valid
12061 markValid : function()
12063 if(!this.el || this.preventMark){ // not rendered
12067 this.el.removeClass([this.invalidClass, this.validClass]);
12068 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12070 var feedback = this.el.select('.form-control-feedback', true).first();
12073 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12076 if(this.disabled || this.allowBlank){
12080 var label = this.el.select('label', true).first();
12081 var icon = this.el.select('i.fa-star', true).first();
12086 if (Roo.bootstrap.version == 3) {
12087 this.el.addClass(this.validClass);
12089 this.inputEl().addClass('is-valid');
12093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12095 var feedback = this.el.select('.form-control-feedback', true).first();
12098 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12104 this.fireEvent('valid', this);
12108 * Mark this field as invalid
12109 * @param {String} msg The validation message
12111 markInvalid : function(msg)
12113 if(!this.el || this.preventMark){ // not rendered
12117 this.el.removeClass([this.invalidClass, this.validClass]);
12118 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12120 var feedback = this.el.select('.form-control-feedback', true).first();
12123 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12126 if(this.disabled || this.allowBlank){
12130 var label = this.el.select('label', true).first();
12131 var icon = this.el.select('i.fa-star', true).first();
12133 if(!this.getValue().length && label && !icon){
12134 this.el.createChild({
12136 cls : 'text-danger fa fa-lg fa-star',
12137 tooltip : 'This field is required',
12138 style : 'margin-right:5px;'
12142 if (Roo.bootstrap.version == 3) {
12143 this.el.addClass(this.invalidClass);
12145 this.inputEl().addClass('is-invalid');
12148 // fixme ... this may be depricated need to test..
12149 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12151 var feedback = this.el.select('.form-control-feedback', true).first();
12154 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12156 if(this.getValue().length || this.forceFeedback){
12157 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12164 this.fireEvent('invalid', this, msg);
12172 * trigger field - base class for combo..
12177 * @class Roo.bootstrap.TriggerField
12178 * @extends Roo.bootstrap.Input
12179 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12180 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12181 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12182 * for which you can provide a custom implementation. For example:
12184 var trigger = new Roo.bootstrap.TriggerField();
12185 trigger.onTriggerClick = myTriggerFn;
12186 trigger.applyTo('my-field');
12189 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12190 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12191 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12192 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12193 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12196 * Create a new TriggerField.
12197 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12198 * to the base TextField)
12200 Roo.bootstrap.TriggerField = function(config){
12201 this.mimicing = false;
12202 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12205 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12207 * @cfg {String} triggerClass A CSS class to apply to the trigger
12210 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12215 * @cfg {Boolean} removable (true|false) special filter default false
12219 /** @cfg {Boolean} grow @hide */
12220 /** @cfg {Number} growMin @hide */
12221 /** @cfg {Number} growMax @hide */
12227 autoSize: Roo.emptyFn,
12231 deferHeight : true,
12234 actionMode : 'wrap',
12239 getAutoCreate : function(){
12241 var align = this.labelAlign || this.parentLabelAlign();
12246 cls: 'form-group' //input-group
12253 type : this.inputType,
12254 cls : 'form-control',
12255 autocomplete: 'new-password',
12256 placeholder : this.placeholder || ''
12260 input.name = this.name;
12263 input.cls += ' input-' + this.size;
12266 if (this.disabled) {
12267 input.disabled=true;
12270 var inputblock = input;
12272 if(this.hasFeedback && !this.allowBlank){
12276 cls: 'glyphicon form-control-feedback'
12279 if(this.removable && !this.editable ){
12281 cls : 'has-feedback',
12287 cls : 'roo-combo-removable-btn close'
12294 cls : 'has-feedback',
12303 if(this.removable && !this.editable ){
12305 cls : 'roo-removable',
12311 cls : 'roo-combo-removable-btn close'
12318 if (this.before || this.after) {
12321 cls : 'input-group',
12325 inputblock.cn.push({
12327 cls : 'input-group-addon input-group-prepend input-group-text',
12332 inputblock.cn.push(input);
12334 if(this.hasFeedback && !this.allowBlank){
12335 inputblock.cls += ' has-feedback';
12336 inputblock.cn.push(feedback);
12340 inputblock.cn.push({
12342 cls : 'input-group-addon input-group-append input-group-text',
12351 var ibwrap = inputblock;
12356 cls: 'roo-select2-choices',
12360 cls: 'roo-select2-search-field',
12372 cls: 'roo-select2-container input-group',
12377 cls: 'form-hidden-field'
12383 if(!this.multiple && this.showToggleBtn){
12389 if (this.caret != false) {
12392 cls: 'fa fa-' + this.caret
12399 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12401 Roo.bootstrap.version == 3 ? caret : '',
12404 cls: 'combobox-clear',
12418 combobox.cls += ' roo-select2-container-multi';
12422 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12423 tooltip : 'This field is required'
12425 if (Roo.bootstrap.version == 4) {
12428 style : 'display:none'
12433 if (align ==='left' && this.fieldLabel.length) {
12435 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12442 cls : 'control-label',
12443 html : this.fieldLabel
12455 var labelCfg = cfg.cn[1];
12456 var contentCfg = cfg.cn[2];
12458 if(this.indicatorpos == 'right'){
12463 cls : 'control-label',
12467 html : this.fieldLabel
12481 labelCfg = cfg.cn[0];
12482 contentCfg = cfg.cn[1];
12485 if(this.labelWidth > 12){
12486 labelCfg.style = "width: " + this.labelWidth + 'px';
12489 if(this.labelWidth < 13 && this.labelmd == 0){
12490 this.labelmd = this.labelWidth;
12493 if(this.labellg > 0){
12494 labelCfg.cls += ' col-lg-' + this.labellg;
12495 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12498 if(this.labelmd > 0){
12499 labelCfg.cls += ' col-md-' + this.labelmd;
12500 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12503 if(this.labelsm > 0){
12504 labelCfg.cls += ' col-sm-' + this.labelsm;
12505 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12508 if(this.labelxs > 0){
12509 labelCfg.cls += ' col-xs-' + this.labelxs;
12510 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12513 } else if ( this.fieldLabel.length) {
12514 // Roo.log(" label");
12519 //cls : 'input-group-addon',
12520 html : this.fieldLabel
12528 if(this.indicatorpos == 'right'){
12536 html : this.fieldLabel
12550 // Roo.log(" no label && no align");
12557 ['xs','sm','md','lg'].map(function(size){
12558 if (settings[size]) {
12559 cfg.cls += ' col-' + size + '-' + settings[size];
12570 onResize : function(w, h){
12571 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12572 // if(typeof w == 'number'){
12573 // var x = w - this.trigger.getWidth();
12574 // this.inputEl().setWidth(this.adjustWidth('input', x));
12575 // this.trigger.setStyle('left', x+'px');
12580 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12583 getResizeEl : function(){
12584 return this.inputEl();
12588 getPositionEl : function(){
12589 return this.inputEl();
12593 alignErrorIcon : function(){
12594 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12598 initEvents : function(){
12602 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12603 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12604 if(!this.multiple && this.showToggleBtn){
12605 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12606 if(this.hideTrigger){
12607 this.trigger.setDisplayed(false);
12609 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12613 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12616 if(this.removable && !this.editable && !this.tickable){
12617 var close = this.closeTriggerEl();
12620 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12621 close.on('click', this.removeBtnClick, this, close);
12625 //this.trigger.addClassOnOver('x-form-trigger-over');
12626 //this.trigger.addClassOnClick('x-form-trigger-click');
12629 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12633 closeTriggerEl : function()
12635 var close = this.el.select('.roo-combo-removable-btn', true).first();
12636 return close ? close : false;
12639 removeBtnClick : function(e, h, el)
12641 e.preventDefault();
12643 if(this.fireEvent("remove", this) !== false){
12645 this.fireEvent("afterremove", this)
12649 createList : function()
12651 this.list = Roo.get(document.body).createChild({
12652 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12653 cls: 'typeahead typeahead-long dropdown-menu shadow',
12654 style: 'display:none'
12657 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12662 initTrigger : function(){
12667 onDestroy : function(){
12669 this.trigger.removeAllListeners();
12670 // this.trigger.remove();
12673 // this.wrap.remove();
12675 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12679 onFocus : function(){
12680 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12682 if(!this.mimicing){
12683 this.wrap.addClass('x-trigger-wrap-focus');
12684 this.mimicing = true;
12685 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12686 if(this.monitorTab){
12687 this.el.on("keydown", this.checkTab, this);
12694 checkTab : function(e){
12695 if(e.getKey() == e.TAB){
12696 this.triggerBlur();
12701 onBlur : function(){
12706 mimicBlur : function(e, t){
12708 if(!this.wrap.contains(t) && this.validateBlur()){
12709 this.triggerBlur();
12715 triggerBlur : function(){
12716 this.mimicing = false;
12717 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12718 if(this.monitorTab){
12719 this.el.un("keydown", this.checkTab, this);
12721 //this.wrap.removeClass('x-trigger-wrap-focus');
12722 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12726 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12727 validateBlur : function(e, t){
12732 onDisable : function(){
12733 this.inputEl().dom.disabled = true;
12734 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12736 // this.wrap.addClass('x-item-disabled');
12741 onEnable : function(){
12742 this.inputEl().dom.disabled = false;
12743 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12745 // this.el.removeClass('x-item-disabled');
12750 onShow : function(){
12751 var ae = this.getActionEl();
12754 ae.dom.style.display = '';
12755 ae.dom.style.visibility = 'visible';
12761 onHide : function(){
12762 var ae = this.getActionEl();
12763 ae.dom.style.display = 'none';
12767 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12768 * by an implementing function.
12770 * @param {EventObject} e
12772 onTriggerClick : Roo.emptyFn
12780 * @class Roo.bootstrap.CardUploader
12781 * @extends Roo.bootstrap.Button
12782 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12783 * @cfg {Number} errorTimeout default 3000
12784 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12785 * @cfg {Array} html The button text.
12789 * Create a new CardUploader
12790 * @param {Object} config The config object
12793 Roo.bootstrap.CardUploader = function(config){
12797 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12800 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12808 * When a image is clicked on - and needs to display a slideshow or similar..
12809 * @param {Roo.bootstrap.Card} this
12810 * @param {Object} The image information data
12816 * When a the download link is clicked
12817 * @param {Roo.bootstrap.Card} this
12818 * @param {Object} The image information data contains
12825 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12828 errorTimeout : 3000,
12832 fileCollection : false,
12835 getAutoCreate : function()
12839 cls :'form-group' ,
12844 //cls : 'input-group-addon',
12845 html : this.fieldLabel
12853 value : this.value,
12854 cls : 'd-none form-control'
12859 multiple : 'multiple',
12861 cls : 'd-none roo-card-upload-selector'
12865 cls : 'roo-card-uploader-button-container w-100 mb-2'
12868 cls : 'card-columns roo-card-uploader-container'
12878 getChildContainer : function() /// what children are added to.
12880 return this.containerEl;
12883 getButtonContainer : function() /// what children are added to.
12885 return this.el.select(".roo-card-uploader-button-container").first();
12888 initEvents : function()
12891 Roo.bootstrap.Input.prototype.initEvents.call(this);
12895 xns: Roo.bootstrap,
12898 container_method : 'getButtonContainer' ,
12899 html : this.html, // fix changable?
12902 'click' : function(btn, e) {
12911 this.urlAPI = (window.createObjectURL && window) ||
12912 (window.URL && URL.revokeObjectURL && URL) ||
12913 (window.webkitURL && webkitURL);
12918 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12920 this.selectorEl.on('change', this.onFileSelected, this);
12923 this.images.forEach(function(img) {
12926 this.images = false;
12928 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12934 onClick : function(e)
12936 e.preventDefault();
12938 this.selectorEl.dom.click();
12942 onFileSelected : function(e)
12944 e.preventDefault();
12946 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12950 Roo.each(this.selectorEl.dom.files, function(file){
12951 this.addFile(file);
12960 addFile : function(file)
12963 if(typeof(file) === 'string'){
12964 throw "Add file by name?"; // should not happen
12968 if(!file || !this.urlAPI){
12978 var url = _this.urlAPI.createObjectURL( file);
12981 id : Roo.bootstrap.CardUploader.ID--,
12982 is_uploaded : false,
12986 mimetype : file.type,
12994 * addCard - add an Attachment to the uploader
12995 * @param data - the data about the image to upload
12999 title : "Title of file",
13000 is_uploaded : false,
13001 src : "http://.....",
13002 srcfile : { the File upload object },
13003 mimetype : file.type,
13006 .. any other data...
13012 addCard : function (data)
13014 // hidden input element?
13015 // if the file is not an image...
13016 //then we need to use something other that and header_image
13021 xns : Roo.bootstrap,
13022 xtype : 'CardFooter',
13025 xns : Roo.bootstrap,
13031 xns : Roo.bootstrap,
13033 html : String.format("<small>{0}</small>", data.title),
13034 cls : 'col-10 text-left',
13039 click : function() {
13041 t.fireEvent( "download", t, data );
13047 xns : Roo.bootstrap,
13049 style: 'max-height: 28px; ',
13055 click : function() {
13056 t.removeCard(data.id)
13068 var cn = this.addxtype(
13071 xns : Roo.bootstrap,
13074 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13075 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13076 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13081 initEvents : function() {
13082 Roo.bootstrap.Card.prototype.initEvents.call(this);
13084 this.imgEl = this.el.select('.card-img-top').first();
13086 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13087 this.imgEl.set({ 'pointer' : 'cursor' });
13090 this.getCardFooter().addClass('p-1');
13097 // dont' really need ot update items.
13098 // this.items.push(cn);
13099 this.fileCollection.add(cn);
13101 if (!data.srcfile) {
13102 this.updateInput();
13107 var reader = new FileReader();
13108 reader.addEventListener("load", function() {
13109 data.srcdata = reader.result;
13112 reader.readAsDataURL(data.srcfile);
13117 removeCard : function(id)
13120 var card = this.fileCollection.get(id);
13121 card.data.is_deleted = 1;
13122 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13123 //this.fileCollection.remove(card);
13124 //this.items = this.items.filter(function(e) { return e != card });
13125 // dont' really need ot update items.
13126 card.el.dom.parentNode.removeChild(card.el.dom);
13127 this.updateInput();
13133 this.fileCollection.each(function(card) {
13134 if (card.el.dom && card.el.dom.parentNode) {
13135 card.el.dom.parentNode.removeChild(card.el.dom);
13138 this.fileCollection.clear();
13139 this.updateInput();
13142 updateInput : function()
13145 this.fileCollection.each(function(e) {
13149 this.inputEl().dom.value = JSON.stringify(data);
13159 Roo.bootstrap.CardUploader.ID = -1;/*
13161 * Ext JS Library 1.1.1
13162 * Copyright(c) 2006-2007, Ext JS, LLC.
13164 * Originally Released Under LGPL - original licence link has changed is not relivant.
13167 * <script type="text/javascript">
13172 * @class Roo.data.SortTypes
13174 * Defines the default sorting (casting?) comparison functions used when sorting data.
13176 Roo.data.SortTypes = {
13178 * Default sort that does nothing
13179 * @param {Mixed} s The value being converted
13180 * @return {Mixed} The comparison value
13182 none : function(s){
13187 * The regular expression used to strip tags
13191 stripTagsRE : /<\/?[^>]+>/gi,
13194 * Strips all HTML tags to sort on text only
13195 * @param {Mixed} s The value being converted
13196 * @return {String} The comparison value
13198 asText : function(s){
13199 return String(s).replace(this.stripTagsRE, "");
13203 * Strips all HTML tags to sort on text only - Case insensitive
13204 * @param {Mixed} s The value being converted
13205 * @return {String} The comparison value
13207 asUCText : function(s){
13208 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13212 * Case insensitive string
13213 * @param {Mixed} s The value being converted
13214 * @return {String} The comparison value
13216 asUCString : function(s) {
13217 return String(s).toUpperCase();
13222 * @param {Mixed} s The value being converted
13223 * @return {Number} The comparison value
13225 asDate : function(s) {
13229 if(s instanceof Date){
13230 return s.getTime();
13232 return Date.parse(String(s));
13237 * @param {Mixed} s The value being converted
13238 * @return {Float} The comparison value
13240 asFloat : function(s) {
13241 var val = parseFloat(String(s).replace(/,/g, ""));
13250 * @param {Mixed} s The value being converted
13251 * @return {Number} The comparison value
13253 asInt : function(s) {
13254 var val = parseInt(String(s).replace(/,/g, ""));
13262 * Ext JS Library 1.1.1
13263 * Copyright(c) 2006-2007, Ext JS, LLC.
13265 * Originally Released Under LGPL - original licence link has changed is not relivant.
13268 * <script type="text/javascript">
13272 * @class Roo.data.Record
13273 * Instances of this class encapsulate both record <em>definition</em> information, and record
13274 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13275 * to access Records cached in an {@link Roo.data.Store} object.<br>
13277 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13278 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13281 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13283 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13284 * {@link #create}. The parameters are the same.
13285 * @param {Array} data An associative Array of data values keyed by the field name.
13286 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13287 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13288 * not specified an integer id is generated.
13290 Roo.data.Record = function(data, id){
13291 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13296 * Generate a constructor for a specific record layout.
13297 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13298 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13299 * Each field definition object may contain the following properties: <ul>
13300 * <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,
13301 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13302 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13303 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13304 * is being used, then this is a string containing the javascript expression to reference the data relative to
13305 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13306 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13307 * this may be omitted.</p></li>
13308 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13309 * <ul><li>auto (Default, implies no conversion)</li>
13314 * <li>date</li></ul></p></li>
13315 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13316 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13317 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13318 * by the Reader into an object that will be stored in the Record. It is passed the
13319 * following parameters:<ul>
13320 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13322 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13324 * <br>usage:<br><pre><code>
13325 var TopicRecord = Roo.data.Record.create(
13326 {name: 'title', mapping: 'topic_title'},
13327 {name: 'author', mapping: 'username'},
13328 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13329 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13330 {name: 'lastPoster', mapping: 'user2'},
13331 {name: 'excerpt', mapping: 'post_text'}
13334 var myNewRecord = new TopicRecord({
13335 title: 'Do my job please',
13338 lastPost: new Date(),
13339 lastPoster: 'Animal',
13340 excerpt: 'No way dude!'
13342 myStore.add(myNewRecord);
13347 Roo.data.Record.create = function(o){
13348 var f = function(){
13349 f.superclass.constructor.apply(this, arguments);
13351 Roo.extend(f, Roo.data.Record);
13352 var p = f.prototype;
13353 p.fields = new Roo.util.MixedCollection(false, function(field){
13356 for(var i = 0, len = o.length; i < len; i++){
13357 p.fields.add(new Roo.data.Field(o[i]));
13359 f.getField = function(name){
13360 return p.fields.get(name);
13365 Roo.data.Record.AUTO_ID = 1000;
13366 Roo.data.Record.EDIT = 'edit';
13367 Roo.data.Record.REJECT = 'reject';
13368 Roo.data.Record.COMMIT = 'commit';
13370 Roo.data.Record.prototype = {
13372 * Readonly flag - true if this record has been modified.
13381 join : function(store){
13382 this.store = store;
13386 * Set the named field to the specified value.
13387 * @param {String} name The name of the field to set.
13388 * @param {Object} value The value to set the field to.
13390 set : function(name, value){
13391 if(this.data[name] == value){
13395 if(!this.modified){
13396 this.modified = {};
13398 if(typeof this.modified[name] == 'undefined'){
13399 this.modified[name] = this.data[name];
13401 this.data[name] = value;
13402 if(!this.editing && this.store){
13403 this.store.afterEdit(this);
13408 * Get the value of the named field.
13409 * @param {String} name The name of the field to get the value of.
13410 * @return {Object} The value of the field.
13412 get : function(name){
13413 return this.data[name];
13417 beginEdit : function(){
13418 this.editing = true;
13419 this.modified = {};
13423 cancelEdit : function(){
13424 this.editing = false;
13425 delete this.modified;
13429 endEdit : function(){
13430 this.editing = false;
13431 if(this.dirty && this.store){
13432 this.store.afterEdit(this);
13437 * Usually called by the {@link Roo.data.Store} which owns the Record.
13438 * Rejects all changes made to the Record since either creation, or the last commit operation.
13439 * Modified fields are reverted to their original values.
13441 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13442 * of reject operations.
13444 reject : function(){
13445 var m = this.modified;
13447 if(typeof m[n] != "function"){
13448 this.data[n] = m[n];
13451 this.dirty = false;
13452 delete this.modified;
13453 this.editing = false;
13455 this.store.afterReject(this);
13460 * Usually called by the {@link Roo.data.Store} which owns the Record.
13461 * Commits all changes made to the Record since either creation, or the last commit operation.
13463 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464 * of commit operations.
13466 commit : function(){
13467 this.dirty = false;
13468 delete this.modified;
13469 this.editing = false;
13471 this.store.afterCommit(this);
13476 hasError : function(){
13477 return this.error != null;
13481 clearError : function(){
13486 * Creates a copy of this record.
13487 * @param {String} id (optional) A new record id if you don't want to use this record's id
13490 copy : function(newId) {
13491 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13495 * Ext JS Library 1.1.1
13496 * Copyright(c) 2006-2007, Ext JS, LLC.
13498 * Originally Released Under LGPL - original licence link has changed is not relivant.
13501 * <script type="text/javascript">
13507 * @class Roo.data.Store
13508 * @extends Roo.util.Observable
13509 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13510 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13512 * 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
13513 * has no knowledge of the format of the data returned by the Proxy.<br>
13515 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13516 * instances from the data object. These records are cached and made available through accessor functions.
13518 * Creates a new Store.
13519 * @param {Object} config A config object containing the objects needed for the Store to access data,
13520 * and read the data into Records.
13522 Roo.data.Store = function(config){
13523 this.data = new Roo.util.MixedCollection(false);
13524 this.data.getKey = function(o){
13527 this.baseParams = {};
13529 this.paramNames = {
13534 "multisort" : "_multisort"
13537 if(config && config.data){
13538 this.inlineData = config.data;
13539 delete config.data;
13542 Roo.apply(this, config);
13544 if(this.reader){ // reader passed
13545 this.reader = Roo.factory(this.reader, Roo.data);
13546 this.reader.xmodule = this.xmodule || false;
13547 if(!this.recordType){
13548 this.recordType = this.reader.recordType;
13550 if(this.reader.onMetaChange){
13551 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13555 if(this.recordType){
13556 this.fields = this.recordType.prototype.fields;
13558 this.modified = [];
13562 * @event datachanged
13563 * Fires when the data cache has changed, and a widget which is using this Store
13564 * as a Record cache should refresh its view.
13565 * @param {Store} this
13567 datachanged : true,
13569 * @event metachange
13570 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13571 * @param {Store} this
13572 * @param {Object} meta The JSON metadata
13577 * Fires when Records have been added to the Store
13578 * @param {Store} this
13579 * @param {Roo.data.Record[]} records The array of Records added
13580 * @param {Number} index The index at which the record(s) were added
13585 * Fires when a Record has been removed from the Store
13586 * @param {Store} this
13587 * @param {Roo.data.Record} record The Record that was removed
13588 * @param {Number} index The index at which the record was removed
13593 * Fires when a Record has been updated
13594 * @param {Store} this
13595 * @param {Roo.data.Record} record The Record that was updated
13596 * @param {String} operation The update operation being performed. Value may be one of:
13598 Roo.data.Record.EDIT
13599 Roo.data.Record.REJECT
13600 Roo.data.Record.COMMIT
13606 * Fires when the data cache has been cleared.
13607 * @param {Store} this
13611 * @event beforeload
13612 * Fires before a request is made for a new data object. If the beforeload handler returns false
13613 * the load action will be canceled.
13614 * @param {Store} this
13615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13619 * @event beforeloadadd
13620 * Fires after a new set of Records has been loaded.
13621 * @param {Store} this
13622 * @param {Roo.data.Record[]} records The Records that were loaded
13623 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13625 beforeloadadd : true,
13628 * Fires after a new set of Records has been loaded, before they are added to the store.
13629 * @param {Store} this
13630 * @param {Roo.data.Record[]} records The Records that were loaded
13631 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632 * @params {Object} return from reader
13636 * @event loadexception
13637 * Fires if an exception occurs in the Proxy during loading.
13638 * Called with the signature of the Proxy's "loadexception" event.
13639 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13642 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13643 * @param {Object} load options
13644 * @param {Object} jsonData from your request (normally this contains the Exception)
13646 loadexception : true
13650 this.proxy = Roo.factory(this.proxy, Roo.data);
13651 this.proxy.xmodule = this.xmodule || false;
13652 this.relayEvents(this.proxy, ["loadexception"]);
13654 this.sortToggle = {};
13655 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13657 Roo.data.Store.superclass.constructor.call(this);
13659 if(this.inlineData){
13660 this.loadData(this.inlineData);
13661 delete this.inlineData;
13665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13667 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13668 * without a remote query - used by combo/forms at present.
13672 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13675 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13678 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13679 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13682 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13683 * on any HTTP request
13686 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13689 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13693 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13694 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13696 remoteSort : false,
13699 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13700 * loaded or when a record is removed. (defaults to false).
13702 pruneModifiedRecords : false,
13705 lastOptions : null,
13708 * Add Records to the Store and fires the add event.
13709 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13711 add : function(records){
13712 records = [].concat(records);
13713 for(var i = 0, len = records.length; i < len; i++){
13714 records[i].join(this);
13716 var index = this.data.length;
13717 this.data.addAll(records);
13718 this.fireEvent("add", this, records, index);
13722 * Remove a Record from the Store and fires the remove event.
13723 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13725 remove : function(record){
13726 var index = this.data.indexOf(record);
13727 this.data.removeAt(index);
13729 if(this.pruneModifiedRecords){
13730 this.modified.remove(record);
13732 this.fireEvent("remove", this, record, index);
13736 * Remove all Records from the Store and fires the clear event.
13738 removeAll : function(){
13740 if(this.pruneModifiedRecords){
13741 this.modified = [];
13743 this.fireEvent("clear", this);
13747 * Inserts Records to the Store at the given index and fires the add event.
13748 * @param {Number} index The start index at which to insert the passed Records.
13749 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13751 insert : function(index, records){
13752 records = [].concat(records);
13753 for(var i = 0, len = records.length; i < len; i++){
13754 this.data.insert(index, records[i]);
13755 records[i].join(this);
13757 this.fireEvent("add", this, records, index);
13761 * Get the index within the cache of the passed Record.
13762 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13763 * @return {Number} The index of the passed Record. Returns -1 if not found.
13765 indexOf : function(record){
13766 return this.data.indexOf(record);
13770 * Get the index within the cache of the Record with the passed id.
13771 * @param {String} id The id of the Record to find.
13772 * @return {Number} The index of the Record. Returns -1 if not found.
13774 indexOfId : function(id){
13775 return this.data.indexOfKey(id);
13779 * Get the Record with the specified id.
13780 * @param {String} id The id of the Record to find.
13781 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13783 getById : function(id){
13784 return this.data.key(id);
13788 * Get the Record at the specified index.
13789 * @param {Number} index The index of the Record to find.
13790 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13792 getAt : function(index){
13793 return this.data.itemAt(index);
13797 * Returns a range of Records between specified indices.
13798 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13799 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13800 * @return {Roo.data.Record[]} An array of Records
13802 getRange : function(start, end){
13803 return this.data.getRange(start, end);
13807 storeOptions : function(o){
13808 o = Roo.apply({}, o);
13811 this.lastOptions = o;
13815 * Loads the Record cache from the configured Proxy using the configured Reader.
13817 * If using remote paging, then the first load call must specify the <em>start</em>
13818 * and <em>limit</em> properties in the options.params property to establish the initial
13819 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13821 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13822 * and this call will return before the new data has been loaded. Perform any post-processing
13823 * in a callback function, or in a "load" event handler.</strong>
13825 * @param {Object} options An object containing properties which control loading options:<ul>
13826 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13827 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13828 * passed the following arguments:<ul>
13829 * <li>r : Roo.data.Record[]</li>
13830 * <li>options: Options object from the load call</li>
13831 * <li>success: Boolean success indicator</li></ul></li>
13832 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13833 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13836 load : function(options){
13837 options = options || {};
13838 if(this.fireEvent("beforeload", this, options) !== false){
13839 this.storeOptions(options);
13840 var p = Roo.apply(options.params || {}, this.baseParams);
13841 // if meta was not loaded from remote source.. try requesting it.
13842 if (!this.reader.metaFromRemote) {
13843 p._requestMeta = 1;
13845 if(this.sortInfo && this.remoteSort){
13846 var pn = this.paramNames;
13847 p[pn["sort"]] = this.sortInfo.field;
13848 p[pn["dir"]] = this.sortInfo.direction;
13850 if (this.multiSort) {
13851 var pn = this.paramNames;
13852 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13855 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13860 * Reloads the Record cache from the configured Proxy using the configured Reader and
13861 * the options from the last load operation performed.
13862 * @param {Object} options (optional) An object containing properties which may override the options
13863 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13864 * the most recently used options are reused).
13866 reload : function(options){
13867 this.load(Roo.applyIf(options||{}, this.lastOptions));
13871 // Called as a callback by the Reader during a load operation.
13872 loadRecords : function(o, options, success){
13873 if(!o || success === false){
13874 if(success !== false){
13875 this.fireEvent("load", this, [], options, o);
13877 if(options.callback){
13878 options.callback.call(options.scope || this, [], options, false);
13882 // if data returned failure - throw an exception.
13883 if (o.success === false) {
13884 // show a message if no listener is registered.
13885 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13886 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13888 // loadmask wil be hooked into this..
13889 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13892 var r = o.records, t = o.totalRecords || r.length;
13894 this.fireEvent("beforeloadadd", this, r, options, o);
13896 if(!options || options.add !== true){
13897 if(this.pruneModifiedRecords){
13898 this.modified = [];
13900 for(var i = 0, len = r.length; i < len; i++){
13904 this.data = this.snapshot;
13905 delete this.snapshot;
13908 this.data.addAll(r);
13909 this.totalLength = t;
13911 this.fireEvent("datachanged", this);
13913 this.totalLength = Math.max(t, this.data.length+r.length);
13917 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13919 var e = new Roo.data.Record({});
13921 e.set(this.parent.displayField, this.parent.emptyTitle);
13922 e.set(this.parent.valueField, '');
13927 this.fireEvent("load", this, r, options, o);
13928 if(options.callback){
13929 options.callback.call(options.scope || this, r, options, true);
13935 * Loads data from a passed data block. A Reader which understands the format of the data
13936 * must have been configured in the constructor.
13937 * @param {Object} data The data block from which to read the Records. The format of the data expected
13938 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13939 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13941 loadData : function(o, append){
13942 var r = this.reader.readRecords(o);
13943 this.loadRecords(r, {add: append}, true);
13947 * using 'cn' the nested child reader read the child array into it's child stores.
13948 * @param {Object} rec The record with a 'children array
13950 loadDataFromChildren : function(rec)
13952 this.loadData(this.reader.toLoadData(rec));
13957 * Gets the number of cached records.
13959 * <em>If using paging, this may not be the total size of the dataset. If the data object
13960 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13961 * the data set size</em>
13963 getCount : function(){
13964 return this.data.length || 0;
13968 * Gets the total number of records in the dataset as returned by the server.
13970 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13971 * the dataset size</em>
13973 getTotalCount : function(){
13974 return this.totalLength || 0;
13978 * Returns the sort state of the Store as an object with two properties:
13980 field {String} The name of the field by which the Records are sorted
13981 direction {String} The sort order, "ASC" or "DESC"
13984 getSortState : function(){
13985 return this.sortInfo;
13989 applySort : function(){
13990 if(this.sortInfo && !this.remoteSort){
13991 var s = this.sortInfo, f = s.field;
13992 var st = this.fields.get(f).sortType;
13993 var fn = function(r1, r2){
13994 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13995 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13997 this.data.sort(s.direction, fn);
13998 if(this.snapshot && this.snapshot != this.data){
13999 this.snapshot.sort(s.direction, fn);
14005 * Sets the default sort column and order to be used by the next load operation.
14006 * @param {String} fieldName The name of the field to sort by.
14007 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14009 setDefaultSort : function(field, dir){
14010 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14014 * Sort the Records.
14015 * If remote sorting is used, the sort is performed on the server, and the cache is
14016 * reloaded. If local sorting is used, the cache is sorted internally.
14017 * @param {String} fieldName The name of the field to sort by.
14018 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14020 sort : function(fieldName, dir){
14021 var f = this.fields.get(fieldName);
14023 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14025 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14026 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14031 this.sortToggle[f.name] = dir;
14032 this.sortInfo = {field: f.name, direction: dir};
14033 if(!this.remoteSort){
14035 this.fireEvent("datachanged", this);
14037 this.load(this.lastOptions);
14042 * Calls the specified function for each of the Records in the cache.
14043 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14044 * Returning <em>false</em> aborts and exits the iteration.
14045 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14047 each : function(fn, scope){
14048 this.data.each(fn, scope);
14052 * Gets all records modified since the last commit. Modified records are persisted across load operations
14053 * (e.g., during paging).
14054 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14056 getModifiedRecords : function(){
14057 return this.modified;
14061 createFilterFn : function(property, value, anyMatch){
14062 if(!value.exec){ // not a regex
14063 value = String(value);
14064 if(value.length == 0){
14067 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14069 return function(r){
14070 return value.test(r.data[property]);
14075 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14076 * @param {String} property A field on your records
14077 * @param {Number} start The record index to start at (defaults to 0)
14078 * @param {Number} end The last record index to include (defaults to length - 1)
14079 * @return {Number} The sum
14081 sum : function(property, start, end){
14082 var rs = this.data.items, v = 0;
14083 start = start || 0;
14084 end = (end || end === 0) ? end : rs.length-1;
14086 for(var i = start; i <= end; i++){
14087 v += (rs[i].data[property] || 0);
14093 * Filter the records by a specified property.
14094 * @param {String} field A field on your records
14095 * @param {String/RegExp} value Either a string that the field
14096 * should start with or a RegExp to test against the field
14097 * @param {Boolean} anyMatch True to match any part not just the beginning
14099 filter : function(property, value, anyMatch){
14100 var fn = this.createFilterFn(property, value, anyMatch);
14101 return fn ? this.filterBy(fn) : this.clearFilter();
14105 * Filter by a function. The specified function will be called with each
14106 * record in this data source. If the function returns true the record is included,
14107 * otherwise it is filtered.
14108 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14109 * @param {Object} scope (optional) The scope of the function (defaults to this)
14111 filterBy : function(fn, scope){
14112 this.snapshot = this.snapshot || this.data;
14113 this.data = this.queryBy(fn, scope||this);
14114 this.fireEvent("datachanged", this);
14118 * Query the records by a specified property.
14119 * @param {String} field A field on your records
14120 * @param {String/RegExp} value Either a string that the field
14121 * should start with or a RegExp to test against the field
14122 * @param {Boolean} anyMatch True to match any part not just the beginning
14123 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14125 query : function(property, value, anyMatch){
14126 var fn = this.createFilterFn(property, value, anyMatch);
14127 return fn ? this.queryBy(fn) : this.data.clone();
14131 * Query by a function. The specified function will be called with each
14132 * record in this data source. If the function returns true the record is included
14134 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14135 * @param {Object} scope (optional) The scope of the function (defaults to this)
14136 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14138 queryBy : function(fn, scope){
14139 var data = this.snapshot || this.data;
14140 return data.filterBy(fn, scope||this);
14144 * Collects unique values for a particular dataIndex from this store.
14145 * @param {String} dataIndex The property to collect
14146 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14147 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14148 * @return {Array} An array of the unique values
14150 collect : function(dataIndex, allowNull, bypassFilter){
14151 var d = (bypassFilter === true && this.snapshot) ?
14152 this.snapshot.items : this.data.items;
14153 var v, sv, r = [], l = {};
14154 for(var i = 0, len = d.length; i < len; i++){
14155 v = d[i].data[dataIndex];
14157 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14166 * Revert to a view of the Record cache with no filtering applied.
14167 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14169 clearFilter : function(suppressEvent){
14170 if(this.snapshot && this.snapshot != this.data){
14171 this.data = this.snapshot;
14172 delete this.snapshot;
14173 if(suppressEvent !== true){
14174 this.fireEvent("datachanged", this);
14180 afterEdit : function(record){
14181 if(this.modified.indexOf(record) == -1){
14182 this.modified.push(record);
14184 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14188 afterReject : function(record){
14189 this.modified.remove(record);
14190 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14194 afterCommit : function(record){
14195 this.modified.remove(record);
14196 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14200 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14201 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14203 commitChanges : function(){
14204 var m = this.modified.slice(0);
14205 this.modified = [];
14206 for(var i = 0, len = m.length; i < len; i++){
14212 * Cancel outstanding changes on all changed records.
14214 rejectChanges : function(){
14215 var m = this.modified.slice(0);
14216 this.modified = [];
14217 for(var i = 0, len = m.length; i < len; i++){
14222 onMetaChange : function(meta, rtype, o){
14223 this.recordType = rtype;
14224 this.fields = rtype.prototype.fields;
14225 delete this.snapshot;
14226 this.sortInfo = meta.sortInfo || this.sortInfo;
14227 this.modified = [];
14228 this.fireEvent('metachange', this, this.reader.meta);
14231 moveIndex : function(data, type)
14233 var index = this.indexOf(data);
14235 var newIndex = index + type;
14239 this.insert(newIndex, data);
14244 * Ext JS Library 1.1.1
14245 * Copyright(c) 2006-2007, Ext JS, LLC.
14247 * Originally Released Under LGPL - original licence link has changed is not relivant.
14250 * <script type="text/javascript">
14254 * @class Roo.data.SimpleStore
14255 * @extends Roo.data.Store
14256 * Small helper class to make creating Stores from Array data easier.
14257 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14258 * @cfg {Array} fields An array of field definition objects, or field name strings.
14259 * @cfg {Object} an existing reader (eg. copied from another store)
14260 * @cfg {Array} data The multi-dimensional array of data
14262 * @param {Object} config
14264 Roo.data.SimpleStore = function(config)
14266 Roo.data.SimpleStore.superclass.constructor.call(this, {
14268 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14271 Roo.data.Record.create(config.fields)
14273 proxy : new Roo.data.MemoryProxy(config.data)
14277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14279 * Ext JS Library 1.1.1
14280 * Copyright(c) 2006-2007, Ext JS, LLC.
14282 * Originally Released Under LGPL - original licence link has changed is not relivant.
14285 * <script type="text/javascript">
14290 * @extends Roo.data.Store
14291 * @class Roo.data.JsonStore
14292 * Small helper class to make creating Stores for JSON data easier. <br/>
14294 var store = new Roo.data.JsonStore({
14295 url: 'get-images.php',
14297 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14300 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14301 * JsonReader and HttpProxy (unless inline data is provided).</b>
14302 * @cfg {Array} fields An array of field definition objects, or field name strings.
14304 * @param {Object} config
14306 Roo.data.JsonStore = function(c){
14307 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14308 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14309 reader: new Roo.data.JsonReader(c, c.fields)
14312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14314 * Ext JS Library 1.1.1
14315 * Copyright(c) 2006-2007, Ext JS, LLC.
14317 * Originally Released Under LGPL - original licence link has changed is not relivant.
14320 * <script type="text/javascript">
14324 Roo.data.Field = function(config){
14325 if(typeof config == "string"){
14326 config = {name: config};
14328 Roo.apply(this, config);
14331 this.type = "auto";
14334 var st = Roo.data.SortTypes;
14335 // named sortTypes are supported, here we look them up
14336 if(typeof this.sortType == "string"){
14337 this.sortType = st[this.sortType];
14340 // set default sortType for strings and dates
14341 if(!this.sortType){
14344 this.sortType = st.asUCString;
14347 this.sortType = st.asDate;
14350 this.sortType = st.none;
14355 var stripRe = /[\$,%]/g;
14357 // prebuilt conversion function for this field, instead of
14358 // switching every time we're reading a value
14360 var cv, dateFormat = this.dateFormat;
14365 cv = function(v){ return v; };
14368 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14372 return v !== undefined && v !== null && v !== '' ?
14373 parseInt(String(v).replace(stripRe, ""), 10) : '';
14378 return v !== undefined && v !== null && v !== '' ?
14379 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14384 cv = function(v){ return v === true || v === "true" || v == 1; };
14391 if(v instanceof Date){
14395 if(dateFormat == "timestamp"){
14396 return new Date(v*1000);
14398 return Date.parseDate(v, dateFormat);
14400 var parsed = Date.parse(v);
14401 return parsed ? new Date(parsed) : null;
14410 Roo.data.Field.prototype = {
14418 * Ext JS Library 1.1.1
14419 * Copyright(c) 2006-2007, Ext JS, LLC.
14421 * Originally Released Under LGPL - original licence link has changed is not relivant.
14424 * <script type="text/javascript">
14427 // Base class for reading structured data from a data source. This class is intended to be
14428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14431 * @class Roo.data.DataReader
14432 * Base class for reading structured data from a data source. This class is intended to be
14433 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14436 Roo.data.DataReader = function(meta, recordType){
14440 this.recordType = recordType instanceof Array ?
14441 Roo.data.Record.create(recordType) : recordType;
14444 Roo.data.DataReader.prototype = {
14447 readerType : 'Data',
14449 * Create an empty record
14450 * @param {Object} data (optional) - overlay some values
14451 * @return {Roo.data.Record} record created.
14453 newRow : function(d) {
14455 this.recordType.prototype.fields.each(function(c) {
14457 case 'int' : da[c.name] = 0; break;
14458 case 'date' : da[c.name] = new Date(); break;
14459 case 'float' : da[c.name] = 0.0; break;
14460 case 'boolean' : da[c.name] = false; break;
14461 default : da[c.name] = ""; break;
14465 return new this.recordType(Roo.apply(da, d));
14471 * Ext JS Library 1.1.1
14472 * Copyright(c) 2006-2007, Ext JS, LLC.
14474 * Originally Released Under LGPL - original licence link has changed is not relivant.
14477 * <script type="text/javascript">
14481 * @class Roo.data.DataProxy
14482 * @extends Roo.data.Observable
14483 * This class is an abstract base class for implementations which provide retrieval of
14484 * unformatted data objects.<br>
14486 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14487 * (of the appropriate type which knows how to parse the data object) to provide a block of
14488 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14490 * Custom implementations must implement the load method as described in
14491 * {@link Roo.data.HttpProxy#load}.
14493 Roo.data.DataProxy = function(){
14496 * @event beforeload
14497 * Fires before a network request is made to retrieve a data object.
14498 * @param {Object} This DataProxy object.
14499 * @param {Object} params The params parameter to the load function.
14504 * Fires before the load method's callback is called.
14505 * @param {Object} This DataProxy object.
14506 * @param {Object} o The data object.
14507 * @param {Object} arg The callback argument object passed to the load function.
14511 * @event loadexception
14512 * Fires if an Exception occurs during data retrieval.
14513 * @param {Object} This DataProxy object.
14514 * @param {Object} o The data object.
14515 * @param {Object} arg The callback argument object passed to the load function.
14516 * @param {Object} e The Exception.
14518 loadexception : true
14520 Roo.data.DataProxy.superclass.constructor.call(this);
14523 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14526 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14530 * Ext JS Library 1.1.1
14531 * Copyright(c) 2006-2007, Ext JS, LLC.
14533 * Originally Released Under LGPL - original licence link has changed is not relivant.
14536 * <script type="text/javascript">
14539 * @class Roo.data.MemoryProxy
14540 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14541 * to the Reader when its load method is called.
14543 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14545 Roo.data.MemoryProxy = function(data){
14549 Roo.data.MemoryProxy.superclass.constructor.call(this);
14553 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14556 * Load data from the requested source (in this case an in-memory
14557 * data object passed to the constructor), read the data object into
14558 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14559 * process that block using the passed callback.
14560 * @param {Object} params This parameter is not used by the MemoryProxy class.
14561 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14562 * object into a block of Roo.data.Records.
14563 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14564 * The function must be passed <ul>
14565 * <li>The Record block object</li>
14566 * <li>The "arg" argument from the load function</li>
14567 * <li>A boolean success indicator</li>
14569 * @param {Object} scope The scope in which to call the callback
14570 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14572 load : function(params, reader, callback, scope, arg){
14573 params = params || {};
14576 result = reader.readRecords(params.data ? params.data :this.data);
14578 this.fireEvent("loadexception", this, arg, null, e);
14579 callback.call(scope, null, arg, false);
14582 callback.call(scope, result, arg, true);
14586 update : function(params, records){
14591 * Ext JS Library 1.1.1
14592 * Copyright(c) 2006-2007, Ext JS, LLC.
14594 * Originally Released Under LGPL - original licence link has changed is not relivant.
14597 * <script type="text/javascript">
14600 * @class Roo.data.HttpProxy
14601 * @extends Roo.data.DataProxy
14602 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14603 * configured to reference a certain URL.<br><br>
14605 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14606 * from which the running page was served.<br><br>
14608 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14610 * Be aware that to enable the browser to parse an XML document, the server must set
14611 * the Content-Type header in the HTTP response to "text/xml".
14613 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14614 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14615 * will be used to make the request.
14617 Roo.data.HttpProxy = function(conn){
14618 Roo.data.HttpProxy.superclass.constructor.call(this);
14619 // is conn a conn config or a real conn?
14621 this.useAjax = !conn || !conn.events;
14625 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14626 // thse are take from connection...
14629 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14632 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14633 * extra parameters to each request made by this object. (defaults to undefined)
14636 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14637 * to each request made by this object. (defaults to undefined)
14640 * @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)
14643 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14646 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14652 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14656 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14657 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14658 * a finer-grained basis than the DataProxy events.
14660 getConnection : function(){
14661 return this.useAjax ? Roo.Ajax : this.conn;
14665 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14666 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14667 * process that block using the passed callback.
14668 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14669 * for the request to the remote server.
14670 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14671 * object into a block of Roo.data.Records.
14672 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14673 * The function must be passed <ul>
14674 * <li>The Record block object</li>
14675 * <li>The "arg" argument from the load function</li>
14676 * <li>A boolean success indicator</li>
14678 * @param {Object} scope The scope in which to call the callback
14679 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14681 load : function(params, reader, callback, scope, arg){
14682 if(this.fireEvent("beforeload", this, params) !== false){
14684 params : params || {},
14686 callback : callback,
14691 callback : this.loadResponse,
14695 Roo.applyIf(o, this.conn);
14696 if(this.activeRequest){
14697 Roo.Ajax.abort(this.activeRequest);
14699 this.activeRequest = Roo.Ajax.request(o);
14701 this.conn.request(o);
14704 callback.call(scope||this, null, arg, false);
14709 loadResponse : function(o, success, response){
14710 delete this.activeRequest;
14712 this.fireEvent("loadexception", this, o, response);
14713 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14718 result = o.reader.read(response);
14720 this.fireEvent("loadexception", this, o, response, e);
14721 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14725 this.fireEvent("load", this, o, o.request.arg);
14726 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14730 update : function(dataSet){
14735 updateResponse : function(dataSet){
14740 * Ext JS Library 1.1.1
14741 * Copyright(c) 2006-2007, Ext JS, LLC.
14743 * Originally Released Under LGPL - original licence link has changed is not relivant.
14746 * <script type="text/javascript">
14750 * @class Roo.data.ScriptTagProxy
14751 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14752 * other than the originating domain of the running page.<br><br>
14754 * <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
14755 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14757 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14758 * source code that is used as the source inside a <script> tag.<br><br>
14760 * In order for the browser to process the returned data, the server must wrap the data object
14761 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14762 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14763 * depending on whether the callback name was passed:
14766 boolean scriptTag = false;
14767 String cb = request.getParameter("callback");
14770 response.setContentType("text/javascript");
14772 response.setContentType("application/x-json");
14774 Writer out = response.getWriter();
14776 out.write(cb + "(");
14778 out.print(dataBlock.toJsonString());
14785 * @param {Object} config A configuration object.
14787 Roo.data.ScriptTagProxy = function(config){
14788 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14789 Roo.apply(this, config);
14790 this.head = document.getElementsByTagName("head")[0];
14793 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14795 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14797 * @cfg {String} url The URL from which to request the data object.
14800 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14804 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14805 * the server the name of the callback function set up by the load call to process the returned data object.
14806 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14807 * javascript output which calls this named function passing the data object as its only parameter.
14809 callbackParam : "callback",
14811 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14812 * name to the request.
14817 * Load data from the configured URL, read the data object into
14818 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14819 * process that block using the passed callback.
14820 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14821 * for the request to the remote server.
14822 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14823 * object into a block of Roo.data.Records.
14824 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14825 * The function must be passed <ul>
14826 * <li>The Record block object</li>
14827 * <li>The "arg" argument from the load function</li>
14828 * <li>A boolean success indicator</li>
14830 * @param {Object} scope The scope in which to call the callback
14831 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14833 load : function(params, reader, callback, scope, arg){
14834 if(this.fireEvent("beforeload", this, params) !== false){
14836 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14838 var url = this.url;
14839 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14841 url += "&_dc=" + (new Date().getTime());
14843 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14846 cb : "stcCallback"+transId,
14847 scriptId : "stcScript"+transId,
14851 callback : callback,
14857 window[trans.cb] = function(o){
14858 conn.handleResponse(o, trans);
14861 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14863 if(this.autoAbort !== false){
14867 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14869 var script = document.createElement("script");
14870 script.setAttribute("src", url);
14871 script.setAttribute("type", "text/javascript");
14872 script.setAttribute("id", trans.scriptId);
14873 this.head.appendChild(script);
14875 this.trans = trans;
14877 callback.call(scope||this, null, arg, false);
14882 isLoading : function(){
14883 return this.trans ? true : false;
14887 * Abort the current server request.
14889 abort : function(){
14890 if(this.isLoading()){
14891 this.destroyTrans(this.trans);
14896 destroyTrans : function(trans, isLoaded){
14897 this.head.removeChild(document.getElementById(trans.scriptId));
14898 clearTimeout(trans.timeoutId);
14900 window[trans.cb] = undefined;
14902 delete window[trans.cb];
14905 // if hasn't been loaded, wait for load to remove it to prevent script error
14906 window[trans.cb] = function(){
14907 window[trans.cb] = undefined;
14909 delete window[trans.cb];
14916 handleResponse : function(o, trans){
14917 this.trans = false;
14918 this.destroyTrans(trans, true);
14921 result = trans.reader.readRecords(o);
14923 this.fireEvent("loadexception", this, o, trans.arg, e);
14924 trans.callback.call(trans.scope||window, null, trans.arg, false);
14927 this.fireEvent("load", this, o, trans.arg);
14928 trans.callback.call(trans.scope||window, result, trans.arg, true);
14932 handleFailure : function(trans){
14933 this.trans = false;
14934 this.destroyTrans(trans, false);
14935 this.fireEvent("loadexception", this, null, trans.arg);
14936 trans.callback.call(trans.scope||window, null, trans.arg, false);
14940 * Ext JS Library 1.1.1
14941 * Copyright(c) 2006-2007, Ext JS, LLC.
14943 * Originally Released Under LGPL - original licence link has changed is not relivant.
14946 * <script type="text/javascript">
14950 * @class Roo.data.JsonReader
14951 * @extends Roo.data.DataReader
14952 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14953 * based on mappings in a provided Roo.data.Record constructor.
14955 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14956 * in the reply previously.
14961 var RecordDef = Roo.data.Record.create([
14962 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14963 {name: 'occupation'} // This field will use "occupation" as the mapping.
14965 var myReader = new Roo.data.JsonReader({
14966 totalProperty: "results", // The property which contains the total dataset size (optional)
14967 root: "rows", // The property which contains an Array of row objects
14968 id: "id" // The property within each row object that provides an ID for the record (optional)
14972 * This would consume a JSON file like this:
14974 { 'results': 2, 'rows': [
14975 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14976 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14979 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14980 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14981 * paged from the remote server.
14982 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14983 * @cfg {String} root name of the property which contains the Array of row objects.
14984 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14985 * @cfg {Array} fields Array of field definition objects
14987 * Create a new JsonReader
14988 * @param {Object} meta Metadata configuration options
14989 * @param {Object} recordType Either an Array of field definition objects,
14990 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14992 Roo.data.JsonReader = function(meta, recordType){
14995 // set some defaults:
14996 Roo.applyIf(meta, {
14997 totalProperty: 'total',
14998 successProperty : 'success',
15003 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15005 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15007 readerType : 'Json',
15010 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15011 * Used by Store query builder to append _requestMeta to params.
15014 metaFromRemote : false,
15016 * This method is only used by a DataProxy which has retrieved data from a remote server.
15017 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15018 * @return {Object} data A data block which is used by an Roo.data.Store object as
15019 * a cache of Roo.data.Records.
15021 read : function(response){
15022 var json = response.responseText;
15024 var o = /* eval:var:o */ eval("("+json+")");
15026 throw {message: "JsonReader.read: Json object not found"};
15032 this.metaFromRemote = true;
15033 this.meta = o.metaData;
15034 this.recordType = Roo.data.Record.create(o.metaData.fields);
15035 this.onMetaChange(this.meta, this.recordType, o);
15037 return this.readRecords(o);
15040 // private function a store will implement
15041 onMetaChange : function(meta, recordType, o){
15048 simpleAccess: function(obj, subsc) {
15055 getJsonAccessor: function(){
15057 return function(expr) {
15059 return(re.test(expr))
15060 ? new Function("obj", "return obj." + expr)
15065 return Roo.emptyFn;
15070 * Create a data block containing Roo.data.Records from an XML document.
15071 * @param {Object} o An object which contains an Array of row objects in the property specified
15072 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15073 * which contains the total size of the dataset.
15074 * @return {Object} data A data block which is used by an Roo.data.Store object as
15075 * a cache of Roo.data.Records.
15077 readRecords : function(o){
15079 * After any data loads, the raw JSON data is available for further custom processing.
15083 var s = this.meta, Record = this.recordType,
15084 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15086 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15088 if(s.totalProperty) {
15089 this.getTotal = this.getJsonAccessor(s.totalProperty);
15091 if(s.successProperty) {
15092 this.getSuccess = this.getJsonAccessor(s.successProperty);
15094 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15096 var g = this.getJsonAccessor(s.id);
15097 this.getId = function(rec) {
15099 return (r === undefined || r === "") ? null : r;
15102 this.getId = function(){return null;};
15105 for(var jj = 0; jj < fl; jj++){
15107 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15108 this.ef[jj] = this.getJsonAccessor(map);
15112 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15113 if(s.totalProperty){
15114 var vt = parseInt(this.getTotal(o), 10);
15119 if(s.successProperty){
15120 var vs = this.getSuccess(o);
15121 if(vs === false || vs === 'false'){
15126 for(var i = 0; i < c; i++){
15129 var id = this.getId(n);
15130 for(var j = 0; j < fl; j++){
15132 var v = this.ef[j](n);
15134 Roo.log('missing convert for ' + f.name);
15138 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15140 var record = new Record(values, id);
15142 records[i] = record;
15148 totalRecords : totalRecords
15151 // used when loading children.. @see loadDataFromChildren
15152 toLoadData: function(rec)
15154 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15155 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15156 return { data : data, total : data.length };
15161 * Ext JS Library 1.1.1
15162 * Copyright(c) 2006-2007, Ext JS, LLC.
15164 * Originally Released Under LGPL - original licence link has changed is not relivant.
15167 * <script type="text/javascript">
15171 * @class Roo.data.ArrayReader
15172 * @extends Roo.data.DataReader
15173 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15174 * Each element of that Array represents a row of data fields. The
15175 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15176 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15180 var RecordDef = Roo.data.Record.create([
15181 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15182 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15184 var myReader = new Roo.data.ArrayReader({
15185 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15189 * This would consume an Array like this:
15191 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15195 * Create a new JsonReader
15196 * @param {Object} meta Metadata configuration options.
15197 * @param {Object|Array} recordType Either an Array of field definition objects
15199 * @cfg {Array} fields Array of field definition objects
15200 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15201 * as specified to {@link Roo.data.Record#create},
15202 * or an {@link Roo.data.Record} object
15205 * created using {@link Roo.data.Record#create}.
15207 Roo.data.ArrayReader = function(meta, recordType)
15209 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15212 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15215 * Create a data block containing Roo.data.Records from an XML document.
15216 * @param {Object} o An Array of row objects which represents the dataset.
15217 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15218 * a cache of Roo.data.Records.
15220 readRecords : function(o)
15222 var sid = this.meta ? this.meta.id : null;
15223 var recordType = this.recordType, fields = recordType.prototype.fields;
15226 for(var i = 0; i < root.length; i++){
15229 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15230 for(var j = 0, jlen = fields.length; j < jlen; j++){
15231 var f = fields.items[j];
15232 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15233 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15235 values[f.name] = v;
15237 var record = new recordType(values, id);
15239 records[records.length] = record;
15243 totalRecords : records.length
15246 // used when loading children.. @see loadDataFromChildren
15247 toLoadData: function(rec)
15249 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15250 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15261 * @class Roo.bootstrap.ComboBox
15262 * @extends Roo.bootstrap.TriggerField
15263 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15264 * @cfg {Boolean} append (true|false) default false
15265 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15266 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15267 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15268 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15269 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15270 * @cfg {Boolean} animate default true
15271 * @cfg {Boolean} emptyResultText only for touch device
15272 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15273 * @cfg {String} emptyTitle default ''
15274 * @cfg {Number} width fixed with? experimental
15276 * Create a new ComboBox.
15277 * @param {Object} config Configuration options
15279 Roo.bootstrap.ComboBox = function(config){
15280 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15284 * Fires when the dropdown list is expanded
15285 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * Fires when the dropdown list is collapsed
15291 * @param {Roo.bootstrap.ComboBox} combo This combo box
15295 * @event beforeselect
15296 * Fires before a list item is selected. Return false to cancel the selection.
15297 * @param {Roo.bootstrap.ComboBox} combo This combo box
15298 * @param {Roo.data.Record} record The data record returned from the underlying store
15299 * @param {Number} index The index of the selected item in the dropdown list
15301 'beforeselect' : true,
15304 * Fires when a list item is selected
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15307 * @param {Number} index The index of the selected item in the dropdown list
15311 * @event beforequery
15312 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15313 * The event object passed has these properties:
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @param {String} query The query
15316 * @param {Boolean} forceAll true to force "all" query
15317 * @param {Boolean} cancel true to cancel the query
15318 * @param {Object} e The query event object
15320 'beforequery': true,
15323 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15324 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15330 * @param {Roo.bootstrap.ComboBox} combo This combo box
15331 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15336 * Fires when the remove value from the combobox array
15337 * @param {Roo.bootstrap.ComboBox} combo This combo box
15341 * @event afterremove
15342 * Fires when the remove value from the combobox array
15343 * @param {Roo.bootstrap.ComboBox} combo This combo box
15345 'afterremove' : true,
15347 * @event specialfilter
15348 * Fires when specialfilter
15349 * @param {Roo.bootstrap.ComboBox} combo This combo box
15351 'specialfilter' : true,
15354 * Fires when tick the element
15355 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @event touchviewdisplay
15360 * Fires when touch view require special display (default is using displayField)
15361 * @param {Roo.bootstrap.ComboBox} combo This combo box
15362 * @param {Object} cfg set html .
15364 'touchviewdisplay' : true
15369 this.tickItems = [];
15371 this.selectedIndex = -1;
15372 if(this.mode == 'local'){
15373 if(config.queryDelay === undefined){
15374 this.queryDelay = 10;
15376 if(config.minChars === undefined){
15382 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15385 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15386 * rendering into an Roo.Editor, defaults to false)
15389 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15390 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15393 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15396 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15397 * the dropdown list (defaults to undefined, with no header element)
15401 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15405 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15407 listWidth: undefined,
15409 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15410 * mode = 'remote' or 'text' if mode = 'local')
15412 displayField: undefined,
15415 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15416 * mode = 'remote' or 'value' if mode = 'local').
15417 * Note: use of a valueField requires the user make a selection
15418 * in order for a value to be mapped.
15420 valueField: undefined,
15422 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15427 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15428 * field's data value (defaults to the underlying DOM element's name)
15430 hiddenName: undefined,
15432 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15436 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15438 selectedClass: 'active',
15441 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15445 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15446 * anchor positions (defaults to 'tl-bl')
15448 listAlign: 'tl-bl?',
15450 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15454 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15455 * query specified by the allQuery config option (defaults to 'query')
15457 triggerAction: 'query',
15459 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15460 * (defaults to 4, does not apply if editable = false)
15464 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15465 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15469 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15470 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15474 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15475 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15479 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15480 * when editable = true (defaults to false)
15482 selectOnFocus:false,
15484 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15486 queryParam: 'query',
15488 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15489 * when mode = 'remote' (defaults to 'Loading...')
15491 loadingText: 'Loading...',
15493 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15497 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15501 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15502 * traditional select (defaults to true)
15506 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15510 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15514 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15515 * listWidth has a higher value)
15519 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15520 * allow the user to set arbitrary text into the field (defaults to false)
15522 forceSelection:false,
15524 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15525 * if typeAhead = true (defaults to 250)
15527 typeAheadDelay : 250,
15529 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15530 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15532 valueNotFoundText : undefined,
15534 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15536 blockFocus : false,
15539 * @cfg {Boolean} disableClear Disable showing of clear button.
15541 disableClear : false,
15543 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15545 alwaysQuery : false,
15548 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15553 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15555 invalidClass : "has-warning",
15558 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15560 validClass : "has-success",
15563 * @cfg {Boolean} specialFilter (true|false) special filter default false
15565 specialFilter : false,
15568 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15570 mobileTouchView : true,
15573 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15575 useNativeIOS : false,
15578 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15580 mobile_restrict_height : false,
15582 ios_options : false,
15594 btnPosition : 'right',
15595 triggerList : true,
15596 showToggleBtn : true,
15598 emptyResultText: 'Empty',
15599 triggerText : 'Select',
15603 // element that contains real text value.. (when hidden is used..)
15605 getAutoCreate : function()
15610 * Render classic select for iso
15613 if(Roo.isIOS && this.useNativeIOS){
15614 cfg = this.getAutoCreateNativeIOS();
15622 if(Roo.isTouch && this.mobileTouchView){
15623 cfg = this.getAutoCreateTouchView();
15630 if(!this.tickable){
15631 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15636 * ComboBox with tickable selections
15639 var align = this.labelAlign || this.parentLabelAlign();
15642 cls : 'form-group roo-combobox-tickable' //input-group
15645 var btn_text_select = '';
15646 var btn_text_done = '';
15647 var btn_text_cancel = '';
15649 if (this.btn_text_show) {
15650 btn_text_select = 'Select';
15651 btn_text_done = 'Done';
15652 btn_text_cancel = 'Cancel';
15657 cls : 'tickable-buttons',
15662 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15663 //html : this.triggerText
15664 html: btn_text_select
15670 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15672 html: btn_text_done
15678 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15680 html: btn_text_cancel
15686 buttons.cn.unshift({
15688 cls: 'roo-select2-search-field-input'
15694 Roo.each(buttons.cn, function(c){
15696 c.cls += ' btn-' + _this.size;
15699 if (_this.disabled) {
15706 style : 'display: contents',
15711 cls: 'form-hidden-field'
15715 cls: 'roo-select2-choices',
15719 cls: 'roo-select2-search-field',
15730 cls: 'roo-select2-container input-group roo-select2-container-multi',
15736 // cls: 'typeahead typeahead-long dropdown-menu',
15737 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15742 if(this.hasFeedback && !this.allowBlank){
15746 cls: 'glyphicon form-control-feedback'
15749 combobox.cn.push(feedback);
15756 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15757 tooltip : 'This field is required'
15759 if (Roo.bootstrap.version == 4) {
15762 style : 'display:none'
15765 if (align ==='left' && this.fieldLabel.length) {
15767 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15774 cls : 'control-label col-form-label',
15775 html : this.fieldLabel
15787 var labelCfg = cfg.cn[1];
15788 var contentCfg = cfg.cn[2];
15791 if(this.indicatorpos == 'right'){
15797 cls : 'control-label col-form-label',
15801 html : this.fieldLabel
15817 labelCfg = cfg.cn[0];
15818 contentCfg = cfg.cn[1];
15822 if(this.labelWidth > 12){
15823 labelCfg.style = "width: " + this.labelWidth + 'px';
15825 if(this.width * 1 > 0){
15826 contentCfg.style = "width: " + this.width + 'px';
15828 if(this.labelWidth < 13 && this.labelmd == 0){
15829 this.labelmd = this.labelWidth;
15832 if(this.labellg > 0){
15833 labelCfg.cls += ' col-lg-' + this.labellg;
15834 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15837 if(this.labelmd > 0){
15838 labelCfg.cls += ' col-md-' + this.labelmd;
15839 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15842 if(this.labelsm > 0){
15843 labelCfg.cls += ' col-sm-' + this.labelsm;
15844 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15847 if(this.labelxs > 0){
15848 labelCfg.cls += ' col-xs-' + this.labelxs;
15849 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15853 } else if ( this.fieldLabel.length) {
15854 // Roo.log(" label");
15859 //cls : 'input-group-addon',
15860 html : this.fieldLabel
15865 if(this.indicatorpos == 'right'){
15869 //cls : 'input-group-addon',
15870 html : this.fieldLabel
15880 // Roo.log(" no label && no align");
15887 ['xs','sm','md','lg'].map(function(size){
15888 if (settings[size]) {
15889 cfg.cls += ' col-' + size + '-' + settings[size];
15897 _initEventsCalled : false,
15900 initEvents: function()
15902 if (this._initEventsCalled) { // as we call render... prevent looping...
15905 this._initEventsCalled = true;
15908 throw "can not find store for combo";
15911 this.indicator = this.indicatorEl();
15913 this.store = Roo.factory(this.store, Roo.data);
15914 this.store.parent = this;
15916 // if we are building from html. then this element is so complex, that we can not really
15917 // use the rendered HTML.
15918 // so we have to trash and replace the previous code.
15919 if (Roo.XComponent.build_from_html) {
15920 // remove this element....
15921 var e = this.el.dom, k=0;
15922 while (e ) { e = e.previousSibling; ++k;}
15927 this.rendered = false;
15929 this.render(this.parent().getChildContainer(true), k);
15932 if(Roo.isIOS && this.useNativeIOS){
15933 this.initIOSView();
15941 if(Roo.isTouch && this.mobileTouchView){
15942 this.initTouchView();
15947 this.initTickableEvents();
15951 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15953 if(this.hiddenName){
15955 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15957 this.hiddenField.dom.value =
15958 this.hiddenValue !== undefined ? this.hiddenValue :
15959 this.value !== undefined ? this.value : '';
15961 // prevent input submission
15962 this.el.dom.removeAttribute('name');
15963 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15968 // this.el.dom.setAttribute('autocomplete', 'off');
15971 var cls = 'x-combo-list';
15973 //this.list = new Roo.Layer({
15974 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15980 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15981 _this.list.setWidth(lw);
15984 this.list.on('mouseover', this.onViewOver, this);
15985 this.list.on('mousemove', this.onViewMove, this);
15986 this.list.on('scroll', this.onViewScroll, this);
15989 this.list.swallowEvent('mousewheel');
15990 this.assetHeight = 0;
15993 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15994 this.assetHeight += this.header.getHeight();
15997 this.innerList = this.list.createChild({cls:cls+'-inner'});
15998 this.innerList.on('mouseover', this.onViewOver, this);
15999 this.innerList.on('mousemove', this.onViewMove, this);
16000 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16002 if(this.allowBlank && !this.pageSize && !this.disableClear){
16003 this.footer = this.list.createChild({cls:cls+'-ft'});
16004 this.pageTb = new Roo.Toolbar(this.footer);
16008 this.footer = this.list.createChild({cls:cls+'-ft'});
16009 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16010 {pageSize: this.pageSize});
16014 if (this.pageTb && this.allowBlank && !this.disableClear) {
16016 this.pageTb.add(new Roo.Toolbar.Fill(), {
16017 cls: 'x-btn-icon x-btn-clear',
16019 handler: function()
16022 _this.clearValue();
16023 _this.onSelect(false, -1);
16028 this.assetHeight += this.footer.getHeight();
16033 this.tpl = Roo.bootstrap.version == 4 ?
16034 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16035 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16038 this.view = new Roo.View(this.list, this.tpl, {
16039 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16041 //this.view.wrapEl.setDisplayed(false);
16042 this.view.on('click', this.onViewClick, this);
16045 this.store.on('beforeload', this.onBeforeLoad, this);
16046 this.store.on('load', this.onLoad, this);
16047 this.store.on('loadexception', this.onLoadException, this);
16049 if(this.resizable){
16050 this.resizer = new Roo.Resizable(this.list, {
16051 pinned:true, handles:'se'
16053 this.resizer.on('resize', function(r, w, h){
16054 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16055 this.listWidth = w;
16056 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16057 this.restrictHeight();
16059 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16062 if(!this.editable){
16063 this.editable = true;
16064 this.setEditable(false);
16069 if (typeof(this.events.add.listeners) != 'undefined') {
16071 this.addicon = this.wrap.createChild(
16072 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16074 this.addicon.on('click', function(e) {
16075 this.fireEvent('add', this);
16078 if (typeof(this.events.edit.listeners) != 'undefined') {
16080 this.editicon = this.wrap.createChild(
16081 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16082 if (this.addicon) {
16083 this.editicon.setStyle('margin-left', '40px');
16085 this.editicon.on('click', function(e) {
16087 // we fire even if inothing is selected..
16088 this.fireEvent('edit', this, this.lastData );
16094 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16095 "up" : function(e){
16096 this.inKeyMode = true;
16100 "down" : function(e){
16101 if(!this.isExpanded()){
16102 this.onTriggerClick();
16104 this.inKeyMode = true;
16109 "enter" : function(e){
16110 // this.onViewClick();
16114 if(this.fireEvent("specialkey", this, e)){
16115 this.onViewClick(false);
16121 "esc" : function(e){
16125 "tab" : function(e){
16128 if(this.fireEvent("specialkey", this, e)){
16129 this.onViewClick(false);
16137 doRelay : function(foo, bar, hname){
16138 if(hname == 'down' || this.scope.isExpanded()){
16139 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16148 this.queryDelay = Math.max(this.queryDelay || 10,
16149 this.mode == 'local' ? 10 : 250);
16152 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16154 if(this.typeAhead){
16155 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16157 if(this.editable !== false){
16158 this.inputEl().on("keyup", this.onKeyUp, this);
16160 if(this.forceSelection){
16161 this.inputEl().on('blur', this.doForce, this);
16165 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16166 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16170 initTickableEvents: function()
16174 if(this.hiddenName){
16176 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16178 this.hiddenField.dom.value =
16179 this.hiddenValue !== undefined ? this.hiddenValue :
16180 this.value !== undefined ? this.value : '';
16182 // prevent input submission
16183 this.el.dom.removeAttribute('name');
16184 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16189 // this.list = this.el.select('ul.dropdown-menu',true).first();
16191 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16192 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16193 if(this.triggerList){
16194 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16197 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16198 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16200 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16201 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16203 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16204 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16206 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16207 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16211 this.cancelBtn.hide();
16216 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16217 _this.list.setWidth(lw);
16220 this.list.on('mouseover', this.onViewOver, this);
16221 this.list.on('mousemove', this.onViewMove, this);
16223 this.list.on('scroll', this.onViewScroll, this);
16226 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16227 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16230 this.view = new Roo.View(this.list, this.tpl, {
16235 selectedClass: this.selectedClass
16238 //this.view.wrapEl.setDisplayed(false);
16239 this.view.on('click', this.onViewClick, this);
16243 this.store.on('beforeload', this.onBeforeLoad, this);
16244 this.store.on('load', this.onLoad, this);
16245 this.store.on('loadexception', this.onLoadException, this);
16248 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16249 "up" : function(e){
16250 this.inKeyMode = true;
16254 "down" : function(e){
16255 this.inKeyMode = true;
16259 "enter" : function(e){
16260 if(this.fireEvent("specialkey", this, e)){
16261 this.onViewClick(false);
16267 "esc" : function(e){
16268 this.onTickableFooterButtonClick(e, false, false);
16271 "tab" : function(e){
16272 this.fireEvent("specialkey", this, e);
16274 this.onTickableFooterButtonClick(e, false, false);
16281 doRelay : function(e, fn, key){
16282 if(this.scope.isExpanded()){
16283 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16292 this.queryDelay = Math.max(this.queryDelay || 10,
16293 this.mode == 'local' ? 10 : 250);
16296 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16298 if(this.typeAhead){
16299 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16302 if(this.editable !== false){
16303 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16306 this.indicator = this.indicatorEl();
16308 if(this.indicator){
16309 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16310 this.indicator.hide();
16315 onDestroy : function(){
16317 this.view.setStore(null);
16318 this.view.el.removeAllListeners();
16319 this.view.el.remove();
16320 this.view.purgeListeners();
16323 this.list.dom.innerHTML = '';
16327 this.store.un('beforeload', this.onBeforeLoad, this);
16328 this.store.un('load', this.onLoad, this);
16329 this.store.un('loadexception', this.onLoadException, this);
16331 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16335 fireKey : function(e){
16336 if(e.isNavKeyPress() && !this.list.isVisible()){
16337 this.fireEvent("specialkey", this, e);
16342 onResize: function(w, h)
16346 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16348 // if(typeof w != 'number'){
16349 // // we do not handle it!?!?
16352 // var tw = this.trigger.getWidth();
16353 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16354 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16356 // this.inputEl().setWidth( this.adjustWidth('input', x));
16358 // //this.trigger.setStyle('left', x+'px');
16360 // if(this.list && this.listWidth === undefined){
16361 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16362 // this.list.setWidth(lw);
16363 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16371 * Allow or prevent the user from directly editing the field text. If false is passed,
16372 * the user will only be able to select from the items defined in the dropdown list. This method
16373 * is the runtime equivalent of setting the 'editable' config option at config time.
16374 * @param {Boolean} value True to allow the user to directly edit the field text
16376 setEditable : function(value){
16377 if(value == this.editable){
16380 this.editable = value;
16382 this.inputEl().dom.setAttribute('readOnly', true);
16383 this.inputEl().on('mousedown', this.onTriggerClick, this);
16384 this.inputEl().addClass('x-combo-noedit');
16386 this.inputEl().dom.removeAttribute('readOnly');
16387 this.inputEl().un('mousedown', this.onTriggerClick, this);
16388 this.inputEl().removeClass('x-combo-noedit');
16394 onBeforeLoad : function(combo,opts){
16395 if(!this.hasFocus){
16399 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16401 this.restrictHeight();
16402 this.selectedIndex = -1;
16406 onLoad : function(){
16408 this.hasQuery = false;
16410 if(!this.hasFocus){
16414 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16415 this.loading.hide();
16418 if(this.store.getCount() > 0){
16421 this.restrictHeight();
16422 if(this.lastQuery == this.allQuery){
16423 if(this.editable && !this.tickable){
16424 this.inputEl().dom.select();
16428 !this.selectByValue(this.value, true) &&
16431 !this.store.lastOptions ||
16432 typeof(this.store.lastOptions.add) == 'undefined' ||
16433 this.store.lastOptions.add != true
16436 this.select(0, true);
16439 if(this.autoFocus){
16442 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16443 this.taTask.delay(this.typeAheadDelay);
16447 this.onEmptyResults();
16453 onLoadException : function()
16455 this.hasQuery = false;
16457 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16458 this.loading.hide();
16461 if(this.tickable && this.editable){
16466 // only causes errors at present
16467 //Roo.log(this.store.reader.jsonData);
16468 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16470 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16476 onTypeAhead : function(){
16477 if(this.store.getCount() > 0){
16478 var r = this.store.getAt(0);
16479 var newValue = r.data[this.displayField];
16480 var len = newValue.length;
16481 var selStart = this.getRawValue().length;
16483 if(selStart != len){
16484 this.setRawValue(newValue);
16485 this.selectText(selStart, newValue.length);
16491 onSelect : function(record, index){
16493 if(this.fireEvent('beforeselect', this, record, index) !== false){
16495 this.setFromData(index > -1 ? record.data : false);
16498 this.fireEvent('select', this, record, index);
16503 * Returns the currently selected field value or empty string if no value is set.
16504 * @return {String} value The selected value
16506 getValue : function()
16508 if(Roo.isIOS && this.useNativeIOS){
16509 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16513 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16516 if(this.valueField){
16517 return typeof this.value != 'undefined' ? this.value : '';
16519 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16523 getRawValue : function()
16525 if(Roo.isIOS && this.useNativeIOS){
16526 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16529 var v = this.inputEl().getValue();
16535 * Clears any text/value currently set in the field
16537 clearValue : function(){
16539 if(this.hiddenField){
16540 this.hiddenField.dom.value = '';
16543 this.setRawValue('');
16544 this.lastSelectionText = '';
16545 this.lastData = false;
16547 var close = this.closeTriggerEl();
16558 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16559 * will be displayed in the field. If the value does not match the data value of an existing item,
16560 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16561 * Otherwise the field will be blank (although the value will still be set).
16562 * @param {String} value The value to match
16564 setValue : function(v)
16566 if(Roo.isIOS && this.useNativeIOS){
16567 this.setIOSValue(v);
16577 if(this.valueField){
16578 var r = this.findRecord(this.valueField, v);
16580 text = r.data[this.displayField];
16581 }else if(this.valueNotFoundText !== undefined){
16582 text = this.valueNotFoundText;
16585 this.lastSelectionText = text;
16586 if(this.hiddenField){
16587 this.hiddenField.dom.value = v;
16589 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16592 var close = this.closeTriggerEl();
16595 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16601 * @property {Object} the last set data for the element
16606 * Sets the value of the field based on a object which is related to the record format for the store.
16607 * @param {Object} value the value to set as. or false on reset?
16609 setFromData : function(o){
16616 var dv = ''; // display value
16617 var vv = ''; // value value..
16619 if (this.displayField) {
16620 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16622 // this is an error condition!!!
16623 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16626 if(this.valueField){
16627 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16630 var close = this.closeTriggerEl();
16633 if(dv.length || vv * 1 > 0){
16635 this.blockFocus=true;
16641 if(this.hiddenField){
16642 this.hiddenField.dom.value = vv;
16644 this.lastSelectionText = dv;
16645 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16649 // no hidden field.. - we store the value in 'value', but still display
16650 // display field!!!!
16651 this.lastSelectionText = dv;
16652 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16659 reset : function(){
16660 // overridden so that last data is reset..
16667 this.setValue(this.originalValue);
16668 //this.clearInvalid();
16669 this.lastData = false;
16671 this.view.clearSelections();
16677 findRecord : function(prop, value){
16679 if(this.store.getCount() > 0){
16680 this.store.each(function(r){
16681 if(r.data[prop] == value){
16691 getName: function()
16693 // returns hidden if it's set..
16694 if (!this.rendered) {return ''};
16695 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16699 onViewMove : function(e, t){
16700 this.inKeyMode = false;
16704 onViewOver : function(e, t){
16705 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16708 var item = this.view.findItemFromChild(t);
16711 var index = this.view.indexOf(item);
16712 this.select(index, false);
16717 onViewClick : function(view, doFocus, el, e)
16719 var index = this.view.getSelectedIndexes()[0];
16721 var r = this.store.getAt(index);
16725 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16732 Roo.each(this.tickItems, function(v,k){
16734 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16736 _this.tickItems.splice(k, 1);
16738 if(typeof(e) == 'undefined' && view == false){
16739 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16751 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16752 this.tickItems.push(r.data);
16755 if(typeof(e) == 'undefined' && view == false){
16756 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16763 this.onSelect(r, index);
16765 if(doFocus !== false && !this.blockFocus){
16766 this.inputEl().focus();
16771 restrictHeight : function(){
16772 //this.innerList.dom.style.height = '';
16773 //var inner = this.innerList.dom;
16774 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16775 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16776 //this.list.beginUpdate();
16777 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16778 this.list.alignTo(this.inputEl(), this.listAlign);
16779 this.list.alignTo(this.inputEl(), this.listAlign);
16780 //this.list.endUpdate();
16784 onEmptyResults : function(){
16786 if(this.tickable && this.editable){
16787 this.hasFocus = false;
16788 this.restrictHeight();
16796 * Returns true if the dropdown list is expanded, else false.
16798 isExpanded : function(){
16799 return this.list.isVisible();
16803 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16804 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16805 * @param {String} value The data value of the item to select
16806 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16807 * selected item if it is not currently in view (defaults to true)
16808 * @return {Boolean} True if the value matched an item in the list, else false
16810 selectByValue : function(v, scrollIntoView){
16811 if(v !== undefined && v !== null){
16812 var r = this.findRecord(this.valueField || this.displayField, v);
16814 this.select(this.store.indexOf(r), scrollIntoView);
16822 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16823 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824 * @param {Number} index The zero-based index of the list item to select
16825 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826 * selected item if it is not currently in view (defaults to true)
16828 select : function(index, scrollIntoView){
16829 this.selectedIndex = index;
16830 this.view.select(index);
16831 if(scrollIntoView !== false){
16832 var el = this.view.getNode(index);
16834 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16837 this.list.scrollChildIntoView(el, false);
16843 selectNext : function(){
16844 var ct = this.store.getCount();
16846 if(this.selectedIndex == -1){
16848 }else if(this.selectedIndex < ct-1){
16849 this.select(this.selectedIndex+1);
16855 selectPrev : function(){
16856 var ct = this.store.getCount();
16858 if(this.selectedIndex == -1){
16860 }else if(this.selectedIndex != 0){
16861 this.select(this.selectedIndex-1);
16867 onKeyUp : function(e){
16868 if(this.editable !== false && !e.isSpecialKey()){
16869 this.lastKey = e.getKey();
16870 this.dqTask.delay(this.queryDelay);
16875 validateBlur : function(){
16876 return !this.list || !this.list.isVisible();
16880 initQuery : function(){
16882 var v = this.getRawValue();
16884 if(this.tickable && this.editable){
16885 v = this.tickableInputEl().getValue();
16892 doForce : function(){
16893 if(this.inputEl().dom.value.length > 0){
16894 this.inputEl().dom.value =
16895 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16901 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16902 * query allowing the query action to be canceled if needed.
16903 * @param {String} query The SQL query to execute
16904 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16905 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16906 * saved in the current store (defaults to false)
16908 doQuery : function(q, forceAll){
16910 if(q === undefined || q === null){
16915 forceAll: forceAll,
16919 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16924 forceAll = qe.forceAll;
16925 if(forceAll === true || (q.length >= this.minChars)){
16927 this.hasQuery = true;
16929 if(this.lastQuery != q || this.alwaysQuery){
16930 this.lastQuery = q;
16931 if(this.mode == 'local'){
16932 this.selectedIndex = -1;
16934 this.store.clearFilter();
16937 if(this.specialFilter){
16938 this.fireEvent('specialfilter', this);
16943 this.store.filter(this.displayField, q);
16946 this.store.fireEvent("datachanged", this.store);
16953 this.store.baseParams[this.queryParam] = q;
16955 var options = {params : this.getParams(q)};
16958 options.add = true;
16959 options.params.start = this.page * this.pageSize;
16962 this.store.load(options);
16965 * this code will make the page width larger, at the beginning, the list not align correctly,
16966 * we should expand the list on onLoad
16967 * so command out it
16972 this.selectedIndex = -1;
16977 this.loadNext = false;
16981 getParams : function(q){
16983 //p[this.queryParam] = q;
16987 p.limit = this.pageSize;
16993 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16995 collapse : function(){
16996 if(!this.isExpanded()){
17002 this.hasFocus = false;
17006 this.cancelBtn.hide();
17007 this.trigger.show();
17010 this.tickableInputEl().dom.value = '';
17011 this.tickableInputEl().blur();
17016 Roo.get(document).un('mousedown', this.collapseIf, this);
17017 Roo.get(document).un('mousewheel', this.collapseIf, this);
17018 if (!this.editable) {
17019 Roo.get(document).un('keydown', this.listKeyPress, this);
17021 this.fireEvent('collapse', this);
17027 collapseIf : function(e){
17028 var in_combo = e.within(this.el);
17029 var in_list = e.within(this.list);
17030 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17032 if (in_combo || in_list || is_list) {
17033 //e.stopPropagation();
17038 this.onTickableFooterButtonClick(e, false, false);
17046 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17048 expand : function(){
17050 if(this.isExpanded() || !this.hasFocus){
17054 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17055 this.list.setWidth(lw);
17061 this.restrictHeight();
17065 this.tickItems = Roo.apply([], this.item);
17068 this.cancelBtn.show();
17069 this.trigger.hide();
17072 this.tickableInputEl().focus();
17077 Roo.get(document).on('mousedown', this.collapseIf, this);
17078 Roo.get(document).on('mousewheel', this.collapseIf, this);
17079 if (!this.editable) {
17080 Roo.get(document).on('keydown', this.listKeyPress, this);
17083 this.fireEvent('expand', this);
17087 // Implements the default empty TriggerField.onTriggerClick function
17088 onTriggerClick : function(e)
17090 Roo.log('trigger click');
17092 if(this.disabled || !this.triggerList){
17097 this.loadNext = false;
17099 if(this.isExpanded()){
17101 if (!this.blockFocus) {
17102 this.inputEl().focus();
17106 this.hasFocus = true;
17107 if(this.triggerAction == 'all') {
17108 this.doQuery(this.allQuery, true);
17110 this.doQuery(this.getRawValue());
17112 if (!this.blockFocus) {
17113 this.inputEl().focus();
17118 onTickableTriggerClick : function(e)
17125 this.loadNext = false;
17126 this.hasFocus = true;
17128 if(this.triggerAction == 'all') {
17129 this.doQuery(this.allQuery, true);
17131 this.doQuery(this.getRawValue());
17135 onSearchFieldClick : function(e)
17137 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17138 this.onTickableFooterButtonClick(e, false, false);
17142 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17147 this.loadNext = false;
17148 this.hasFocus = true;
17150 if(this.triggerAction == 'all') {
17151 this.doQuery(this.allQuery, true);
17153 this.doQuery(this.getRawValue());
17157 listKeyPress : function(e)
17159 //Roo.log('listkeypress');
17160 // scroll to first matching element based on key pres..
17161 if (e.isSpecialKey()) {
17164 var k = String.fromCharCode(e.getKey()).toUpperCase();
17167 var csel = this.view.getSelectedNodes();
17168 var cselitem = false;
17170 var ix = this.view.indexOf(csel[0]);
17171 cselitem = this.store.getAt(ix);
17172 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17178 this.store.each(function(v) {
17180 // start at existing selection.
17181 if (cselitem.id == v.id) {
17187 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17188 match = this.store.indexOf(v);
17194 if (match === false) {
17195 return true; // no more action?
17198 this.view.select(match);
17199 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17200 sn.scrollIntoView(sn.dom.parentNode, false);
17203 onViewScroll : function(e, t){
17205 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){
17209 this.hasQuery = true;
17211 this.loading = this.list.select('.loading', true).first();
17213 if(this.loading === null){
17214 this.list.createChild({
17216 cls: 'loading roo-select2-more-results roo-select2-active',
17217 html: 'Loading more results...'
17220 this.loading = this.list.select('.loading', true).first();
17222 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17224 this.loading.hide();
17227 this.loading.show();
17232 this.loadNext = true;
17234 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17239 addItem : function(o)
17241 var dv = ''; // display value
17243 if (this.displayField) {
17244 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17246 // this is an error condition!!!
17247 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17254 var choice = this.choices.createChild({
17256 cls: 'roo-select2-search-choice',
17265 cls: 'roo-select2-search-choice-close fa fa-times',
17270 }, this.searchField);
17272 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17274 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17282 this.inputEl().dom.value = '';
17287 onRemoveItem : function(e, _self, o)
17289 e.preventDefault();
17291 this.lastItem = Roo.apply([], this.item);
17293 var index = this.item.indexOf(o.data) * 1;
17296 Roo.log('not this item?!');
17300 this.item.splice(index, 1);
17305 this.fireEvent('remove', this, e);
17311 syncValue : function()
17313 if(!this.item.length){
17320 Roo.each(this.item, function(i){
17321 if(_this.valueField){
17322 value.push(i[_this.valueField]);
17329 this.value = value.join(',');
17331 if(this.hiddenField){
17332 this.hiddenField.dom.value = this.value;
17335 this.store.fireEvent("datachanged", this.store);
17340 clearItem : function()
17342 if(!this.multiple){
17348 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17356 if(this.tickable && !Roo.isTouch){
17357 this.view.refresh();
17361 inputEl: function ()
17363 if(Roo.isIOS && this.useNativeIOS){
17364 return this.el.select('select.roo-ios-select', true).first();
17367 if(Roo.isTouch && this.mobileTouchView){
17368 return this.el.select('input.form-control',true).first();
17372 return this.searchField;
17375 return this.el.select('input.form-control',true).first();
17378 onTickableFooterButtonClick : function(e, btn, el)
17380 e.preventDefault();
17382 this.lastItem = Roo.apply([], this.item);
17384 if(btn && btn.name == 'cancel'){
17385 this.tickItems = Roo.apply([], this.item);
17394 Roo.each(this.tickItems, function(o){
17402 validate : function()
17404 if(this.getVisibilityEl().hasClass('hidden')){
17408 var v = this.getRawValue();
17411 v = this.getValue();
17414 if(this.disabled || this.allowBlank || v.length){
17419 this.markInvalid();
17423 tickableInputEl : function()
17425 if(!this.tickable || !this.editable){
17426 return this.inputEl();
17429 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17433 getAutoCreateTouchView : function()
17438 cls: 'form-group' //input-group
17444 type : this.inputType,
17445 cls : 'form-control x-combo-noedit',
17446 autocomplete: 'new-password',
17447 placeholder : this.placeholder || '',
17452 input.name = this.name;
17456 input.cls += ' input-' + this.size;
17459 if (this.disabled) {
17460 input.disabled = true;
17464 cls : 'roo-combobox-wrap',
17471 inputblock.cls += ' input-group';
17473 inputblock.cn.unshift({
17475 cls : 'input-group-addon input-group-prepend input-group-text',
17480 if(this.removable && !this.multiple){
17481 inputblock.cls += ' roo-removable';
17483 inputblock.cn.push({
17486 cls : 'roo-combo-removable-btn close'
17490 if(this.hasFeedback && !this.allowBlank){
17492 inputblock.cls += ' has-feedback';
17494 inputblock.cn.push({
17496 cls: 'glyphicon form-control-feedback'
17503 inputblock.cls += (this.before) ? '' : ' input-group';
17505 inputblock.cn.push({
17507 cls : 'input-group-addon input-group-append input-group-text',
17513 var ibwrap = inputblock;
17518 cls: 'roo-select2-choices',
17522 cls: 'roo-select2-search-field',
17535 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17540 cls: 'form-hidden-field'
17546 if(!this.multiple && this.showToggleBtn){
17552 if (this.caret != false) {
17555 cls: 'fa fa-' + this.caret
17562 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17564 Roo.bootstrap.version == 3 ? caret : '',
17567 cls: 'combobox-clear',
17581 combobox.cls += ' roo-select2-container-multi';
17584 var required = this.allowBlank ? {
17586 style: 'display: none'
17589 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17590 tooltip : 'This field is required'
17593 var align = this.labelAlign || this.parentLabelAlign();
17595 if (align ==='left' && this.fieldLabel.length) {
17601 cls : 'control-label col-form-label',
17602 html : this.fieldLabel
17606 cls : 'roo-combobox-wrap ',
17613 var labelCfg = cfg.cn[1];
17614 var contentCfg = cfg.cn[2];
17617 if(this.indicatorpos == 'right'){
17622 cls : 'control-label col-form-label',
17626 html : this.fieldLabel
17632 cls : "roo-combobox-wrap ",
17640 labelCfg = cfg.cn[0];
17641 contentCfg = cfg.cn[1];
17646 if(this.labelWidth > 12){
17647 labelCfg.style = "width: " + this.labelWidth + 'px';
17650 if(this.labelWidth < 13 && this.labelmd == 0){
17651 this.labelmd = this.labelWidth;
17654 if(this.labellg > 0){
17655 labelCfg.cls += ' col-lg-' + this.labellg;
17656 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17659 if(this.labelmd > 0){
17660 labelCfg.cls += ' col-md-' + this.labelmd;
17661 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17664 if(this.labelsm > 0){
17665 labelCfg.cls += ' col-sm-' + this.labelsm;
17666 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17669 if(this.labelxs > 0){
17670 labelCfg.cls += ' col-xs-' + this.labelxs;
17671 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17675 } else if ( this.fieldLabel.length) {
17680 cls : 'control-label',
17681 html : this.fieldLabel
17692 if(this.indicatorpos == 'right'){
17696 cls : 'control-label',
17697 html : this.fieldLabel,
17715 var settings = this;
17717 ['xs','sm','md','lg'].map(function(size){
17718 if (settings[size]) {
17719 cfg.cls += ' col-' + size + '-' + settings[size];
17726 initTouchView : function()
17728 this.renderTouchView();
17730 this.touchViewEl.on('scroll', function(){
17731 this.el.dom.scrollTop = 0;
17734 this.originalValue = this.getValue();
17736 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17738 this.inputEl().on("click", this.showTouchView, this);
17739 if (this.triggerEl) {
17740 this.triggerEl.on("click", this.showTouchView, this);
17744 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17745 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17747 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17749 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17750 this.store.on('load', this.onTouchViewLoad, this);
17751 this.store.on('loadexception', this.onTouchViewLoadException, this);
17753 if(this.hiddenName){
17755 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17757 this.hiddenField.dom.value =
17758 this.hiddenValue !== undefined ? this.hiddenValue :
17759 this.value !== undefined ? this.value : '';
17761 this.el.dom.removeAttribute('name');
17762 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17766 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17767 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17770 if(this.removable && !this.multiple){
17771 var close = this.closeTriggerEl();
17773 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17774 close.on('click', this.removeBtnClick, this, close);
17778 * fix the bug in Safari iOS8
17780 this.inputEl().on("focus", function(e){
17781 document.activeElement.blur();
17784 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17791 renderTouchView : function()
17793 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17794 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17796 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17797 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17800 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17801 this.touchViewBodyEl.setStyle('overflow', 'auto');
17803 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17804 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17806 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17807 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17811 showTouchView : function()
17817 this.touchViewHeaderEl.hide();
17819 if(this.modalTitle.length){
17820 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17821 this.touchViewHeaderEl.show();
17824 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17825 this.touchViewEl.show();
17827 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17829 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17830 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17832 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17834 if(this.modalTitle.length){
17835 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17838 this.touchViewBodyEl.setHeight(bodyHeight);
17842 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17844 this.touchViewEl.addClass(['in','show']);
17847 if(this._touchViewMask){
17848 Roo.get(document.body).addClass("x-body-masked");
17849 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17850 this._touchViewMask.setStyle('z-index', 10000);
17851 this._touchViewMask.addClass('show');
17854 this.doTouchViewQuery();
17858 hideTouchView : function()
17860 this.touchViewEl.removeClass(['in','show']);
17864 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17866 this.touchViewEl.setStyle('display', 'none');
17869 if(this._touchViewMask){
17870 this._touchViewMask.removeClass('show');
17871 Roo.get(document.body).removeClass("x-body-masked");
17875 setTouchViewValue : function()
17882 Roo.each(this.tickItems, function(o){
17887 this.hideTouchView();
17890 doTouchViewQuery : function()
17899 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17903 if(!this.alwaysQuery || this.mode == 'local'){
17904 this.onTouchViewLoad();
17911 onTouchViewBeforeLoad : function(combo,opts)
17917 onTouchViewLoad : function()
17919 if(this.store.getCount() < 1){
17920 this.onTouchViewEmptyResults();
17924 this.clearTouchView();
17926 var rawValue = this.getRawValue();
17928 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17930 this.tickItems = [];
17932 this.store.data.each(function(d, rowIndex){
17933 var row = this.touchViewListGroup.createChild(template);
17935 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17936 row.addClass(d.data.cls);
17939 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17942 html : d.data[this.displayField]
17945 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17946 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17949 row.removeClass('selected');
17950 if(!this.multiple && this.valueField &&
17951 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955 row.addClass('selected');
17958 if(this.multiple && this.valueField &&
17959 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17963 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17964 this.tickItems.push(d.data);
17967 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17971 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17973 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17975 if(this.modalTitle.length){
17976 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17979 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17981 if(this.mobile_restrict_height && listHeight < bodyHeight){
17982 this.touchViewBodyEl.setHeight(listHeight);
17987 if(firstChecked && listHeight > bodyHeight){
17988 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17993 onTouchViewLoadException : function()
17995 this.hideTouchView();
17998 onTouchViewEmptyResults : function()
18000 this.clearTouchView();
18002 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18004 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18008 clearTouchView : function()
18010 this.touchViewListGroup.dom.innerHTML = '';
18013 onTouchViewClick : function(e, el, o)
18015 e.preventDefault();
18018 var rowIndex = o.rowIndex;
18020 var r = this.store.getAt(rowIndex);
18022 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18024 if(!this.multiple){
18025 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18026 c.dom.removeAttribute('checked');
18029 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18031 this.setFromData(r.data);
18033 var close = this.closeTriggerEl();
18039 this.hideTouchView();
18041 this.fireEvent('select', this, r, rowIndex);
18046 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18047 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18048 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18052 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18053 this.addItem(r.data);
18054 this.tickItems.push(r.data);
18058 getAutoCreateNativeIOS : function()
18061 cls: 'form-group' //input-group,
18066 cls : 'roo-ios-select'
18070 combobox.name = this.name;
18073 if (this.disabled) {
18074 combobox.disabled = true;
18077 var settings = this;
18079 ['xs','sm','md','lg'].map(function(size){
18080 if (settings[size]) {
18081 cfg.cls += ' col-' + size + '-' + settings[size];
18091 initIOSView : function()
18093 this.store.on('load', this.onIOSViewLoad, this);
18098 onIOSViewLoad : function()
18100 if(this.store.getCount() < 1){
18104 this.clearIOSView();
18106 if(this.allowBlank) {
18108 var default_text = '-- SELECT --';
18110 if(this.placeholder.length){
18111 default_text = this.placeholder;
18114 if(this.emptyTitle.length){
18115 default_text += ' - ' + this.emptyTitle + ' -';
18118 var opt = this.inputEl().createChild({
18121 html : default_text
18125 o[this.valueField] = 0;
18126 o[this.displayField] = default_text;
18128 this.ios_options.push({
18135 this.store.data.each(function(d, rowIndex){
18139 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18140 html = d.data[this.displayField];
18145 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18146 value = d.data[this.valueField];
18155 if(this.value == d.data[this.valueField]){
18156 option['selected'] = true;
18159 var opt = this.inputEl().createChild(option);
18161 this.ios_options.push({
18168 this.inputEl().on('change', function(){
18169 this.fireEvent('select', this);
18174 clearIOSView: function()
18176 this.inputEl().dom.innerHTML = '';
18178 this.ios_options = [];
18181 setIOSValue: function(v)
18185 if(!this.ios_options){
18189 Roo.each(this.ios_options, function(opts){
18191 opts.el.dom.removeAttribute('selected');
18193 if(opts.data[this.valueField] != v){
18197 opts.el.dom.setAttribute('selected', true);
18203 * @cfg {Boolean} grow
18207 * @cfg {Number} growMin
18211 * @cfg {Number} growMax
18220 Roo.apply(Roo.bootstrap.ComboBox, {
18224 cls: 'modal-header',
18246 cls: 'list-group-item',
18250 cls: 'roo-combobox-list-group-item-value'
18254 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18268 listItemCheckbox : {
18270 cls: 'list-group-item',
18274 cls: 'roo-combobox-list-group-item-value'
18278 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18294 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18299 cls: 'modal-footer',
18307 cls: 'col-xs-6 text-left',
18310 cls: 'btn btn-danger roo-touch-view-cancel',
18316 cls: 'col-xs-6 text-right',
18319 cls: 'btn btn-success roo-touch-view-ok',
18330 Roo.apply(Roo.bootstrap.ComboBox, {
18332 touchViewTemplate : {
18334 cls: 'modal fade roo-combobox-touch-view',
18338 cls: 'modal-dialog',
18339 style : 'position:fixed', // we have to fix position....
18343 cls: 'modal-content',
18345 Roo.bootstrap.ComboBox.header,
18346 Roo.bootstrap.ComboBox.body,
18347 Roo.bootstrap.ComboBox.footer
18356 * Ext JS Library 1.1.1
18357 * Copyright(c) 2006-2007, Ext JS, LLC.
18359 * Originally Released Under LGPL - original licence link has changed is not relivant.
18362 * <script type="text/javascript">
18367 * @extends Roo.util.Observable
18368 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18369 * This class also supports single and multi selection modes. <br>
18370 * Create a data model bound view:
18372 var store = new Roo.data.Store(...);
18374 var view = new Roo.View({
18376 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18378 singleSelect: true,
18379 selectedClass: "ydataview-selected",
18383 // listen for node click?
18384 view.on("click", function(vw, index, node, e){
18385 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18389 dataModel.load("foobar.xml");
18391 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18393 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18394 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18396 * Note: old style constructor is still suported (container, template, config)
18399 * Create a new View
18400 * @param {Object} config The config object
18403 Roo.View = function(config, depreciated_tpl, depreciated_config){
18405 this.parent = false;
18407 if (typeof(depreciated_tpl) == 'undefined') {
18408 // new way.. - universal constructor.
18409 Roo.apply(this, config);
18410 this.el = Roo.get(this.el);
18413 this.el = Roo.get(config);
18414 this.tpl = depreciated_tpl;
18415 Roo.apply(this, depreciated_config);
18417 this.wrapEl = this.el.wrap().wrap();
18418 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18421 if(typeof(this.tpl) == "string"){
18422 this.tpl = new Roo.Template(this.tpl);
18424 // support xtype ctors..
18425 this.tpl = new Roo.factory(this.tpl, Roo);
18429 this.tpl.compile();
18434 * @event beforeclick
18435 * Fires before a click is processed. Returns false to cancel the default action.
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
18441 "beforeclick" : true,
18444 * Fires when a template node is 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
18453 * Fires when a template node is double 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
18461 * @event contextmenu
18462 * Fires when a template node is right clicked.
18463 * @param {Roo.View} this
18464 * @param {Number} index The index of the target node
18465 * @param {HTMLElement} node The target node
18466 * @param {Roo.EventObject} e The raw event object
18468 "contextmenu" : true,
18470 * @event selectionchange
18471 * Fires when the selected nodes change.
18472 * @param {Roo.View} this
18473 * @param {Array} selections Array of the selected nodes
18475 "selectionchange" : true,
18478 * @event beforeselect
18479 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18480 * @param {Roo.View} this
18481 * @param {HTMLElement} node The node to be selected
18482 * @param {Array} selections Array of currently selected nodes
18484 "beforeselect" : true,
18486 * @event preparedata
18487 * Fires on every row to render, to allow you to change the data.
18488 * @param {Roo.View} this
18489 * @param {Object} data to be rendered (change this)
18491 "preparedata" : true
18499 "click": this.onClick,
18500 "dblclick": this.onDblClick,
18501 "contextmenu": this.onContextMenu,
18505 this.selections = [];
18507 this.cmp = new Roo.CompositeElementLite([]);
18509 this.store = Roo.factory(this.store, Roo.data);
18510 this.setStore(this.store, true);
18513 if ( this.footer && this.footer.xtype) {
18515 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18517 this.footer.dataSource = this.store;
18518 this.footer.container = fctr;
18519 this.footer = Roo.factory(this.footer, Roo);
18520 fctr.insertFirst(this.el);
18522 // this is a bit insane - as the paging toolbar seems to detach the el..
18523 // dom.parentNode.parentNode.parentNode
18524 // they get detached?
18528 Roo.View.superclass.constructor.call(this);
18533 Roo.extend(Roo.View, Roo.util.Observable, {
18536 * @cfg {Roo.data.Store} store Data store to load data from.
18541 * @cfg {String|Roo.Element} el The container element.
18546 * @cfg {String|Roo.Template} tpl The template used by this View
18550 * @cfg {String} dataName the named area of the template to use as the data area
18551 * Works with domtemplates roo-name="name"
18555 * @cfg {String} selectedClass The css class to add to selected nodes
18557 selectedClass : "x-view-selected",
18559 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18564 * @cfg {String} text to display on mask (default Loading)
18568 * @cfg {Boolean} multiSelect Allow multiple selection
18570 multiSelect : false,
18572 * @cfg {Boolean} singleSelect Allow single selection
18574 singleSelect: false,
18577 * @cfg {Boolean} toggleSelect - selecting
18579 toggleSelect : false,
18582 * @cfg {Boolean} tickable - selecting
18587 * Returns the element this view is bound to.
18588 * @return {Roo.Element}
18590 getEl : function(){
18591 return this.wrapEl;
18597 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18599 refresh : function(){
18600 //Roo.log('refresh');
18603 // if we are using something like 'domtemplate', then
18604 // the what gets used is:
18605 // t.applySubtemplate(NAME, data, wrapping data..)
18606 // the outer template then get' applied with
18607 // the store 'extra data'
18608 // and the body get's added to the
18609 // roo-name="data" node?
18610 // <span class='roo-tpl-{name}'></span> ?????
18614 this.clearSelections();
18615 this.el.update("");
18617 var records = this.store.getRange();
18618 if(records.length < 1) {
18620 // is this valid?? = should it render a template??
18622 this.el.update(this.emptyText);
18626 if (this.dataName) {
18627 this.el.update(t.apply(this.store.meta)); //????
18628 el = this.el.child('.roo-tpl-' + this.dataName);
18631 for(var i = 0, len = records.length; i < len; i++){
18632 var data = this.prepareData(records[i].data, i, records[i]);
18633 this.fireEvent("preparedata", this, data, i, records[i]);
18635 var d = Roo.apply({}, data);
18638 Roo.apply(d, {'roo-id' : Roo.id()});
18642 Roo.each(this.parent.item, function(item){
18643 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18646 Roo.apply(d, {'roo-data-checked' : 'checked'});
18650 html[html.length] = Roo.util.Format.trim(
18652 t.applySubtemplate(this.dataName, d, this.store.meta) :
18659 el.update(html.join(""));
18660 this.nodes = el.dom.childNodes;
18661 this.updateIndexes(0);
18666 * Function to override to reformat the data that is sent to
18667 * the template for each node.
18668 * DEPRICATED - use the preparedata event handler.
18669 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18670 * a JSON object for an UpdateManager bound view).
18672 prepareData : function(data, index, record)
18674 this.fireEvent("preparedata", this, data, index, record);
18678 onUpdate : function(ds, record){
18679 // Roo.log('on update');
18680 this.clearSelections();
18681 var index = this.store.indexOf(record);
18682 var n = this.nodes[index];
18683 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18684 n.parentNode.removeChild(n);
18685 this.updateIndexes(index, index);
18691 onAdd : function(ds, records, index)
18693 //Roo.log(['on Add', ds, records, index] );
18694 this.clearSelections();
18695 if(this.nodes.length == 0){
18699 var n = this.nodes[index];
18700 for(var i = 0, len = records.length; i < len; i++){
18701 var d = this.prepareData(records[i].data, i, records[i]);
18703 this.tpl.insertBefore(n, d);
18706 this.tpl.append(this.el, d);
18709 this.updateIndexes(index);
18712 onRemove : function(ds, record, index){
18713 // Roo.log('onRemove');
18714 this.clearSelections();
18715 var el = this.dataName ?
18716 this.el.child('.roo-tpl-' + this.dataName) :
18719 el.dom.removeChild(this.nodes[index]);
18720 this.updateIndexes(index);
18724 * Refresh an individual node.
18725 * @param {Number} index
18727 refreshNode : function(index){
18728 this.onUpdate(this.store, this.store.getAt(index));
18731 updateIndexes : function(startIndex, endIndex){
18732 var ns = this.nodes;
18733 startIndex = startIndex || 0;
18734 endIndex = endIndex || ns.length - 1;
18735 for(var i = startIndex; i <= endIndex; i++){
18736 ns[i].nodeIndex = i;
18741 * Changes the data store this view uses and refresh the view.
18742 * @param {Store} store
18744 setStore : function(store, initial){
18745 if(!initial && this.store){
18746 this.store.un("datachanged", this.refresh);
18747 this.store.un("add", this.onAdd);
18748 this.store.un("remove", this.onRemove);
18749 this.store.un("update", this.onUpdate);
18750 this.store.un("clear", this.refresh);
18751 this.store.un("beforeload", this.onBeforeLoad);
18752 this.store.un("load", this.onLoad);
18753 this.store.un("loadexception", this.onLoad);
18757 store.on("datachanged", this.refresh, this);
18758 store.on("add", this.onAdd, this);
18759 store.on("remove", this.onRemove, this);
18760 store.on("update", this.onUpdate, this);
18761 store.on("clear", this.refresh, this);
18762 store.on("beforeload", this.onBeforeLoad, this);
18763 store.on("load", this.onLoad, this);
18764 store.on("loadexception", this.onLoad, this);
18772 * onbeforeLoad - masks the loading area.
18775 onBeforeLoad : function(store,opts)
18777 //Roo.log('onBeforeLoad');
18779 this.el.update("");
18781 this.el.mask(this.mask ? this.mask : "Loading" );
18783 onLoad : function ()
18790 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18791 * @param {HTMLElement} node
18792 * @return {HTMLElement} The template node
18794 findItemFromChild : function(node){
18795 var el = this.dataName ?
18796 this.el.child('.roo-tpl-' + this.dataName,true) :
18799 if(!node || node.parentNode == el){
18802 var p = node.parentNode;
18803 while(p && p != el){
18804 if(p.parentNode == el){
18813 onClick : function(e){
18814 var item = this.findItemFromChild(e.getTarget());
18816 var index = this.indexOf(item);
18817 if(this.onItemClick(item, index, e) !== false){
18818 this.fireEvent("click", this, index, item, e);
18821 this.clearSelections();
18826 onContextMenu : function(e){
18827 var item = this.findItemFromChild(e.getTarget());
18829 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18834 onDblClick : function(e){
18835 var item = this.findItemFromChild(e.getTarget());
18837 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18841 onItemClick : function(item, index, e)
18843 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18846 if (this.toggleSelect) {
18847 var m = this.isSelected(item) ? 'unselect' : 'select';
18850 _t[m](item, true, false);
18853 if(this.multiSelect || this.singleSelect){
18854 if(this.multiSelect && e.shiftKey && this.lastSelection){
18855 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18857 this.select(item, this.multiSelect && e.ctrlKey);
18858 this.lastSelection = item;
18861 if(!this.tickable){
18862 e.preventDefault();
18870 * Get the number of selected nodes.
18873 getSelectionCount : function(){
18874 return this.selections.length;
18878 * Get the currently selected nodes.
18879 * @return {Array} An array of HTMLElements
18881 getSelectedNodes : function(){
18882 return this.selections;
18886 * Get the indexes of the selected nodes.
18889 getSelectedIndexes : function(){
18890 var indexes = [], s = this.selections;
18891 for(var i = 0, len = s.length; i < len; i++){
18892 indexes.push(s[i].nodeIndex);
18898 * Clear all selections
18899 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18901 clearSelections : function(suppressEvent){
18902 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18903 this.cmp.elements = this.selections;
18904 this.cmp.removeClass(this.selectedClass);
18905 this.selections = [];
18906 if(!suppressEvent){
18907 this.fireEvent("selectionchange", this, this.selections);
18913 * Returns true if the passed node is selected
18914 * @param {HTMLElement/Number} node The node or node index
18915 * @return {Boolean}
18917 isSelected : function(node){
18918 var s = this.selections;
18922 node = this.getNode(node);
18923 return s.indexOf(node) !== -1;
18928 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18929 * @param {Boolean} keepExisting (optional) true to keep existing selections
18930 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18932 select : function(nodeInfo, keepExisting, suppressEvent){
18933 if(nodeInfo instanceof Array){
18935 this.clearSelections(true);
18937 for(var i = 0, len = nodeInfo.length; i < len; i++){
18938 this.select(nodeInfo[i], true, true);
18942 var node = this.getNode(nodeInfo);
18943 if(!node || this.isSelected(node)){
18944 return; // already selected.
18947 this.clearSelections(true);
18950 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18951 Roo.fly(node).addClass(this.selectedClass);
18952 this.selections.push(node);
18953 if(!suppressEvent){
18954 this.fireEvent("selectionchange", this, this.selections);
18962 * @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
18963 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18964 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18966 unselect : function(nodeInfo, keepExisting, suppressEvent)
18968 if(nodeInfo instanceof Array){
18969 Roo.each(this.selections, function(s) {
18970 this.unselect(s, nodeInfo);
18974 var node = this.getNode(nodeInfo);
18975 if(!node || !this.isSelected(node)){
18976 //Roo.log("not selected");
18977 return; // not selected.
18981 Roo.each(this.selections, function(s) {
18983 Roo.fly(node).removeClass(this.selectedClass);
18990 this.selections= ns;
18991 this.fireEvent("selectionchange", this, this.selections);
18995 * Gets a template node.
18996 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18997 * @return {HTMLElement} The node or null if it wasn't found
18999 getNode : function(nodeInfo){
19000 if(typeof nodeInfo == "string"){
19001 return document.getElementById(nodeInfo);
19002 }else if(typeof nodeInfo == "number"){
19003 return this.nodes[nodeInfo];
19009 * Gets a range template nodes.
19010 * @param {Number} startIndex
19011 * @param {Number} endIndex
19012 * @return {Array} An array of nodes
19014 getNodes : function(start, end){
19015 var ns = this.nodes;
19016 start = start || 0;
19017 end = typeof end == "undefined" ? ns.length - 1 : end;
19020 for(var i = start; i <= end; i++){
19024 for(var i = start; i >= end; i--){
19032 * Finds the index of the passed node
19033 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19034 * @return {Number} The index of the node or -1
19036 indexOf : function(node){
19037 node = this.getNode(node);
19038 if(typeof node.nodeIndex == "number"){
19039 return node.nodeIndex;
19041 var ns = this.nodes;
19042 for(var i = 0, len = ns.length; i < len; i++){
19053 * based on jquery fullcalendar
19057 Roo.bootstrap = Roo.bootstrap || {};
19059 * @class Roo.bootstrap.Calendar
19060 * @extends Roo.bootstrap.Component
19061 * Bootstrap Calendar class
19062 * @cfg {Boolean} loadMask (true|false) default false
19063 * @cfg {Object} header generate the user specific header of the calendar, default false
19066 * Create a new Container
19067 * @param {Object} config The config object
19072 Roo.bootstrap.Calendar = function(config){
19073 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19077 * Fires when a date is selected
19078 * @param {DatePicker} this
19079 * @param {Date} date The selected date
19083 * @event monthchange
19084 * Fires when the displayed month changes
19085 * @param {DatePicker} this
19086 * @param {Date} date The selected month
19088 'monthchange': true,
19090 * @event evententer
19091 * Fires when mouse over an event
19092 * @param {Calendar} this
19093 * @param {event} Event
19095 'evententer': true,
19097 * @event eventleave
19098 * Fires when the mouse leaves an
19099 * @param {Calendar} this
19102 'eventleave': true,
19104 * @event eventclick
19105 * Fires when the mouse click an
19106 * @param {Calendar} this
19115 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19118 * @cfg {Number} startDay
19119 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19127 getAutoCreate : function(){
19130 var fc_button = function(name, corner, style, content ) {
19131 return Roo.apply({},{
19133 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19135 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19138 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19149 style : 'width:100%',
19156 cls : 'fc-header-left',
19158 fc_button('prev', 'left', 'arrow', '‹' ),
19159 fc_button('next', 'right', 'arrow', '›' ),
19160 { tag: 'span', cls: 'fc-header-space' },
19161 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19169 cls : 'fc-header-center',
19173 cls: 'fc-header-title',
19176 html : 'month / year'
19184 cls : 'fc-header-right',
19186 /* fc_button('month', 'left', '', 'month' ),
19187 fc_button('week', '', '', 'week' ),
19188 fc_button('day', 'right', '', 'day' )
19200 header = this.header;
19203 var cal_heads = function() {
19205 // fixme - handle this.
19207 for (var i =0; i < Date.dayNames.length; i++) {
19208 var d = Date.dayNames[i];
19211 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19212 html : d.substring(0,3)
19216 ret[0].cls += ' fc-first';
19217 ret[6].cls += ' fc-last';
19220 var cal_cell = function(n) {
19223 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19228 cls: 'fc-day-number',
19232 cls: 'fc-day-content',
19236 style: 'position: relative;' // height: 17px;
19248 var cal_rows = function() {
19251 for (var r = 0; r < 6; r++) {
19258 for (var i =0; i < Date.dayNames.length; i++) {
19259 var d = Date.dayNames[i];
19260 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19263 row.cn[0].cls+=' fc-first';
19264 row.cn[0].cn[0].style = 'min-height:90px';
19265 row.cn[6].cls+=' fc-last';
19269 ret[0].cls += ' fc-first';
19270 ret[4].cls += ' fc-prev-last';
19271 ret[5].cls += ' fc-last';
19278 cls: 'fc-border-separate',
19279 style : 'width:100%',
19287 cls : 'fc-first fc-last',
19305 cls : 'fc-content',
19306 style : "position: relative;",
19309 cls : 'fc-view fc-view-month fc-grid',
19310 style : 'position: relative',
19311 unselectable : 'on',
19314 cls : 'fc-event-container',
19315 style : 'position:absolute;z-index:8;top:0;left:0;'
19333 initEvents : function()
19336 throw "can not find store for calendar";
19342 style: "text-align:center",
19346 style: "background-color:white;width:50%;margin:250 auto",
19350 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19361 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19363 var size = this.el.select('.fc-content', true).first().getSize();
19364 this.maskEl.setSize(size.width, size.height);
19365 this.maskEl.enableDisplayMode("block");
19366 if(!this.loadMask){
19367 this.maskEl.hide();
19370 this.store = Roo.factory(this.store, Roo.data);
19371 this.store.on('load', this.onLoad, this);
19372 this.store.on('beforeload', this.onBeforeLoad, this);
19376 this.cells = this.el.select('.fc-day',true);
19377 //Roo.log(this.cells);
19378 this.textNodes = this.el.query('.fc-day-number');
19379 this.cells.addClassOnOver('fc-state-hover');
19381 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19382 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19383 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19384 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19386 this.on('monthchange', this.onMonthChange, this);
19388 this.update(new Date().clearTime());
19391 resize : function() {
19392 var sz = this.el.getSize();
19394 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19395 this.el.select('.fc-day-content div',true).setHeight(34);
19400 showPrevMonth : function(e){
19401 this.update(this.activeDate.add("mo", -1));
19403 showToday : function(e){
19404 this.update(new Date().clearTime());
19407 showNextMonth : function(e){
19408 this.update(this.activeDate.add("mo", 1));
19412 showPrevYear : function(){
19413 this.update(this.activeDate.add("y", -1));
19417 showNextYear : function(){
19418 this.update(this.activeDate.add("y", 1));
19423 update : function(date)
19425 var vd = this.activeDate;
19426 this.activeDate = date;
19427 // if(vd && this.el){
19428 // var t = date.getTime();
19429 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19430 // Roo.log('using add remove');
19432 // this.fireEvent('monthchange', this, date);
19434 // this.cells.removeClass("fc-state-highlight");
19435 // this.cells.each(function(c){
19436 // if(c.dateValue == t){
19437 // c.addClass("fc-state-highlight");
19438 // setTimeout(function(){
19439 // try{c.dom.firstChild.focus();}catch(e){}
19449 var days = date.getDaysInMonth();
19451 var firstOfMonth = date.getFirstDateOfMonth();
19452 var startingPos = firstOfMonth.getDay()-this.startDay;
19454 if(startingPos < this.startDay){
19458 var pm = date.add(Date.MONTH, -1);
19459 var prevStart = pm.getDaysInMonth()-startingPos;
19461 this.cells = this.el.select('.fc-day',true);
19462 this.textNodes = this.el.query('.fc-day-number');
19463 this.cells.addClassOnOver('fc-state-hover');
19465 var cells = this.cells.elements;
19466 var textEls = this.textNodes;
19468 Roo.each(cells, function(cell){
19469 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19472 days += startingPos;
19474 // convert everything to numbers so it's fast
19475 var day = 86400000;
19476 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19479 //Roo.log(prevStart);
19481 var today = new Date().clearTime().getTime();
19482 var sel = date.clearTime().getTime();
19483 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19484 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19485 var ddMatch = this.disabledDatesRE;
19486 var ddText = this.disabledDatesText;
19487 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19488 var ddaysText = this.disabledDaysText;
19489 var format = this.format;
19491 var setCellClass = function(cal, cell){
19495 //Roo.log('set Cell Class');
19497 var t = d.getTime();
19501 cell.dateValue = t;
19503 cell.className += " fc-today";
19504 cell.className += " fc-state-highlight";
19505 cell.title = cal.todayText;
19508 // disable highlight in other month..
19509 //cell.className += " fc-state-highlight";
19514 cell.className = " fc-state-disabled";
19515 cell.title = cal.minText;
19519 cell.className = " fc-state-disabled";
19520 cell.title = cal.maxText;
19524 if(ddays.indexOf(d.getDay()) != -1){
19525 cell.title = ddaysText;
19526 cell.className = " fc-state-disabled";
19529 if(ddMatch && format){
19530 var fvalue = d.dateFormat(format);
19531 if(ddMatch.test(fvalue)){
19532 cell.title = ddText.replace("%0", fvalue);
19533 cell.className = " fc-state-disabled";
19537 if (!cell.initialClassName) {
19538 cell.initialClassName = cell.dom.className;
19541 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19546 for(; i < startingPos; i++) {
19547 textEls[i].innerHTML = (++prevStart);
19548 d.setDate(d.getDate()+1);
19550 cells[i].className = "fc-past fc-other-month";
19551 setCellClass(this, cells[i]);
19556 for(; i < days; i++){
19557 intDay = i - startingPos + 1;
19558 textEls[i].innerHTML = (intDay);
19559 d.setDate(d.getDate()+1);
19561 cells[i].className = ''; // "x-date-active";
19562 setCellClass(this, cells[i]);
19566 for(; i < 42; i++) {
19567 textEls[i].innerHTML = (++extraDays);
19568 d.setDate(d.getDate()+1);
19570 cells[i].className = "fc-future fc-other-month";
19571 setCellClass(this, cells[i]);
19574 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19576 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19578 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19579 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19581 if(totalRows != 6){
19582 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19583 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19586 this.fireEvent('monthchange', this, date);
19590 if(!this.internalRender){
19591 var main = this.el.dom.firstChild;
19592 var w = main.offsetWidth;
19593 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19594 Roo.fly(main).setWidth(w);
19595 this.internalRender = true;
19596 // opera does not respect the auto grow header center column
19597 // then, after it gets a width opera refuses to recalculate
19598 // without a second pass
19599 if(Roo.isOpera && !this.secondPass){
19600 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19601 this.secondPass = true;
19602 this.update.defer(10, this, [date]);
19609 findCell : function(dt) {
19610 dt = dt.clearTime().getTime();
19612 this.cells.each(function(c){
19613 //Roo.log("check " +c.dateValue + '?=' + dt);
19614 if(c.dateValue == dt){
19624 findCells : function(ev) {
19625 var s = ev.start.clone().clearTime().getTime();
19627 var e= ev.end.clone().clearTime().getTime();
19630 this.cells.each(function(c){
19631 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19633 if(c.dateValue > e){
19636 if(c.dateValue < s){
19645 // findBestRow: function(cells)
19649 // for (var i =0 ; i < cells.length;i++) {
19650 // ret = Math.max(cells[i].rows || 0,ret);
19657 addItem : function(ev)
19659 // look for vertical location slot in
19660 var cells = this.findCells(ev);
19662 // ev.row = this.findBestRow(cells);
19664 // work out the location.
19668 for(var i =0; i < cells.length; i++) {
19670 cells[i].row = cells[0].row;
19673 cells[i].row = cells[i].row + 1;
19683 if (crow.start.getY() == cells[i].getY()) {
19685 crow.end = cells[i];
19702 cells[0].events.push(ev);
19704 this.calevents.push(ev);
19707 clearEvents: function() {
19709 if(!this.calevents){
19713 Roo.each(this.cells.elements, function(c){
19719 Roo.each(this.calevents, function(e) {
19720 Roo.each(e.els, function(el) {
19721 el.un('mouseenter' ,this.onEventEnter, this);
19722 el.un('mouseleave' ,this.onEventLeave, this);
19727 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19733 renderEvents: function()
19737 this.cells.each(function(c) {
19746 if(c.row != c.events.length){
19747 r = 4 - (4 - (c.row - c.events.length));
19750 c.events = ev.slice(0, r);
19751 c.more = ev.slice(r);
19753 if(c.more.length && c.more.length == 1){
19754 c.events.push(c.more.pop());
19757 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19761 this.cells.each(function(c) {
19763 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19766 for (var e = 0; e < c.events.length; e++){
19767 var ev = c.events[e];
19768 var rows = ev.rows;
19770 for(var i = 0; i < rows.length; i++) {
19772 // how many rows should it span..
19775 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19776 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19778 unselectable : "on",
19781 cls: 'fc-event-inner',
19785 // cls: 'fc-event-time',
19786 // html : cells.length > 1 ? '' : ev.time
19790 cls: 'fc-event-title',
19791 html : String.format('{0}', ev.title)
19798 cls: 'ui-resizable-handle ui-resizable-e',
19799 html : '  '
19806 cfg.cls += ' fc-event-start';
19808 if ((i+1) == rows.length) {
19809 cfg.cls += ' fc-event-end';
19812 var ctr = _this.el.select('.fc-event-container',true).first();
19813 var cg = ctr.createChild(cfg);
19815 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19816 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19818 var r = (c.more.length) ? 1 : 0;
19819 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19820 cg.setWidth(ebox.right - sbox.x -2);
19822 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19823 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19824 cg.on('click', _this.onEventClick, _this, ev);
19835 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19836 style : 'position: absolute',
19837 unselectable : "on",
19840 cls: 'fc-event-inner',
19844 cls: 'fc-event-title',
19852 cls: 'ui-resizable-handle ui-resizable-e',
19853 html : '  '
19859 var ctr = _this.el.select('.fc-event-container',true).first();
19860 var cg = ctr.createChild(cfg);
19862 var sbox = c.select('.fc-day-content',true).first().getBox();
19863 var ebox = c.select('.fc-day-content',true).first().getBox();
19865 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19866 cg.setWidth(ebox.right - sbox.x -2);
19868 cg.on('click', _this.onMoreEventClick, _this, c.more);
19878 onEventEnter: function (e, el,event,d) {
19879 this.fireEvent('evententer', this, el, event);
19882 onEventLeave: function (e, el,event,d) {
19883 this.fireEvent('eventleave', this, el, event);
19886 onEventClick: function (e, el,event,d) {
19887 this.fireEvent('eventclick', this, el, event);
19890 onMonthChange: function () {
19894 onMoreEventClick: function(e, el, more)
19898 this.calpopover.placement = 'right';
19899 this.calpopover.setTitle('More');
19901 this.calpopover.setContent('');
19903 var ctr = this.calpopover.el.select('.popover-content', true).first();
19905 Roo.each(more, function(m){
19907 cls : 'fc-event-hori fc-event-draggable',
19910 var cg = ctr.createChild(cfg);
19912 cg.on('click', _this.onEventClick, _this, m);
19915 this.calpopover.show(el);
19920 onLoad: function ()
19922 this.calevents = [];
19925 if(this.store.getCount() > 0){
19926 this.store.data.each(function(d){
19929 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19930 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19931 time : d.data.start_time,
19932 title : d.data.title,
19933 description : d.data.description,
19934 venue : d.data.venue
19939 this.renderEvents();
19941 if(this.calevents.length && this.loadMask){
19942 this.maskEl.hide();
19946 onBeforeLoad: function()
19948 this.clearEvents();
19950 this.maskEl.show();
19964 * @class Roo.bootstrap.Popover
19965 * @extends Roo.bootstrap.Component
19966 * Bootstrap Popover class
19967 * @cfg {String} html contents of the popover (or false to use children..)
19968 * @cfg {String} title of popover (or false to hide)
19969 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19970 * @cfg {String} trigger click || hover (or false to trigger manually)
19971 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19972 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19973 * - if false and it has a 'parent' then it will be automatically added to that element
19974 * - if string - Roo.get will be called
19975 * @cfg {Number} delay - delay before showing
19978 * Create a new Popover
19979 * @param {Object} config The config object
19982 Roo.bootstrap.Popover = function(config){
19983 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19989 * After the popover show
19991 * @param {Roo.bootstrap.Popover} this
19996 * After the popover hide
19998 * @param {Roo.bootstrap.Popover} this
20004 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20009 placement : 'right',
20010 trigger : 'hover', // hover
20016 can_build_overlaid : false,
20018 maskEl : false, // the mask element
20021 alignEl : false, // when show is called with an element - this get's stored.
20023 getChildContainer : function()
20025 return this.contentEl;
20028 getPopoverHeader : function()
20030 this.title = true; // flag not to hide it..
20031 this.headerEl.addClass('p-0');
20032 return this.headerEl
20036 getAutoCreate : function(){
20039 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20040 style: 'display:block',
20046 cls : 'popover-inner ',
20050 cls: 'popover-title popover-header',
20051 html : this.title === false ? '' : this.title
20054 cls : 'popover-content popover-body ' + (this.cls || ''),
20055 html : this.html || ''
20066 * @param {string} the title
20068 setTitle: function(str)
20072 this.headerEl.dom.innerHTML = str;
20077 * @param {string} the body content
20079 setContent: function(str)
20082 if (this.contentEl) {
20083 this.contentEl.dom.innerHTML = str;
20087 // as it get's added to the bottom of the page.
20088 onRender : function(ct, position)
20090 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20095 var cfg = Roo.apply({}, this.getAutoCreate());
20099 cfg.cls += ' ' + this.cls;
20102 cfg.style = this.style;
20104 //Roo.log("adding to ");
20105 this.el = Roo.get(document.body).createChild(cfg, position);
20106 // Roo.log(this.el);
20109 this.contentEl = this.el.select('.popover-content',true).first();
20110 this.headerEl = this.el.select('.popover-title',true).first();
20113 if(typeof(this.items) != 'undefined'){
20114 var items = this.items;
20117 for(var i =0;i < items.length;i++) {
20118 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20122 this.items = nitems;
20124 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20125 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20132 resizeMask : function()
20134 this.maskEl.setSize(
20135 Roo.lib.Dom.getViewWidth(true),
20136 Roo.lib.Dom.getViewHeight(true)
20140 initEvents : function()
20144 Roo.bootstrap.Popover.register(this);
20147 this.arrowEl = this.el.select('.arrow',true).first();
20148 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20149 this.el.enableDisplayMode('block');
20153 if (this.over === false && !this.parent()) {
20156 if (this.triggers === false) {
20161 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20162 var triggers = this.trigger ? this.trigger.split(' ') : [];
20163 Roo.each(triggers, function(trigger) {
20165 if (trigger == 'click') {
20166 on_el.on('click', this.toggle, this);
20167 } else if (trigger != 'manual') {
20168 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20169 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20171 on_el.on(eventIn ,this.enter, this);
20172 on_el.on(eventOut, this.leave, this);
20182 toggle : function () {
20183 this.hoverState == 'in' ? this.leave() : this.enter();
20186 enter : function () {
20188 clearTimeout(this.timeout);
20190 this.hoverState = 'in';
20192 if (!this.delay || !this.delay.show) {
20197 this.timeout = setTimeout(function () {
20198 if (_t.hoverState == 'in') {
20201 }, this.delay.show)
20204 leave : function() {
20205 clearTimeout(this.timeout);
20207 this.hoverState = 'out';
20209 if (!this.delay || !this.delay.hide) {
20214 this.timeout = setTimeout(function () {
20215 if (_t.hoverState == 'out') {
20218 }, this.delay.hide)
20222 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20223 * @param {string} (left|right|top|bottom) position
20225 show : function (on_el, placement)
20227 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20228 on_el = on_el || false; // default to false
20231 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20232 on_el = this.parent().el;
20233 } else if (this.over) {
20234 on_el = Roo.get(this.over);
20239 this.alignEl = Roo.get( on_el );
20242 this.render(document.body);
20248 if (this.title === false) {
20249 this.headerEl.hide();
20254 this.el.dom.style.display = 'block';
20257 if (this.alignEl) {
20258 this.updatePosition(this.placement, true);
20261 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20262 var es = this.el.getSize();
20263 var x = Roo.lib.Dom.getViewWidth()/2;
20264 var y = Roo.lib.Dom.getViewHeight()/2;
20265 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20270 //var arrow = this.el.select('.arrow',true).first();
20271 //arrow.set(align[2],
20273 this.el.addClass('in');
20277 this.hoverState = 'in';
20280 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20281 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20282 this.maskEl.dom.style.display = 'block';
20283 this.maskEl.addClass('show');
20285 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20287 this.fireEvent('show', this);
20291 * fire this manually after loading a grid in the table for example
20292 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20293 * @param {Boolean} try and move it if we cant get right position.
20295 updatePosition : function(placement, try_move)
20297 // allow for calling with no parameters
20298 placement = placement ? placement : this.placement;
20299 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20301 this.el.removeClass([
20302 'fade','top','bottom', 'left', 'right','in',
20303 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20305 this.el.addClass(placement + ' bs-popover-' + placement);
20307 if (!this.alignEl ) {
20311 switch (placement) {
20313 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20314 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20315 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20316 //normal display... or moved up/down.
20317 this.el.setXY(offset);
20318 var xy = this.alignEl.getAnchorXY('tr', false);
20320 this.arrowEl.setXY(xy);
20323 // continue through...
20324 return this.updatePosition('left', false);
20328 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20329 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20330 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20331 //normal display... or moved up/down.
20332 this.el.setXY(offset);
20333 var xy = this.alignEl.getAnchorXY('tl', false);
20334 xy[0]-=10;xy[1]+=5; // << fix me
20335 this.arrowEl.setXY(xy);
20339 return this.updatePosition('right', false);
20342 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20343 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20344 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20345 //normal display... or moved up/down.
20346 this.el.setXY(offset);
20347 var xy = this.alignEl.getAnchorXY('t', false);
20348 xy[1]-=10; // << fix me
20349 this.arrowEl.setXY(xy);
20353 return this.updatePosition('bottom', false);
20356 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20357 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20358 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20359 //normal display... or moved up/down.
20360 this.el.setXY(offset);
20361 var xy = this.alignEl.getAnchorXY('b', false);
20362 xy[1]+=2; // << fix me
20363 this.arrowEl.setXY(xy);
20367 return this.updatePosition('top', false);
20378 this.el.setXY([0,0]);
20379 this.el.removeClass('in');
20381 this.hoverState = null;
20382 this.maskEl.hide(); // always..
20383 this.fireEvent('hide', this);
20389 Roo.apply(Roo.bootstrap.Popover, {
20392 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20393 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20394 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20395 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20400 clickHander : false,
20404 onMouseDown : function(e)
20406 if (this.popup.length && !e.getTarget(".roo-popover") && this.popup.length) {
20407 /// what is nothing is showing..
20416 register : function(popup)
20418 if (!Roo.bootstrap.Popover.clickHandler) {
20419 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20421 // hide other popups.
20422 popup.on('show', Roo.bootstrap.Popover.onShow, Roo.bootstrap.Popover, popup);
20423 popup.on('hide', Roo.bootstrap.Popover.onHide, Roo.bootstrap.Popover, popup);
20425 this.popups.push(popup);
20427 hideAll : function()
20429 this.popups.forEach(function(p) {
20433 onShow : function(p) {
20434 this.popups.push(p);
20440 * Card header - holder for the card header elements.
20445 * @class Roo.bootstrap.PopoverNav
20446 * @extends Roo.bootstrap.NavGroup
20447 * Bootstrap Popover header navigation class
20449 * Create a new Popover Header Navigation
20450 * @param {Object} config The config object
20453 Roo.bootstrap.PopoverNav = function(config){
20454 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20457 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20460 container_method : 'getPopoverHeader'
20478 * @class Roo.bootstrap.Progress
20479 * @extends Roo.bootstrap.Component
20480 * Bootstrap Progress class
20481 * @cfg {Boolean} striped striped of the progress bar
20482 * @cfg {Boolean} active animated of the progress bar
20486 * Create a new Progress
20487 * @param {Object} config The config object
20490 Roo.bootstrap.Progress = function(config){
20491 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20494 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20499 getAutoCreate : function(){
20507 cfg.cls += ' progress-striped';
20511 cfg.cls += ' active';
20530 * @class Roo.bootstrap.ProgressBar
20531 * @extends Roo.bootstrap.Component
20532 * Bootstrap ProgressBar class
20533 * @cfg {Number} aria_valuenow aria-value now
20534 * @cfg {Number} aria_valuemin aria-value min
20535 * @cfg {Number} aria_valuemax aria-value max
20536 * @cfg {String} label label for the progress bar
20537 * @cfg {String} panel (success | info | warning | danger )
20538 * @cfg {String} role role of the progress bar
20539 * @cfg {String} sr_only text
20543 * Create a new ProgressBar
20544 * @param {Object} config The config object
20547 Roo.bootstrap.ProgressBar = function(config){
20548 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20551 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20555 aria_valuemax : 100,
20561 getAutoCreate : function()
20566 cls: 'progress-bar',
20567 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20579 cfg.role = this.role;
20582 if(this.aria_valuenow){
20583 cfg['aria-valuenow'] = this.aria_valuenow;
20586 if(this.aria_valuemin){
20587 cfg['aria-valuemin'] = this.aria_valuemin;
20590 if(this.aria_valuemax){
20591 cfg['aria-valuemax'] = this.aria_valuemax;
20594 if(this.label && !this.sr_only){
20595 cfg.html = this.label;
20599 cfg.cls += ' progress-bar-' + this.panel;
20605 update : function(aria_valuenow)
20607 this.aria_valuenow = aria_valuenow;
20609 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20624 * @class Roo.bootstrap.TabGroup
20625 * @extends Roo.bootstrap.Column
20626 * Bootstrap Column class
20627 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20628 * @cfg {Boolean} carousel true to make the group behave like a carousel
20629 * @cfg {Boolean} bullets show bullets for the panels
20630 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20631 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20632 * @cfg {Boolean} showarrow (true|false) show arrow default true
20635 * Create a new TabGroup
20636 * @param {Object} config The config object
20639 Roo.bootstrap.TabGroup = function(config){
20640 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20642 this.navId = Roo.id();
20645 Roo.bootstrap.TabGroup.register(this);
20649 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20652 transition : false,
20657 slideOnTouch : false,
20660 getAutoCreate : function()
20662 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20664 cfg.cls += ' tab-content';
20666 if (this.carousel) {
20667 cfg.cls += ' carousel slide';
20670 cls : 'carousel-inner',
20674 if(this.bullets && !Roo.isTouch){
20677 cls : 'carousel-bullets',
20681 if(this.bullets_cls){
20682 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20689 cfg.cn[0].cn.push(bullets);
20692 if(this.showarrow){
20693 cfg.cn[0].cn.push({
20695 class : 'carousel-arrow',
20699 class : 'carousel-prev',
20703 class : 'fa fa-chevron-left'
20709 class : 'carousel-next',
20713 class : 'fa fa-chevron-right'
20726 initEvents: function()
20728 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20729 // this.el.on("touchstart", this.onTouchStart, this);
20732 if(this.autoslide){
20735 this.slideFn = window.setInterval(function() {
20736 _this.showPanelNext();
20740 if(this.showarrow){
20741 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20742 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20748 // onTouchStart : function(e, el, o)
20750 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20754 // this.showPanelNext();
20758 getChildContainer : function()
20760 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20764 * register a Navigation item
20765 * @param {Roo.bootstrap.NavItem} the navitem to add
20767 register : function(item)
20769 this.tabs.push( item);
20770 item.navId = this.navId; // not really needed..
20775 getActivePanel : function()
20778 Roo.each(this.tabs, function(t) {
20788 getPanelByName : function(n)
20791 Roo.each(this.tabs, function(t) {
20792 if (t.tabId == n) {
20800 indexOfPanel : function(p)
20803 Roo.each(this.tabs, function(t,i) {
20804 if (t.tabId == p.tabId) {
20813 * show a specific panel
20814 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20815 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20817 showPanel : function (pan)
20819 if(this.transition || typeof(pan) == 'undefined'){
20820 Roo.log("waiting for the transitionend");
20824 if (typeof(pan) == 'number') {
20825 pan = this.tabs[pan];
20828 if (typeof(pan) == 'string') {
20829 pan = this.getPanelByName(pan);
20832 var cur = this.getActivePanel();
20835 Roo.log('pan or acitve pan is undefined');
20839 if (pan.tabId == this.getActivePanel().tabId) {
20843 if (false === cur.fireEvent('beforedeactivate')) {
20847 if(this.bullets > 0 && !Roo.isTouch){
20848 this.setActiveBullet(this.indexOfPanel(pan));
20851 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20853 //class="carousel-item carousel-item-next carousel-item-left"
20855 this.transition = true;
20856 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20857 var lr = dir == 'next' ? 'left' : 'right';
20858 pan.el.addClass(dir); // or prev
20859 pan.el.addClass('carousel-item-' + dir); // or prev
20860 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20861 cur.el.addClass(lr); // or right
20862 pan.el.addClass(lr);
20863 cur.el.addClass('carousel-item-' +lr); // or right
20864 pan.el.addClass('carousel-item-' +lr);
20868 cur.el.on('transitionend', function() {
20869 Roo.log("trans end?");
20871 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20872 pan.setActive(true);
20874 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20875 cur.setActive(false);
20877 _this.transition = false;
20879 }, this, { single: true } );
20884 cur.setActive(false);
20885 pan.setActive(true);
20890 showPanelNext : function()
20892 var i = this.indexOfPanel(this.getActivePanel());
20894 if (i >= this.tabs.length - 1 && !this.autoslide) {
20898 if (i >= this.tabs.length - 1 && this.autoslide) {
20902 this.showPanel(this.tabs[i+1]);
20905 showPanelPrev : function()
20907 var i = this.indexOfPanel(this.getActivePanel());
20909 if (i < 1 && !this.autoslide) {
20913 if (i < 1 && this.autoslide) {
20914 i = this.tabs.length;
20917 this.showPanel(this.tabs[i-1]);
20921 addBullet: function()
20923 if(!this.bullets || Roo.isTouch){
20926 var ctr = this.el.select('.carousel-bullets',true).first();
20927 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20928 var bullet = ctr.createChild({
20929 cls : 'bullet bullet-' + i
20930 },ctr.dom.lastChild);
20935 bullet.on('click', (function(e, el, o, ii, t){
20937 e.preventDefault();
20939 this.showPanel(ii);
20941 if(this.autoslide && this.slideFn){
20942 clearInterval(this.slideFn);
20943 this.slideFn = window.setInterval(function() {
20944 _this.showPanelNext();
20948 }).createDelegate(this, [i, bullet], true));
20953 setActiveBullet : function(i)
20959 Roo.each(this.el.select('.bullet', true).elements, function(el){
20960 el.removeClass('selected');
20963 var bullet = this.el.select('.bullet-' + i, true).first();
20969 bullet.addClass('selected');
20980 Roo.apply(Roo.bootstrap.TabGroup, {
20984 * register a Navigation Group
20985 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20987 register : function(navgrp)
20989 this.groups[navgrp.navId] = navgrp;
20993 * fetch a Navigation Group based on the navigation ID
20994 * if one does not exist , it will get created.
20995 * @param {string} the navgroup to add
20996 * @returns {Roo.bootstrap.NavGroup} the navgroup
20998 get: function(navId) {
20999 if (typeof(this.groups[navId]) == 'undefined') {
21000 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21002 return this.groups[navId] ;
21017 * @class Roo.bootstrap.TabPanel
21018 * @extends Roo.bootstrap.Component
21019 * Bootstrap TabPanel class
21020 * @cfg {Boolean} active panel active
21021 * @cfg {String} html panel content
21022 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21023 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21024 * @cfg {String} href click to link..
21025 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21029 * Create a new TabPanel
21030 * @param {Object} config The config object
21033 Roo.bootstrap.TabPanel = function(config){
21034 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21038 * Fires when the active status changes
21039 * @param {Roo.bootstrap.TabPanel} this
21040 * @param {Boolean} state the new state
21045 * @event beforedeactivate
21046 * Fires before a tab is de-activated - can be used to do validation on a form.
21047 * @param {Roo.bootstrap.TabPanel} this
21048 * @return {Boolean} false if there is an error
21051 'beforedeactivate': true
21054 this.tabId = this.tabId || Roo.id();
21058 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21065 touchSlide : false,
21066 getAutoCreate : function(){
21071 // item is needed for carousel - not sure if it has any effect otherwise
21072 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21073 html: this.html || ''
21077 cfg.cls += ' active';
21081 cfg.tabId = this.tabId;
21089 initEvents: function()
21091 var p = this.parent();
21093 this.navId = this.navId || p.navId;
21095 if (typeof(this.navId) != 'undefined') {
21096 // not really needed.. but just in case.. parent should be a NavGroup.
21097 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21101 var i = tg.tabs.length - 1;
21103 if(this.active && tg.bullets > 0 && i < tg.bullets){
21104 tg.setActiveBullet(i);
21108 this.el.on('click', this.onClick, this);
21110 if(Roo.isTouch && this.touchSlide){
21111 this.el.on("touchstart", this.onTouchStart, this);
21112 this.el.on("touchmove", this.onTouchMove, this);
21113 this.el.on("touchend", this.onTouchEnd, this);
21118 onRender : function(ct, position)
21120 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21123 setActive : function(state)
21125 Roo.log("panel - set active " + this.tabId + "=" + state);
21127 this.active = state;
21129 this.el.removeClass('active');
21131 } else if (!this.el.hasClass('active')) {
21132 this.el.addClass('active');
21135 this.fireEvent('changed', this, state);
21138 onClick : function(e)
21140 e.preventDefault();
21142 if(!this.href.length){
21146 window.location.href = this.href;
21155 onTouchStart : function(e)
21157 this.swiping = false;
21159 this.startX = e.browserEvent.touches[0].clientX;
21160 this.startY = e.browserEvent.touches[0].clientY;
21163 onTouchMove : function(e)
21165 this.swiping = true;
21167 this.endX = e.browserEvent.touches[0].clientX;
21168 this.endY = e.browserEvent.touches[0].clientY;
21171 onTouchEnd : function(e)
21178 var tabGroup = this.parent();
21180 if(this.endX > this.startX){ // swiping right
21181 tabGroup.showPanelPrev();
21185 if(this.startX > this.endX){ // swiping left
21186 tabGroup.showPanelNext();
21205 * @class Roo.bootstrap.DateField
21206 * @extends Roo.bootstrap.Input
21207 * Bootstrap DateField class
21208 * @cfg {Number} weekStart default 0
21209 * @cfg {String} viewMode default empty, (months|years)
21210 * @cfg {String} minViewMode default empty, (months|years)
21211 * @cfg {Number} startDate default -Infinity
21212 * @cfg {Number} endDate default Infinity
21213 * @cfg {Boolean} todayHighlight default false
21214 * @cfg {Boolean} todayBtn default false
21215 * @cfg {Boolean} calendarWeeks default false
21216 * @cfg {Object} daysOfWeekDisabled default empty
21217 * @cfg {Boolean} singleMode default false (true | false)
21219 * @cfg {Boolean} keyboardNavigation default true
21220 * @cfg {String} language default en
21223 * Create a new DateField
21224 * @param {Object} config The config object
21227 Roo.bootstrap.DateField = function(config){
21228 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21232 * Fires when this field show.
21233 * @param {Roo.bootstrap.DateField} this
21234 * @param {Mixed} date The date value
21239 * Fires when this field hide.
21240 * @param {Roo.bootstrap.DateField} this
21241 * @param {Mixed} date The date value
21246 * Fires when select a date.
21247 * @param {Roo.bootstrap.DateField} this
21248 * @param {Mixed} date The date value
21252 * @event beforeselect
21253 * Fires when before select a date.
21254 * @param {Roo.bootstrap.DateField} this
21255 * @param {Mixed} date The date value
21257 beforeselect : true
21261 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21264 * @cfg {String} format
21265 * The default date format string which can be overriden for localization support. The format must be
21266 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21270 * @cfg {String} altFormats
21271 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21272 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21274 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21282 todayHighlight : false,
21288 keyboardNavigation: true,
21290 calendarWeeks: false,
21292 startDate: -Infinity,
21296 daysOfWeekDisabled: [],
21300 singleMode : false,
21302 UTCDate: function()
21304 return new Date(Date.UTC.apply(Date, arguments));
21307 UTCToday: function()
21309 var today = new Date();
21310 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21313 getDate: function() {
21314 var d = this.getUTCDate();
21315 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21318 getUTCDate: function() {
21322 setDate: function(d) {
21323 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21326 setUTCDate: function(d) {
21328 this.setValue(this.formatDate(this.date));
21331 onRender: function(ct, position)
21334 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21336 this.language = this.language || 'en';
21337 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21338 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21340 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21341 this.format = this.format || 'm/d/y';
21342 this.isInline = false;
21343 this.isInput = true;
21344 this.component = this.el.select('.add-on', true).first() || false;
21345 this.component = (this.component && this.component.length === 0) ? false : this.component;
21346 this.hasInput = this.component && this.inputEl().length;
21348 if (typeof(this.minViewMode === 'string')) {
21349 switch (this.minViewMode) {
21351 this.minViewMode = 1;
21354 this.minViewMode = 2;
21357 this.minViewMode = 0;
21362 if (typeof(this.viewMode === 'string')) {
21363 switch (this.viewMode) {
21376 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21378 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21380 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21382 this.picker().on('mousedown', this.onMousedown, this);
21383 this.picker().on('click', this.onClick, this);
21385 this.picker().addClass('datepicker-dropdown');
21387 this.startViewMode = this.viewMode;
21389 if(this.singleMode){
21390 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21391 v.setVisibilityMode(Roo.Element.DISPLAY);
21395 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21396 v.setStyle('width', '189px');
21400 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21401 if(!this.calendarWeeks){
21406 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21407 v.attr('colspan', function(i, val){
21408 return parseInt(val) + 1;
21413 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21415 this.setStartDate(this.startDate);
21416 this.setEndDate(this.endDate);
21418 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21425 if(this.isInline) {
21430 picker : function()
21432 return this.pickerEl;
21433 // return this.el.select('.datepicker', true).first();
21436 fillDow: function()
21438 var dowCnt = this.weekStart;
21447 if(this.calendarWeeks){
21455 while (dowCnt < this.weekStart + 7) {
21459 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21463 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21466 fillMonths: function()
21469 var months = this.picker().select('>.datepicker-months td', true).first();
21471 months.dom.innerHTML = '';
21477 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21480 months.createChild(month);
21487 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;
21489 if (this.date < this.startDate) {
21490 this.viewDate = new Date(this.startDate);
21491 } else if (this.date > this.endDate) {
21492 this.viewDate = new Date(this.endDate);
21494 this.viewDate = new Date(this.date);
21502 var d = new Date(this.viewDate),
21503 year = d.getUTCFullYear(),
21504 month = d.getUTCMonth(),
21505 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21506 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21507 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21508 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21509 currentDate = this.date && this.date.valueOf(),
21510 today = this.UTCToday();
21512 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21514 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21516 // this.picker.select('>tfoot th.today').
21517 // .text(dates[this.language].today)
21518 // .toggle(this.todayBtn !== false);
21520 this.updateNavArrows();
21523 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21525 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21527 prevMonth.setUTCDate(day);
21529 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21531 var nextMonth = new Date(prevMonth);
21533 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21535 nextMonth = nextMonth.valueOf();
21537 var fillMonths = false;
21539 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21541 while(prevMonth.valueOf() <= nextMonth) {
21544 if (prevMonth.getUTCDay() === this.weekStart) {
21546 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21554 if(this.calendarWeeks){
21555 // ISO 8601: First week contains first thursday.
21556 // ISO also states week starts on Monday, but we can be more abstract here.
21558 // Start of current week: based on weekstart/current date
21559 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21560 // Thursday of this week
21561 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21562 // First Thursday of year, year from thursday
21563 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21564 // Calendar week: ms between thursdays, div ms per day, div 7 days
21565 calWeek = (th - yth) / 864e5 / 7 + 1;
21567 fillMonths.cn.push({
21575 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21577 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21580 if (this.todayHighlight &&
21581 prevMonth.getUTCFullYear() == today.getFullYear() &&
21582 prevMonth.getUTCMonth() == today.getMonth() &&
21583 prevMonth.getUTCDate() == today.getDate()) {
21584 clsName += ' today';
21587 if (currentDate && prevMonth.valueOf() === currentDate) {
21588 clsName += ' active';
21591 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21592 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21593 clsName += ' disabled';
21596 fillMonths.cn.push({
21598 cls: 'day ' + clsName,
21599 html: prevMonth.getDate()
21602 prevMonth.setDate(prevMonth.getDate()+1);
21605 var currentYear = this.date && this.date.getUTCFullYear();
21606 var currentMonth = this.date && this.date.getUTCMonth();
21608 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21610 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21611 v.removeClass('active');
21613 if(currentYear === year && k === currentMonth){
21614 v.addClass('active');
21617 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21618 v.addClass('disabled');
21624 year = parseInt(year/10, 10) * 10;
21626 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21628 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21631 for (var i = -1; i < 11; i++) {
21632 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21634 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21642 showMode: function(dir)
21645 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21648 Roo.each(this.picker().select('>div',true).elements, function(v){
21649 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21652 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21657 if(this.isInline) {
21661 this.picker().removeClass(['bottom', 'top']);
21663 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21665 * place to the top of element!
21669 this.picker().addClass('top');
21670 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21675 this.picker().addClass('bottom');
21677 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21680 parseDate : function(value)
21682 if(!value || value instanceof Date){
21685 var v = Date.parseDate(value, this.format);
21686 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21687 v = Date.parseDate(value, 'Y-m-d');
21689 if(!v && this.altFormats){
21690 if(!this.altFormatsArray){
21691 this.altFormatsArray = this.altFormats.split("|");
21693 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21694 v = Date.parseDate(value, this.altFormatsArray[i]);
21700 formatDate : function(date, fmt)
21702 return (!date || !(date instanceof Date)) ?
21703 date : date.dateFormat(fmt || this.format);
21706 onFocus : function()
21708 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21712 onBlur : function()
21714 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21716 var d = this.inputEl().getValue();
21723 showPopup : function()
21725 this.picker().show();
21729 this.fireEvent('showpopup', this, this.date);
21732 hidePopup : function()
21734 if(this.isInline) {
21737 this.picker().hide();
21738 this.viewMode = this.startViewMode;
21741 this.fireEvent('hidepopup', this, this.date);
21745 onMousedown: function(e)
21747 e.stopPropagation();
21748 e.preventDefault();
21753 Roo.bootstrap.DateField.superclass.keyup.call(this);
21757 setValue: function(v)
21759 if(this.fireEvent('beforeselect', this, v) !== false){
21760 var d = new Date(this.parseDate(v) ).clearTime();
21762 if(isNaN(d.getTime())){
21763 this.date = this.viewDate = '';
21764 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21768 v = this.formatDate(d);
21770 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21772 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21776 this.fireEvent('select', this, this.date);
21780 getValue: function()
21782 return this.formatDate(this.date);
21785 fireKey: function(e)
21787 if (!this.picker().isVisible()){
21788 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21794 var dateChanged = false,
21796 newDate, newViewDate;
21801 e.preventDefault();
21805 if (!this.keyboardNavigation) {
21808 dir = e.keyCode == 37 ? -1 : 1;
21811 newDate = this.moveYear(this.date, dir);
21812 newViewDate = this.moveYear(this.viewDate, dir);
21813 } else if (e.shiftKey){
21814 newDate = this.moveMonth(this.date, dir);
21815 newViewDate = this.moveMonth(this.viewDate, dir);
21817 newDate = new Date(this.date);
21818 newDate.setUTCDate(this.date.getUTCDate() + dir);
21819 newViewDate = new Date(this.viewDate);
21820 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21822 if (this.dateWithinRange(newDate)){
21823 this.date = newDate;
21824 this.viewDate = newViewDate;
21825 this.setValue(this.formatDate(this.date));
21827 e.preventDefault();
21828 dateChanged = true;
21833 if (!this.keyboardNavigation) {
21836 dir = e.keyCode == 38 ? -1 : 1;
21838 newDate = this.moveYear(this.date, dir);
21839 newViewDate = this.moveYear(this.viewDate, dir);
21840 } else if (e.shiftKey){
21841 newDate = this.moveMonth(this.date, dir);
21842 newViewDate = this.moveMonth(this.viewDate, dir);
21844 newDate = new Date(this.date);
21845 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21846 newViewDate = new Date(this.viewDate);
21847 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21849 if (this.dateWithinRange(newDate)){
21850 this.date = newDate;
21851 this.viewDate = newViewDate;
21852 this.setValue(this.formatDate(this.date));
21854 e.preventDefault();
21855 dateChanged = true;
21859 this.setValue(this.formatDate(this.date));
21861 e.preventDefault();
21864 this.setValue(this.formatDate(this.date));
21878 onClick: function(e)
21880 e.stopPropagation();
21881 e.preventDefault();
21883 var target = e.getTarget();
21885 if(target.nodeName.toLowerCase() === 'i'){
21886 target = Roo.get(target).dom.parentNode;
21889 var nodeName = target.nodeName;
21890 var className = target.className;
21891 var html = target.innerHTML;
21892 //Roo.log(nodeName);
21894 switch(nodeName.toLowerCase()) {
21896 switch(className) {
21902 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21903 switch(this.viewMode){
21905 this.viewDate = this.moveMonth(this.viewDate, dir);
21909 this.viewDate = this.moveYear(this.viewDate, dir);
21915 var date = new Date();
21916 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21918 this.setValue(this.formatDate(this.date));
21925 if (className.indexOf('disabled') < 0) {
21926 this.viewDate.setUTCDate(1);
21927 if (className.indexOf('month') > -1) {
21928 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21930 var year = parseInt(html, 10) || 0;
21931 this.viewDate.setUTCFullYear(year);
21935 if(this.singleMode){
21936 this.setValue(this.formatDate(this.viewDate));
21947 //Roo.log(className);
21948 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21949 var day = parseInt(html, 10) || 1;
21950 var year = (this.viewDate || new Date()).getUTCFullYear(),
21951 month = (this.viewDate || new Date()).getUTCMonth();
21953 if (className.indexOf('old') > -1) {
21960 } else if (className.indexOf('new') > -1) {
21968 //Roo.log([year,month,day]);
21969 this.date = this.UTCDate(year, month, day,0,0,0,0);
21970 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21972 //Roo.log(this.formatDate(this.date));
21973 this.setValue(this.formatDate(this.date));
21980 setStartDate: function(startDate)
21982 this.startDate = startDate || -Infinity;
21983 if (this.startDate !== -Infinity) {
21984 this.startDate = this.parseDate(this.startDate);
21987 this.updateNavArrows();
21990 setEndDate: function(endDate)
21992 this.endDate = endDate || Infinity;
21993 if (this.endDate !== Infinity) {
21994 this.endDate = this.parseDate(this.endDate);
21997 this.updateNavArrows();
22000 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22002 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22003 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22004 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22006 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22007 return parseInt(d, 10);
22010 this.updateNavArrows();
22013 updateNavArrows: function()
22015 if(this.singleMode){
22019 var d = new Date(this.viewDate),
22020 year = d.getUTCFullYear(),
22021 month = d.getUTCMonth();
22023 Roo.each(this.picker().select('.prev', true).elements, function(v){
22025 switch (this.viewMode) {
22028 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22034 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22041 Roo.each(this.picker().select('.next', true).elements, function(v){
22043 switch (this.viewMode) {
22046 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22052 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22060 moveMonth: function(date, dir)
22065 var new_date = new Date(date.valueOf()),
22066 day = new_date.getUTCDate(),
22067 month = new_date.getUTCMonth(),
22068 mag = Math.abs(dir),
22070 dir = dir > 0 ? 1 : -1;
22073 // If going back one month, make sure month is not current month
22074 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22076 return new_date.getUTCMonth() == month;
22078 // If going forward one month, make sure month is as expected
22079 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22081 return new_date.getUTCMonth() != new_month;
22083 new_month = month + dir;
22084 new_date.setUTCMonth(new_month);
22085 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22086 if (new_month < 0 || new_month > 11) {
22087 new_month = (new_month + 12) % 12;
22090 // For magnitudes >1, move one month at a time...
22091 for (var i=0; i<mag; i++) {
22092 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22093 new_date = this.moveMonth(new_date, dir);
22095 // ...then reset the day, keeping it in the new month
22096 new_month = new_date.getUTCMonth();
22097 new_date.setUTCDate(day);
22099 return new_month != new_date.getUTCMonth();
22102 // Common date-resetting loop -- if date is beyond end of month, make it
22105 new_date.setUTCDate(--day);
22106 new_date.setUTCMonth(new_month);
22111 moveYear: function(date, dir)
22113 return this.moveMonth(date, dir*12);
22116 dateWithinRange: function(date)
22118 return date >= this.startDate && date <= this.endDate;
22124 this.picker().remove();
22127 validateValue : function(value)
22129 if(this.getVisibilityEl().hasClass('hidden')){
22133 if(value.length < 1) {
22134 if(this.allowBlank){
22140 if(value.length < this.minLength){
22143 if(value.length > this.maxLength){
22147 var vt = Roo.form.VTypes;
22148 if(!vt[this.vtype](value, this)){
22152 if(typeof this.validator == "function"){
22153 var msg = this.validator(value);
22159 if(this.regex && !this.regex.test(value)){
22163 if(typeof(this.parseDate(value)) == 'undefined'){
22167 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22171 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22181 this.date = this.viewDate = '';
22183 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22188 Roo.apply(Roo.bootstrap.DateField, {
22199 html: '<i class="fa fa-arrow-left"/>'
22209 html: '<i class="fa fa-arrow-right"/>'
22251 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22252 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22253 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22254 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22255 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22268 navFnc: 'FullYear',
22273 navFnc: 'FullYear',
22278 Roo.apply(Roo.bootstrap.DateField, {
22282 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22286 cls: 'datepicker-days',
22290 cls: 'table-condensed',
22292 Roo.bootstrap.DateField.head,
22296 Roo.bootstrap.DateField.footer
22303 cls: 'datepicker-months',
22307 cls: 'table-condensed',
22309 Roo.bootstrap.DateField.head,
22310 Roo.bootstrap.DateField.content,
22311 Roo.bootstrap.DateField.footer
22318 cls: 'datepicker-years',
22322 cls: 'table-condensed',
22324 Roo.bootstrap.DateField.head,
22325 Roo.bootstrap.DateField.content,
22326 Roo.bootstrap.DateField.footer
22345 * @class Roo.bootstrap.TimeField
22346 * @extends Roo.bootstrap.Input
22347 * Bootstrap DateField class
22351 * Create a new TimeField
22352 * @param {Object} config The config object
22355 Roo.bootstrap.TimeField = function(config){
22356 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22360 * Fires when this field show.
22361 * @param {Roo.bootstrap.DateField} thisthis
22362 * @param {Mixed} date The date value
22367 * Fires when this field hide.
22368 * @param {Roo.bootstrap.DateField} this
22369 * @param {Mixed} date The date value
22374 * Fires when select a date.
22375 * @param {Roo.bootstrap.DateField} this
22376 * @param {Mixed} date The date value
22382 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22385 * @cfg {String} format
22386 * The default time format string which can be overriden for localization support. The format must be
22387 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22391 getAutoCreate : function()
22393 this.after = '<i class="fa far fa-clock"></i>';
22394 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22398 onRender: function(ct, position)
22401 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22403 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22405 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22407 this.pop = this.picker().select('>.datepicker-time',true).first();
22408 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22410 this.picker().on('mousedown', this.onMousedown, this);
22411 this.picker().on('click', this.onClick, this);
22413 this.picker().addClass('datepicker-dropdown');
22418 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22419 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22420 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22421 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22422 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22423 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22427 fireKey: function(e){
22428 if (!this.picker().isVisible()){
22429 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22435 e.preventDefault();
22443 this.onTogglePeriod();
22446 this.onIncrementMinutes();
22449 this.onDecrementMinutes();
22458 onClick: function(e) {
22459 e.stopPropagation();
22460 e.preventDefault();
22463 picker : function()
22465 return this.pickerEl;
22468 fillTime: function()
22470 var time = this.pop.select('tbody', true).first();
22472 time.dom.innerHTML = '';
22487 cls: 'hours-up fa fas fa-chevron-up'
22507 cls: 'minutes-up fa fas fa-chevron-up'
22528 cls: 'timepicker-hour',
22543 cls: 'timepicker-minute',
22558 cls: 'btn btn-primary period',
22580 cls: 'hours-down fa fas fa-chevron-down'
22600 cls: 'minutes-down fa fas fa-chevron-down'
22618 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22625 var hours = this.time.getHours();
22626 var minutes = this.time.getMinutes();
22639 hours = hours - 12;
22643 hours = '0' + hours;
22647 minutes = '0' + minutes;
22650 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22651 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22652 this.pop.select('button', true).first().dom.innerHTML = period;
22658 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22660 var cls = ['bottom'];
22662 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22669 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22673 //this.picker().setXY(20000,20000);
22674 this.picker().addClass(cls.join('-'));
22678 Roo.each(cls, function(c){
22683 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22684 //_this.picker().setTop(_this.inputEl().getHeight());
22688 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22690 //_this.picker().setTop(0 - _this.picker().getHeight());
22695 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22699 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22707 onFocus : function()
22709 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22713 onBlur : function()
22715 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22721 this.picker().show();
22726 this.fireEvent('show', this, this.date);
22731 this.picker().hide();
22734 this.fireEvent('hide', this, this.date);
22737 setTime : function()
22740 this.setValue(this.time.format(this.format));
22742 this.fireEvent('select', this, this.date);
22747 onMousedown: function(e){
22748 e.stopPropagation();
22749 e.preventDefault();
22752 onIncrementHours: function()
22754 Roo.log('onIncrementHours');
22755 this.time = this.time.add(Date.HOUR, 1);
22760 onDecrementHours: function()
22762 Roo.log('onDecrementHours');
22763 this.time = this.time.add(Date.HOUR, -1);
22767 onIncrementMinutes: function()
22769 Roo.log('onIncrementMinutes');
22770 this.time = this.time.add(Date.MINUTE, 1);
22774 onDecrementMinutes: function()
22776 Roo.log('onDecrementMinutes');
22777 this.time = this.time.add(Date.MINUTE, -1);
22781 onTogglePeriod: function()
22783 Roo.log('onTogglePeriod');
22784 this.time = this.time.add(Date.HOUR, 12);
22792 Roo.apply(Roo.bootstrap.TimeField, {
22796 cls: 'datepicker dropdown-menu',
22800 cls: 'datepicker-time',
22804 cls: 'table-condensed',
22833 cls: 'btn btn-info ok',
22861 * @class Roo.bootstrap.MonthField
22862 * @extends Roo.bootstrap.Input
22863 * Bootstrap MonthField class
22865 * @cfg {String} language default en
22868 * Create a new MonthField
22869 * @param {Object} config The config object
22872 Roo.bootstrap.MonthField = function(config){
22873 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22878 * Fires when this field show.
22879 * @param {Roo.bootstrap.MonthField} this
22880 * @param {Mixed} date The date value
22885 * Fires when this field hide.
22886 * @param {Roo.bootstrap.MonthField} this
22887 * @param {Mixed} date The date value
22892 * Fires when select a date.
22893 * @param {Roo.bootstrap.MonthField} this
22894 * @param {String} oldvalue The old value
22895 * @param {String} newvalue The new value
22901 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22903 onRender: function(ct, position)
22906 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22908 this.language = this.language || 'en';
22909 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22910 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22912 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22913 this.isInline = false;
22914 this.isInput = true;
22915 this.component = this.el.select('.add-on', true).first() || false;
22916 this.component = (this.component && this.component.length === 0) ? false : this.component;
22917 this.hasInput = this.component && this.inputEL().length;
22919 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22921 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22923 this.picker().on('mousedown', this.onMousedown, this);
22924 this.picker().on('click', this.onClick, this);
22926 this.picker().addClass('datepicker-dropdown');
22928 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22929 v.setStyle('width', '189px');
22936 if(this.isInline) {
22942 setValue: function(v, suppressEvent)
22944 var o = this.getValue();
22946 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22950 if(suppressEvent !== true){
22951 this.fireEvent('select', this, o, v);
22956 getValue: function()
22961 onClick: function(e)
22963 e.stopPropagation();
22964 e.preventDefault();
22966 var target = e.getTarget();
22968 if(target.nodeName.toLowerCase() === 'i'){
22969 target = Roo.get(target).dom.parentNode;
22972 var nodeName = target.nodeName;
22973 var className = target.className;
22974 var html = target.innerHTML;
22976 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22980 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22982 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22988 picker : function()
22990 return this.pickerEl;
22993 fillMonths: function()
22996 var months = this.picker().select('>.datepicker-months td', true).first();
22998 months.dom.innerHTML = '';
23004 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23007 months.createChild(month);
23016 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23017 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23020 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23021 e.removeClass('active');
23023 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23024 e.addClass('active');
23031 if(this.isInline) {
23035 this.picker().removeClass(['bottom', 'top']);
23037 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23039 * place to the top of element!
23043 this.picker().addClass('top');
23044 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23049 this.picker().addClass('bottom');
23051 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23054 onFocus : function()
23056 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23060 onBlur : function()
23062 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23064 var d = this.inputEl().getValue();
23073 this.picker().show();
23074 this.picker().select('>.datepicker-months', true).first().show();
23078 this.fireEvent('show', this, this.date);
23083 if(this.isInline) {
23086 this.picker().hide();
23087 this.fireEvent('hide', this, this.date);
23091 onMousedown: function(e)
23093 e.stopPropagation();
23094 e.preventDefault();
23099 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23103 fireKey: function(e)
23105 if (!this.picker().isVisible()){
23106 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23117 e.preventDefault();
23121 dir = e.keyCode == 37 ? -1 : 1;
23123 this.vIndex = this.vIndex + dir;
23125 if(this.vIndex < 0){
23129 if(this.vIndex > 11){
23133 if(isNaN(this.vIndex)){
23137 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23143 dir = e.keyCode == 38 ? -1 : 1;
23145 this.vIndex = this.vIndex + dir * 4;
23147 if(this.vIndex < 0){
23151 if(this.vIndex > 11){
23155 if(isNaN(this.vIndex)){
23159 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23164 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23165 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23169 e.preventDefault();
23172 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23173 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23189 this.picker().remove();
23194 Roo.apply(Roo.bootstrap.MonthField, {
23213 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23214 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23219 Roo.apply(Roo.bootstrap.MonthField, {
23223 cls: 'datepicker dropdown-menu roo-dynamic',
23227 cls: 'datepicker-months',
23231 cls: 'table-condensed',
23233 Roo.bootstrap.DateField.content
23253 * @class Roo.bootstrap.CheckBox
23254 * @extends Roo.bootstrap.Input
23255 * Bootstrap CheckBox class
23257 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23258 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23259 * @cfg {String} boxLabel The text that appears beside the checkbox
23260 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23261 * @cfg {Boolean} checked initnal the element
23262 * @cfg {Boolean} inline inline the element (default false)
23263 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23264 * @cfg {String} tooltip label tooltip
23267 * Create a new CheckBox
23268 * @param {Object} config The config object
23271 Roo.bootstrap.CheckBox = function(config){
23272 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23277 * Fires when the element is checked or unchecked.
23278 * @param {Roo.bootstrap.CheckBox} this This input
23279 * @param {Boolean} checked The new checked value
23284 * Fires when the element is click.
23285 * @param {Roo.bootstrap.CheckBox} this This input
23292 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23294 inputType: 'checkbox',
23303 // checkbox success does not make any sense really..
23308 getAutoCreate : function()
23310 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23316 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23319 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23325 type : this.inputType,
23326 value : this.inputValue,
23327 cls : 'roo-' + this.inputType, //'form-box',
23328 placeholder : this.placeholder || ''
23332 if(this.inputType != 'radio'){
23336 cls : 'roo-hidden-value',
23337 value : this.checked ? this.inputValue : this.valueOff
23342 if (this.weight) { // Validity check?
23343 cfg.cls += " " + this.inputType + "-" + this.weight;
23346 if (this.disabled) {
23347 input.disabled=true;
23351 input.checked = this.checked;
23356 input.name = this.name;
23358 if(this.inputType != 'radio'){
23359 hidden.name = this.name;
23360 input.name = '_hidden_' + this.name;
23365 input.cls += ' input-' + this.size;
23370 ['xs','sm','md','lg'].map(function(size){
23371 if (settings[size]) {
23372 cfg.cls += ' col-' + size + '-' + settings[size];
23376 var inputblock = input;
23378 if (this.before || this.after) {
23381 cls : 'input-group',
23386 inputblock.cn.push({
23388 cls : 'input-group-addon',
23393 inputblock.cn.push(input);
23395 if(this.inputType != 'radio'){
23396 inputblock.cn.push(hidden);
23400 inputblock.cn.push({
23402 cls : 'input-group-addon',
23408 var boxLabelCfg = false;
23414 //'for': id, // box label is handled by onclick - so no for...
23416 html: this.boxLabel
23419 boxLabelCfg.tooltip = this.tooltip;
23425 if (align ==='left' && this.fieldLabel.length) {
23426 // Roo.log("left and has label");
23431 cls : 'control-label',
23432 html : this.fieldLabel
23443 cfg.cn[1].cn.push(boxLabelCfg);
23446 if(this.labelWidth > 12){
23447 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23450 if(this.labelWidth < 13 && this.labelmd == 0){
23451 this.labelmd = this.labelWidth;
23454 if(this.labellg > 0){
23455 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23456 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23459 if(this.labelmd > 0){
23460 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23461 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23464 if(this.labelsm > 0){
23465 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23466 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23469 if(this.labelxs > 0){
23470 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23471 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23474 } else if ( this.fieldLabel.length) {
23475 // Roo.log(" label");
23479 tag: this.boxLabel ? 'span' : 'label',
23481 cls: 'control-label box-input-label',
23482 //cls : 'input-group-addon',
23483 html : this.fieldLabel
23490 cfg.cn.push(boxLabelCfg);
23495 // Roo.log(" no label && no align");
23496 cfg.cn = [ inputblock ] ;
23498 cfg.cn.push(boxLabelCfg);
23506 if(this.inputType != 'radio'){
23507 cfg.cn.push(hidden);
23515 * return the real input element.
23517 inputEl: function ()
23519 return this.el.select('input.roo-' + this.inputType,true).first();
23521 hiddenEl: function ()
23523 return this.el.select('input.roo-hidden-value',true).first();
23526 labelEl: function()
23528 return this.el.select('label.control-label',true).first();
23530 /* depricated... */
23534 return this.labelEl();
23537 boxLabelEl: function()
23539 return this.el.select('label.box-label',true).first();
23542 initEvents : function()
23544 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23546 this.inputEl().on('click', this.onClick, this);
23548 if (this.boxLabel) {
23549 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23552 this.startValue = this.getValue();
23555 Roo.bootstrap.CheckBox.register(this);
23559 onClick : function(e)
23561 if(this.fireEvent('click', this, e) !== false){
23562 this.setChecked(!this.checked);
23567 setChecked : function(state,suppressEvent)
23569 this.startValue = this.getValue();
23571 if(this.inputType == 'radio'){
23573 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23574 e.dom.checked = false;
23577 this.inputEl().dom.checked = true;
23579 this.inputEl().dom.value = this.inputValue;
23581 if(suppressEvent !== true){
23582 this.fireEvent('check', this, true);
23590 this.checked = state;
23592 this.inputEl().dom.checked = state;
23595 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23597 if(suppressEvent !== true){
23598 this.fireEvent('check', this, state);
23604 getValue : function()
23606 if(this.inputType == 'radio'){
23607 return this.getGroupValue();
23610 return this.hiddenEl().dom.value;
23614 getGroupValue : function()
23616 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23620 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23623 setValue : function(v,suppressEvent)
23625 if(this.inputType == 'radio'){
23626 this.setGroupValue(v, suppressEvent);
23630 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23635 setGroupValue : function(v, suppressEvent)
23637 this.startValue = this.getValue();
23639 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23640 e.dom.checked = false;
23642 if(e.dom.value == v){
23643 e.dom.checked = true;
23647 if(suppressEvent !== true){
23648 this.fireEvent('check', this, true);
23656 validate : function()
23658 if(this.getVisibilityEl().hasClass('hidden')){
23664 (this.inputType == 'radio' && this.validateRadio()) ||
23665 (this.inputType == 'checkbox' && this.validateCheckbox())
23671 this.markInvalid();
23675 validateRadio : function()
23677 if(this.getVisibilityEl().hasClass('hidden')){
23681 if(this.allowBlank){
23687 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23688 if(!e.dom.checked){
23700 validateCheckbox : function()
23703 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23704 //return (this.getValue() == this.inputValue) ? true : false;
23707 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23715 for(var i in group){
23716 if(group[i].el.isVisible(true)){
23724 for(var i in group){
23729 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23736 * Mark this field as valid
23738 markValid : function()
23742 this.fireEvent('valid', this);
23744 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23747 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23754 if(this.inputType == 'radio'){
23755 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23756 var fg = e.findParent('.form-group', false, true);
23757 if (Roo.bootstrap.version == 3) {
23758 fg.removeClass([_this.invalidClass, _this.validClass]);
23759 fg.addClass(_this.validClass);
23761 fg.removeClass(['is-valid', 'is-invalid']);
23762 fg.addClass('is-valid');
23770 var fg = this.el.findParent('.form-group', false, true);
23771 if (Roo.bootstrap.version == 3) {
23772 fg.removeClass([this.invalidClass, this.validClass]);
23773 fg.addClass(this.validClass);
23775 fg.removeClass(['is-valid', 'is-invalid']);
23776 fg.addClass('is-valid');
23781 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23787 for(var i in group){
23788 var fg = group[i].el.findParent('.form-group', false, true);
23789 if (Roo.bootstrap.version == 3) {
23790 fg.removeClass([this.invalidClass, this.validClass]);
23791 fg.addClass(this.validClass);
23793 fg.removeClass(['is-valid', 'is-invalid']);
23794 fg.addClass('is-valid');
23800 * Mark this field as invalid
23801 * @param {String} msg The validation message
23803 markInvalid : function(msg)
23805 if(this.allowBlank){
23811 this.fireEvent('invalid', this, msg);
23813 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23816 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23820 label.markInvalid();
23823 if(this.inputType == 'radio'){
23825 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23826 var fg = e.findParent('.form-group', false, true);
23827 if (Roo.bootstrap.version == 3) {
23828 fg.removeClass([_this.invalidClass, _this.validClass]);
23829 fg.addClass(_this.invalidClass);
23831 fg.removeClass(['is-invalid', 'is-valid']);
23832 fg.addClass('is-invalid');
23840 var fg = this.el.findParent('.form-group', false, true);
23841 if (Roo.bootstrap.version == 3) {
23842 fg.removeClass([_this.invalidClass, _this.validClass]);
23843 fg.addClass(_this.invalidClass);
23845 fg.removeClass(['is-invalid', 'is-valid']);
23846 fg.addClass('is-invalid');
23851 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23857 for(var i in group){
23858 var fg = group[i].el.findParent('.form-group', false, true);
23859 if (Roo.bootstrap.version == 3) {
23860 fg.removeClass([_this.invalidClass, _this.validClass]);
23861 fg.addClass(_this.invalidClass);
23863 fg.removeClass(['is-invalid', 'is-valid']);
23864 fg.addClass('is-invalid');
23870 clearInvalid : function()
23872 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23874 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23876 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23878 if (label && label.iconEl) {
23879 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23880 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23884 disable : function()
23886 if(this.inputType != 'radio'){
23887 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23894 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23895 _this.getActionEl().addClass(this.disabledClass);
23896 e.dom.disabled = true;
23900 this.disabled = true;
23901 this.fireEvent("disable", this);
23905 enable : function()
23907 if(this.inputType != 'radio'){
23908 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23915 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23916 _this.getActionEl().removeClass(this.disabledClass);
23917 e.dom.disabled = false;
23921 this.disabled = false;
23922 this.fireEvent("enable", this);
23926 setBoxLabel : function(v)
23931 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23937 Roo.apply(Roo.bootstrap.CheckBox, {
23942 * register a CheckBox Group
23943 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23945 register : function(checkbox)
23947 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23948 this.groups[checkbox.groupId] = {};
23951 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23955 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23959 * fetch a CheckBox Group based on the group ID
23960 * @param {string} the group ID
23961 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23963 get: function(groupId) {
23964 if (typeof(this.groups[groupId]) == 'undefined') {
23968 return this.groups[groupId] ;
23981 * @class Roo.bootstrap.Radio
23982 * @extends Roo.bootstrap.Component
23983 * Bootstrap Radio class
23984 * @cfg {String} boxLabel - the label associated
23985 * @cfg {String} value - the value of radio
23988 * Create a new Radio
23989 * @param {Object} config The config object
23991 Roo.bootstrap.Radio = function(config){
23992 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23996 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24002 getAutoCreate : function()
24006 cls : 'form-group radio',
24011 html : this.boxLabel
24019 initEvents : function()
24021 this.parent().register(this);
24023 this.el.on('click', this.onClick, this);
24027 onClick : function(e)
24029 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24030 this.setChecked(true);
24034 setChecked : function(state, suppressEvent)
24036 this.parent().setValue(this.value, suppressEvent);
24040 setBoxLabel : function(v)
24045 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24060 * @class Roo.bootstrap.SecurePass
24061 * @extends Roo.bootstrap.Input
24062 * Bootstrap SecurePass class
24066 * Create a new SecurePass
24067 * @param {Object} config The config object
24070 Roo.bootstrap.SecurePass = function (config) {
24071 // these go here, so the translation tool can replace them..
24073 PwdEmpty: "Please type a password, and then retype it to confirm.",
24074 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24075 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24076 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24077 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24078 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24079 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24080 TooWeak: "Your password is Too Weak."
24082 this.meterLabel = "Password strength:";
24083 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24084 this.meterClass = [
24085 "roo-password-meter-tooweak",
24086 "roo-password-meter-weak",
24087 "roo-password-meter-medium",
24088 "roo-password-meter-strong",
24089 "roo-password-meter-grey"
24094 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24097 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24099 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24101 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24102 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24103 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24104 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24105 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24106 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24107 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24117 * @cfg {String/Object} Label for the strength meter (defaults to
24118 * 'Password strength:')
24123 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24124 * ['Weak', 'Medium', 'Strong'])
24127 pwdStrengths: false,
24140 initEvents: function ()
24142 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24144 if (this.el.is('input[type=password]') && Roo.isSafari) {
24145 this.el.on('keydown', this.SafariOnKeyDown, this);
24148 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24151 onRender: function (ct, position)
24153 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24154 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24155 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24157 this.trigger.createChild({
24162 cls: 'roo-password-meter-grey col-xs-12',
24165 //width: this.meterWidth + 'px'
24169 cls: 'roo-password-meter-text'
24175 if (this.hideTrigger) {
24176 this.trigger.setDisplayed(false);
24178 this.setSize(this.width || '', this.height || '');
24181 onDestroy: function ()
24183 if (this.trigger) {
24184 this.trigger.removeAllListeners();
24185 this.trigger.remove();
24188 this.wrap.remove();
24190 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24193 checkStrength: function ()
24195 var pwd = this.inputEl().getValue();
24196 if (pwd == this._lastPwd) {
24201 if (this.ClientSideStrongPassword(pwd)) {
24203 } else if (this.ClientSideMediumPassword(pwd)) {
24205 } else if (this.ClientSideWeakPassword(pwd)) {
24211 Roo.log('strength1: ' + strength);
24213 //var pm = this.trigger.child('div/div/div').dom;
24214 var pm = this.trigger.child('div/div');
24215 pm.removeClass(this.meterClass);
24216 pm.addClass(this.meterClass[strength]);
24219 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24221 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24223 this._lastPwd = pwd;
24227 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24229 this._lastPwd = '';
24231 var pm = this.trigger.child('div/div');
24232 pm.removeClass(this.meterClass);
24233 pm.addClass('roo-password-meter-grey');
24236 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24239 this.inputEl().dom.type='password';
24242 validateValue: function (value)
24244 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24247 if (value.length == 0) {
24248 if (this.allowBlank) {
24249 this.clearInvalid();
24253 this.markInvalid(this.errors.PwdEmpty);
24254 this.errorMsg = this.errors.PwdEmpty;
24262 if (!value.match(/[\x21-\x7e]+/)) {
24263 this.markInvalid(this.errors.PwdBadChar);
24264 this.errorMsg = this.errors.PwdBadChar;
24267 if (value.length < 6) {
24268 this.markInvalid(this.errors.PwdShort);
24269 this.errorMsg = this.errors.PwdShort;
24272 if (value.length > 16) {
24273 this.markInvalid(this.errors.PwdLong);
24274 this.errorMsg = this.errors.PwdLong;
24278 if (this.ClientSideStrongPassword(value)) {
24280 } else if (this.ClientSideMediumPassword(value)) {
24282 } else if (this.ClientSideWeakPassword(value)) {
24289 if (strength < 2) {
24290 //this.markInvalid(this.errors.TooWeak);
24291 this.errorMsg = this.errors.TooWeak;
24296 console.log('strength2: ' + strength);
24298 //var pm = this.trigger.child('div/div/div').dom;
24300 var pm = this.trigger.child('div/div');
24301 pm.removeClass(this.meterClass);
24302 pm.addClass(this.meterClass[strength]);
24304 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24306 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24308 this.errorMsg = '';
24312 CharacterSetChecks: function (type)
24315 this.fResult = false;
24318 isctype: function (character, type)
24321 case this.kCapitalLetter:
24322 if (character >= 'A' && character <= 'Z') {
24327 case this.kSmallLetter:
24328 if (character >= 'a' && character <= 'z') {
24334 if (character >= '0' && character <= '9') {
24339 case this.kPunctuation:
24340 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24351 IsLongEnough: function (pwd, size)
24353 return !(pwd == null || isNaN(size) || pwd.length < size);
24356 SpansEnoughCharacterSets: function (word, nb)
24358 if (!this.IsLongEnough(word, nb))
24363 var characterSetChecks = new Array(
24364 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24365 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24368 for (var index = 0; index < word.length; ++index) {
24369 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24370 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24371 characterSetChecks[nCharSet].fResult = true;
24378 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24379 if (characterSetChecks[nCharSet].fResult) {
24384 if (nCharSets < nb) {
24390 ClientSideStrongPassword: function (pwd)
24392 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24395 ClientSideMediumPassword: function (pwd)
24397 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24400 ClientSideWeakPassword: function (pwd)
24402 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24405 })//<script type="text/javascript">
24408 * Based Ext JS Library 1.1.1
24409 * Copyright(c) 2006-2007, Ext JS, LLC.
24415 * @class Roo.HtmlEditorCore
24416 * @extends Roo.Component
24417 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24419 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24422 Roo.HtmlEditorCore = function(config){
24425 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24430 * @event initialize
24431 * Fires when the editor is fully initialized (including the iframe)
24432 * @param {Roo.HtmlEditorCore} this
24437 * Fires when the editor is first receives the focus. Any insertion must wait
24438 * until after this event.
24439 * @param {Roo.HtmlEditorCore} this
24443 * @event beforesync
24444 * Fires before the textarea is updated with content from the editor iframe. Return false
24445 * to cancel the sync.
24446 * @param {Roo.HtmlEditorCore} this
24447 * @param {String} html
24451 * @event beforepush
24452 * Fires before the iframe editor is updated with content from the textarea. Return false
24453 * to cancel the push.
24454 * @param {Roo.HtmlEditorCore} this
24455 * @param {String} html
24460 * Fires when the textarea is updated with content from the editor iframe.
24461 * @param {Roo.HtmlEditorCore} this
24462 * @param {String} html
24467 * Fires when the iframe editor is updated with content from the textarea.
24468 * @param {Roo.HtmlEditorCore} this
24469 * @param {String} html
24474 * @event editorevent
24475 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24476 * @param {Roo.HtmlEditorCore} this
24482 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24484 // defaults : white / black...
24485 this.applyBlacklists();
24492 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24496 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24502 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24507 * @cfg {Number} height (in pixels)
24511 * @cfg {Number} width (in pixels)
24516 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24519 stylesheets: false,
24524 // private properties
24525 validationEvent : false,
24527 initialized : false,
24529 sourceEditMode : false,
24530 onFocus : Roo.emptyFn,
24532 hideMode:'offsets',
24536 // blacklist + whitelisted elements..
24543 * Protected method that will not generally be called directly. It
24544 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24545 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24547 getDocMarkup : function(){
24551 // inherit styels from page...??
24552 if (this.stylesheets === false) {
24554 Roo.get(document.head).select('style').each(function(node) {
24555 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24558 Roo.get(document.head).select('link').each(function(node) {
24559 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24562 } else if (!this.stylesheets.length) {
24564 st = '<style type="text/css">' +
24565 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24568 for (var i in this.stylesheets) {
24569 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24574 st += '<style type="text/css">' +
24575 'IMG { cursor: pointer } ' +
24578 var cls = 'roo-htmleditor-body';
24580 if(this.bodyCls.length){
24581 cls += ' ' + this.bodyCls;
24584 return '<html><head>' + st +
24585 //<style type="text/css">' +
24586 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24588 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24592 onRender : function(ct, position)
24595 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24596 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24599 this.el.dom.style.border = '0 none';
24600 this.el.dom.setAttribute('tabIndex', -1);
24601 this.el.addClass('x-hidden hide');
24605 if(Roo.isIE){ // fix IE 1px bogus margin
24606 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24610 this.frameId = Roo.id();
24614 var iframe = this.owner.wrap.createChild({
24616 cls: 'form-control', // bootstrap..
24618 name: this.frameId,
24619 frameBorder : 'no',
24620 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24625 this.iframe = iframe.dom;
24627 this.assignDocWin();
24629 this.doc.designMode = 'on';
24632 this.doc.write(this.getDocMarkup());
24636 var task = { // must defer to wait for browser to be ready
24638 //console.log("run task?" + this.doc.readyState);
24639 this.assignDocWin();
24640 if(this.doc.body || this.doc.readyState == 'complete'){
24642 this.doc.designMode="on";
24646 Roo.TaskMgr.stop(task);
24647 this.initEditor.defer(10, this);
24654 Roo.TaskMgr.start(task);
24659 onResize : function(w, h)
24661 Roo.log('resize: ' +w + ',' + h );
24662 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24666 if(typeof w == 'number'){
24668 this.iframe.style.width = w + 'px';
24670 if(typeof h == 'number'){
24672 this.iframe.style.height = h + 'px';
24674 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24681 * Toggles the editor between standard and source edit mode.
24682 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24684 toggleSourceEdit : function(sourceEditMode){
24686 this.sourceEditMode = sourceEditMode === true;
24688 if(this.sourceEditMode){
24690 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24693 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24694 //this.iframe.className = '';
24697 //this.setSize(this.owner.wrap.getSize());
24698 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24705 * Protected method that will not generally be called directly. If you need/want
24706 * custom HTML cleanup, this is the method you should override.
24707 * @param {String} html The HTML to be cleaned
24708 * return {String} The cleaned HTML
24710 cleanHtml : function(html){
24711 html = String(html);
24712 if(html.length > 5){
24713 if(Roo.isSafari){ // strip safari nonsense
24714 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24717 if(html == ' '){
24724 * HTML Editor -> Textarea
24725 * Protected method that will not generally be called directly. Syncs the contents
24726 * of the editor iframe with the textarea.
24728 syncValue : function(){
24729 if(this.initialized){
24730 var bd = (this.doc.body || this.doc.documentElement);
24731 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24732 var html = bd.innerHTML;
24734 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24735 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24737 html = '<div style="'+m[0]+'">' + html + '</div>';
24740 html = this.cleanHtml(html);
24741 // fix up the special chars.. normaly like back quotes in word...
24742 // however we do not want to do this with chinese..
24743 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24745 var cc = match.charCodeAt();
24747 // Get the character value, handling surrogate pairs
24748 if (match.length == 2) {
24749 // It's a surrogate pair, calculate the Unicode code point
24750 var high = match.charCodeAt(0) - 0xD800;
24751 var low = match.charCodeAt(1) - 0xDC00;
24752 cc = (high * 0x400) + low + 0x10000;
24754 (cc >= 0x4E00 && cc < 0xA000 ) ||
24755 (cc >= 0x3400 && cc < 0x4E00 ) ||
24756 (cc >= 0xf900 && cc < 0xfb00 )
24761 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24762 return "&#" + cc + ";";
24769 if(this.owner.fireEvent('beforesync', this, html) !== false){
24770 this.el.dom.value = html;
24771 this.owner.fireEvent('sync', this, html);
24777 * Protected method that will not generally be called directly. Pushes the value of the textarea
24778 * into the iframe editor.
24780 pushValue : function(){
24781 if(this.initialized){
24782 var v = this.el.dom.value.trim();
24784 // if(v.length < 1){
24788 if(this.owner.fireEvent('beforepush', this, v) !== false){
24789 var d = (this.doc.body || this.doc.documentElement);
24791 this.cleanUpPaste();
24792 this.el.dom.value = d.innerHTML;
24793 this.owner.fireEvent('push', this, v);
24799 deferFocus : function(){
24800 this.focus.defer(10, this);
24804 focus : function(){
24805 if(this.win && !this.sourceEditMode){
24812 assignDocWin: function()
24814 var iframe = this.iframe;
24817 this.doc = iframe.contentWindow.document;
24818 this.win = iframe.contentWindow;
24820 // if (!Roo.get(this.frameId)) {
24823 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24824 // this.win = Roo.get(this.frameId).dom.contentWindow;
24826 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24830 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24831 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24836 initEditor : function(){
24837 //console.log("INIT EDITOR");
24838 this.assignDocWin();
24842 this.doc.designMode="on";
24844 this.doc.write(this.getDocMarkup());
24847 var dbody = (this.doc.body || this.doc.documentElement);
24848 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24849 // this copies styles from the containing element into thsi one..
24850 // not sure why we need all of this..
24851 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24853 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24854 //ss['background-attachment'] = 'fixed'; // w3c
24855 dbody.bgProperties = 'fixed'; // ie
24856 //Roo.DomHelper.applyStyles(dbody, ss);
24857 Roo.EventManager.on(this.doc, {
24858 //'mousedown': this.onEditorEvent,
24859 'mouseup': this.onEditorEvent,
24860 'dblclick': this.onEditorEvent,
24861 'click': this.onEditorEvent,
24862 'keyup': this.onEditorEvent,
24867 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24869 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24870 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24872 this.initialized = true;
24874 this.owner.fireEvent('initialize', this);
24879 onDestroy : function(){
24885 //for (var i =0; i < this.toolbars.length;i++) {
24886 // // fixme - ask toolbars for heights?
24887 // this.toolbars[i].onDestroy();
24890 //this.wrap.dom.innerHTML = '';
24891 //this.wrap.remove();
24896 onFirstFocus : function(){
24898 this.assignDocWin();
24901 this.activated = true;
24904 if(Roo.isGecko){ // prevent silly gecko errors
24906 var s = this.win.getSelection();
24907 if(!s.focusNode || s.focusNode.nodeType != 3){
24908 var r = s.getRangeAt(0);
24909 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24914 this.execCmd('useCSS', true);
24915 this.execCmd('styleWithCSS', false);
24918 this.owner.fireEvent('activate', this);
24922 adjustFont: function(btn){
24923 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24924 //if(Roo.isSafari){ // safari
24927 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24928 if(Roo.isSafari){ // safari
24929 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24930 v = (v < 10) ? 10 : v;
24931 v = (v > 48) ? 48 : v;
24932 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24937 v = Math.max(1, v+adjust);
24939 this.execCmd('FontSize', v );
24942 onEditorEvent : function(e)
24944 this.owner.fireEvent('editorevent', this, e);
24945 // this.updateToolbar();
24946 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24949 insertTag : function(tg)
24951 // could be a bit smarter... -> wrap the current selected tRoo..
24952 if (tg.toLowerCase() == 'span' ||
24953 tg.toLowerCase() == 'code' ||
24954 tg.toLowerCase() == 'sup' ||
24955 tg.toLowerCase() == 'sub'
24958 range = this.createRange(this.getSelection());
24959 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24960 wrappingNode.appendChild(range.extractContents());
24961 range.insertNode(wrappingNode);
24968 this.execCmd("formatblock", tg);
24972 insertText : function(txt)
24976 var range = this.createRange();
24977 range.deleteContents();
24978 //alert(Sender.getAttribute('label'));
24980 range.insertNode(this.doc.createTextNode(txt));
24986 * Executes a Midas editor command on the editor document and performs necessary focus and
24987 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24988 * @param {String} cmd The Midas command
24989 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24991 relayCmd : function(cmd, value){
24993 this.execCmd(cmd, value);
24994 this.owner.fireEvent('editorevent', this);
24995 //this.updateToolbar();
24996 this.owner.deferFocus();
25000 * Executes a Midas editor command directly on the editor document.
25001 * For visual commands, you should use {@link #relayCmd} instead.
25002 * <b>This should only be called after the editor is initialized.</b>
25003 * @param {String} cmd The Midas command
25004 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25006 execCmd : function(cmd, value){
25007 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25014 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25016 * @param {String} text | dom node..
25018 insertAtCursor : function(text)
25021 if(!this.activated){
25027 var r = this.doc.selection.createRange();
25038 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25042 // from jquery ui (MIT licenced)
25044 var win = this.win;
25046 if (win.getSelection && win.getSelection().getRangeAt) {
25047 range = win.getSelection().getRangeAt(0);
25048 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25049 range.insertNode(node);
25050 } else if (win.document.selection && win.document.selection.createRange) {
25051 // no firefox support
25052 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25053 win.document.selection.createRange().pasteHTML(txt);
25055 // no firefox support
25056 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25057 this.execCmd('InsertHTML', txt);
25066 mozKeyPress : function(e){
25068 var c = e.getCharCode(), cmd;
25071 c = String.fromCharCode(c).toLowerCase();
25085 this.cleanUpPaste.defer(100, this);
25093 e.preventDefault();
25101 fixKeys : function(){ // load time branching for fastest keydown performance
25103 return function(e){
25104 var k = e.getKey(), r;
25107 r = this.doc.selection.createRange();
25110 r.pasteHTML('    ');
25117 r = this.doc.selection.createRange();
25119 var target = r.parentElement();
25120 if(!target || target.tagName.toLowerCase() != 'li'){
25122 r.pasteHTML('<br />');
25128 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25129 this.cleanUpPaste.defer(100, this);
25135 }else if(Roo.isOpera){
25136 return function(e){
25137 var k = e.getKey();
25141 this.execCmd('InsertHTML','    ');
25144 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25145 this.cleanUpPaste.defer(100, this);
25150 }else if(Roo.isSafari){
25151 return function(e){
25152 var k = e.getKey();
25156 this.execCmd('InsertText','\t');
25160 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25161 this.cleanUpPaste.defer(100, this);
25169 getAllAncestors: function()
25171 var p = this.getSelectedNode();
25174 a.push(p); // push blank onto stack..
25175 p = this.getParentElement();
25179 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25183 a.push(this.doc.body);
25187 lastSelNode : false,
25190 getSelection : function()
25192 this.assignDocWin();
25193 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25196 getSelectedNode: function()
25198 // this may only work on Gecko!!!
25200 // should we cache this!!!!
25205 var range = this.createRange(this.getSelection()).cloneRange();
25208 var parent = range.parentElement();
25210 var testRange = range.duplicate();
25211 testRange.moveToElementText(parent);
25212 if (testRange.inRange(range)) {
25215 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25218 parent = parent.parentElement;
25223 // is ancestor a text element.
25224 var ac = range.commonAncestorContainer;
25225 if (ac.nodeType == 3) {
25226 ac = ac.parentNode;
25229 var ar = ac.childNodes;
25232 var other_nodes = [];
25233 var has_other_nodes = false;
25234 for (var i=0;i<ar.length;i++) {
25235 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25238 // fullly contained node.
25240 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25245 // probably selected..
25246 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25247 other_nodes.push(ar[i]);
25251 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25256 has_other_nodes = true;
25258 if (!nodes.length && other_nodes.length) {
25259 nodes= other_nodes;
25261 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25267 createRange: function(sel)
25269 // this has strange effects when using with
25270 // top toolbar - not sure if it's a great idea.
25271 //this.editor.contentWindow.focus();
25272 if (typeof sel != "undefined") {
25274 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25276 return this.doc.createRange();
25279 return this.doc.createRange();
25282 getParentElement: function()
25285 this.assignDocWin();
25286 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25288 var range = this.createRange(sel);
25291 var p = range.commonAncestorContainer;
25292 while (p.nodeType == 3) { // text node
25303 * Range intersection.. the hard stuff...
25307 * [ -- selected range --- ]
25311 * if end is before start or hits it. fail.
25312 * if start is after end or hits it fail.
25314 * if either hits (but other is outside. - then it's not
25320 // @see http://www.thismuchiknow.co.uk/?p=64.
25321 rangeIntersectsNode : function(range, node)
25323 var nodeRange = node.ownerDocument.createRange();
25325 nodeRange.selectNode(node);
25327 nodeRange.selectNodeContents(node);
25330 var rangeStartRange = range.cloneRange();
25331 rangeStartRange.collapse(true);
25333 var rangeEndRange = range.cloneRange();
25334 rangeEndRange.collapse(false);
25336 var nodeStartRange = nodeRange.cloneRange();
25337 nodeStartRange.collapse(true);
25339 var nodeEndRange = nodeRange.cloneRange();
25340 nodeEndRange.collapse(false);
25342 return rangeStartRange.compareBoundaryPoints(
25343 Range.START_TO_START, nodeEndRange) == -1 &&
25344 rangeEndRange.compareBoundaryPoints(
25345 Range.START_TO_START, nodeStartRange) == 1;
25349 rangeCompareNode : function(range, node)
25351 var nodeRange = node.ownerDocument.createRange();
25353 nodeRange.selectNode(node);
25355 nodeRange.selectNodeContents(node);
25359 range.collapse(true);
25361 nodeRange.collapse(true);
25363 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25364 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25366 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25368 var nodeIsBefore = ss == 1;
25369 var nodeIsAfter = ee == -1;
25371 if (nodeIsBefore && nodeIsAfter) {
25374 if (!nodeIsBefore && nodeIsAfter) {
25375 return 1; //right trailed.
25378 if (nodeIsBefore && !nodeIsAfter) {
25379 return 2; // left trailed.
25385 // private? - in a new class?
25386 cleanUpPaste : function()
25388 // cleans up the whole document..
25389 Roo.log('cleanuppaste');
25391 this.cleanUpChildren(this.doc.body);
25392 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25393 if (clean != this.doc.body.innerHTML) {
25394 this.doc.body.innerHTML = clean;
25399 cleanWordChars : function(input) {// change the chars to hex code
25400 var he = Roo.HtmlEditorCore;
25402 var output = input;
25403 Roo.each(he.swapCodes, function(sw) {
25404 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25406 output = output.replace(swapper, sw[1]);
25413 cleanUpChildren : function (n)
25415 if (!n.childNodes.length) {
25418 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25419 this.cleanUpChild(n.childNodes[i]);
25426 cleanUpChild : function (node)
25429 //console.log(node);
25430 if (node.nodeName == "#text") {
25431 // clean up silly Windows -- stuff?
25434 if (node.nodeName == "#comment") {
25435 node.parentNode.removeChild(node);
25436 // clean up silly Windows -- stuff?
25439 var lcname = node.tagName.toLowerCase();
25440 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25441 // whitelist of tags..
25443 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25445 node.parentNode.removeChild(node);
25450 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25452 // spans with no attributes - just remove them..
25453 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25454 remove_keep_children = true;
25457 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25458 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25460 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25461 // remove_keep_children = true;
25464 if (remove_keep_children) {
25465 this.cleanUpChildren(node);
25466 // inserts everything just before this node...
25467 while (node.childNodes.length) {
25468 var cn = node.childNodes[0];
25469 node.removeChild(cn);
25470 node.parentNode.insertBefore(cn, node);
25472 node.parentNode.removeChild(node);
25476 if (!node.attributes || !node.attributes.length) {
25481 this.cleanUpChildren(node);
25485 function cleanAttr(n,v)
25488 if (v.match(/^\./) || v.match(/^\//)) {
25491 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25494 if (v.match(/^#/)) {
25497 if (v.match(/^\{/)) { // allow template editing.
25500 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25501 node.removeAttribute(n);
25505 var cwhite = this.cwhite;
25506 var cblack = this.cblack;
25508 function cleanStyle(n,v)
25510 if (v.match(/expression/)) { //XSS?? should we even bother..
25511 node.removeAttribute(n);
25515 var parts = v.split(/;/);
25518 Roo.each(parts, function(p) {
25519 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25523 var l = p.split(':').shift().replace(/\s+/g,'');
25524 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25526 if ( cwhite.length && cblack.indexOf(l) > -1) {
25527 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25528 //node.removeAttribute(n);
25532 // only allow 'c whitelisted system attributes'
25533 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25534 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25535 //node.removeAttribute(n);
25545 if (clean.length) {
25546 node.setAttribute(n, clean.join(';'));
25548 node.removeAttribute(n);
25554 for (var i = node.attributes.length-1; i > -1 ; i--) {
25555 var a = node.attributes[i];
25558 if (a.name.toLowerCase().substr(0,2)=='on') {
25559 node.removeAttribute(a.name);
25562 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25563 node.removeAttribute(a.name);
25566 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25567 cleanAttr(a.name,a.value); // fixme..
25570 if (a.name == 'style') {
25571 cleanStyle(a.name,a.value);
25574 /// clean up MS crap..
25575 // tecnically this should be a list of valid class'es..
25578 if (a.name == 'class') {
25579 if (a.value.match(/^Mso/)) {
25580 node.removeAttribute('class');
25583 if (a.value.match(/^body$/)) {
25584 node.removeAttribute('class');
25595 this.cleanUpChildren(node);
25601 * Clean up MS wordisms...
25603 cleanWord : function(node)
25606 this.cleanWord(this.doc.body);
25611 node.nodeName == 'SPAN' &&
25612 !node.hasAttributes() &&
25613 node.childNodes.length == 1 &&
25614 node.firstChild.nodeName == "#text"
25616 var textNode = node.firstChild;
25617 node.removeChild(textNode);
25618 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25619 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25621 node.parentNode.insertBefore(textNode, node);
25622 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25623 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25625 node.parentNode.removeChild(node);
25628 if (node.nodeName == "#text") {
25629 // clean up silly Windows -- stuff?
25632 if (node.nodeName == "#comment") {
25633 node.parentNode.removeChild(node);
25634 // clean up silly Windows -- stuff?
25638 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25639 node.parentNode.removeChild(node);
25642 //Roo.log(node.tagName);
25643 // remove - but keep children..
25644 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25645 //Roo.log('-- removed');
25646 while (node.childNodes.length) {
25647 var cn = node.childNodes[0];
25648 node.removeChild(cn);
25649 node.parentNode.insertBefore(cn, node);
25650 // move node to parent - and clean it..
25651 this.cleanWord(cn);
25653 node.parentNode.removeChild(node);
25654 /// no need to iterate chidlren = it's got none..
25655 //this.iterateChildren(node, this.cleanWord);
25659 if (node.className.length) {
25661 var cn = node.className.split(/\W+/);
25663 Roo.each(cn, function(cls) {
25664 if (cls.match(/Mso[a-zA-Z]+/)) {
25669 node.className = cna.length ? cna.join(' ') : '';
25671 node.removeAttribute("class");
25675 if (node.hasAttribute("lang")) {
25676 node.removeAttribute("lang");
25679 if (node.hasAttribute("style")) {
25681 var styles = node.getAttribute("style").split(";");
25683 Roo.each(styles, function(s) {
25684 if (!s.match(/:/)) {
25687 var kv = s.split(":");
25688 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25691 // what ever is left... we allow.
25694 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25695 if (!nstyle.length) {
25696 node.removeAttribute('style');
25699 this.iterateChildren(node, this.cleanWord);
25705 * iterateChildren of a Node, calling fn each time, using this as the scole..
25706 * @param {DomNode} node node to iterate children of.
25707 * @param {Function} fn method of this class to call on each item.
25709 iterateChildren : function(node, fn)
25711 if (!node.childNodes.length) {
25714 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25715 fn.call(this, node.childNodes[i])
25721 * cleanTableWidths.
25723 * Quite often pasting from word etc.. results in tables with column and widths.
25724 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25727 cleanTableWidths : function(node)
25732 this.cleanTableWidths(this.doc.body);
25737 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25740 Roo.log(node.tagName);
25741 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25742 this.iterateChildren(node, this.cleanTableWidths);
25745 if (node.hasAttribute('width')) {
25746 node.removeAttribute('width');
25750 if (node.hasAttribute("style")) {
25753 var styles = node.getAttribute("style").split(";");
25755 Roo.each(styles, function(s) {
25756 if (!s.match(/:/)) {
25759 var kv = s.split(":");
25760 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25763 // what ever is left... we allow.
25766 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25767 if (!nstyle.length) {
25768 node.removeAttribute('style');
25772 this.iterateChildren(node, this.cleanTableWidths);
25780 domToHTML : function(currentElement, depth, nopadtext) {
25782 depth = depth || 0;
25783 nopadtext = nopadtext || false;
25785 if (!currentElement) {
25786 return this.domToHTML(this.doc.body);
25789 //Roo.log(currentElement);
25791 var allText = false;
25792 var nodeName = currentElement.nodeName;
25793 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25795 if (nodeName == '#text') {
25797 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25802 if (nodeName != 'BODY') {
25805 // Prints the node tagName, such as <A>, <IMG>, etc
25808 for(i = 0; i < currentElement.attributes.length;i++) {
25810 var aname = currentElement.attributes.item(i).name;
25811 if (!currentElement.attributes.item(i).value.length) {
25814 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25817 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25826 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25829 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25834 // Traverse the tree
25836 var currentElementChild = currentElement.childNodes.item(i);
25837 var allText = true;
25838 var innerHTML = '';
25840 while (currentElementChild) {
25841 // Formatting code (indent the tree so it looks nice on the screen)
25842 var nopad = nopadtext;
25843 if (lastnode == 'SPAN') {
25847 if (currentElementChild.nodeName == '#text') {
25848 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25849 toadd = nopadtext ? toadd : toadd.trim();
25850 if (!nopad && toadd.length > 80) {
25851 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25853 innerHTML += toadd;
25856 currentElementChild = currentElement.childNodes.item(i);
25862 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25864 // Recursively traverse the tree structure of the child node
25865 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25866 lastnode = currentElementChild.nodeName;
25868 currentElementChild=currentElement.childNodes.item(i);
25874 // The remaining code is mostly for formatting the tree
25875 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25880 ret+= "</"+tagName+">";
25886 applyBlacklists : function()
25888 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25889 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25893 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25894 if (b.indexOf(tag) > -1) {
25897 this.white.push(tag);
25901 Roo.each(w, function(tag) {
25902 if (b.indexOf(tag) > -1) {
25905 if (this.white.indexOf(tag) > -1) {
25908 this.white.push(tag);
25913 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25914 if (w.indexOf(tag) > -1) {
25917 this.black.push(tag);
25921 Roo.each(b, function(tag) {
25922 if (w.indexOf(tag) > -1) {
25925 if (this.black.indexOf(tag) > -1) {
25928 this.black.push(tag);
25933 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25934 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25938 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25939 if (b.indexOf(tag) > -1) {
25942 this.cwhite.push(tag);
25946 Roo.each(w, function(tag) {
25947 if (b.indexOf(tag) > -1) {
25950 if (this.cwhite.indexOf(tag) > -1) {
25953 this.cwhite.push(tag);
25958 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25959 if (w.indexOf(tag) > -1) {
25962 this.cblack.push(tag);
25966 Roo.each(b, function(tag) {
25967 if (w.indexOf(tag) > -1) {
25970 if (this.cblack.indexOf(tag) > -1) {
25973 this.cblack.push(tag);
25978 setStylesheets : function(stylesheets)
25980 if(typeof(stylesheets) == 'string'){
25981 Roo.get(this.iframe.contentDocument.head).createChild({
25983 rel : 'stylesheet',
25992 Roo.each(stylesheets, function(s) {
25997 Roo.get(_this.iframe.contentDocument.head).createChild({
25999 rel : 'stylesheet',
26008 removeStylesheets : function()
26012 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26017 setStyle : function(style)
26019 Roo.get(this.iframe.contentDocument.head).createChild({
26028 // hide stuff that is not compatible
26042 * @event specialkey
26046 * @cfg {String} fieldClass @hide
26049 * @cfg {String} focusClass @hide
26052 * @cfg {String} autoCreate @hide
26055 * @cfg {String} inputType @hide
26058 * @cfg {String} invalidClass @hide
26061 * @cfg {String} invalidText @hide
26064 * @cfg {String} msgFx @hide
26067 * @cfg {String} validateOnBlur @hide
26071 Roo.HtmlEditorCore.white = [
26072 'area', 'br', 'img', 'input', 'hr', 'wbr',
26074 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26075 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26076 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26077 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26078 'table', 'ul', 'xmp',
26080 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26083 'dir', 'menu', 'ol', 'ul', 'dl',
26089 Roo.HtmlEditorCore.black = [
26090 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26092 'base', 'basefont', 'bgsound', 'blink', 'body',
26093 'frame', 'frameset', 'head', 'html', 'ilayer',
26094 'iframe', 'layer', 'link', 'meta', 'object',
26095 'script', 'style' ,'title', 'xml' // clean later..
26097 Roo.HtmlEditorCore.clean = [
26098 'script', 'style', 'title', 'xml'
26100 Roo.HtmlEditorCore.remove = [
26105 Roo.HtmlEditorCore.ablack = [
26109 Roo.HtmlEditorCore.aclean = [
26110 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26114 Roo.HtmlEditorCore.pwhite= [
26115 'http', 'https', 'mailto'
26118 // white listed style attributes.
26119 Roo.HtmlEditorCore.cwhite= [
26120 // 'text-align', /// default is to allow most things..
26126 // black listed style attributes.
26127 Roo.HtmlEditorCore.cblack= [
26128 // 'font-size' -- this can be set by the project
26132 Roo.HtmlEditorCore.swapCodes =[
26133 [ 8211, "–" ],
26134 [ 8212, "—" ],
26151 * @class Roo.bootstrap.HtmlEditor
26152 * @extends Roo.bootstrap.TextArea
26153 * Bootstrap HtmlEditor class
26156 * Create a new HtmlEditor
26157 * @param {Object} config The config object
26160 Roo.bootstrap.HtmlEditor = function(config){
26161 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26162 if (!this.toolbars) {
26163 this.toolbars = [];
26166 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26169 * @event initialize
26170 * Fires when the editor is fully initialized (including the iframe)
26171 * @param {HtmlEditor} this
26176 * Fires when the editor is first receives the focus. Any insertion must wait
26177 * until after this event.
26178 * @param {HtmlEditor} this
26182 * @event beforesync
26183 * Fires before the textarea is updated with content from the editor iframe. Return false
26184 * to cancel the sync.
26185 * @param {HtmlEditor} this
26186 * @param {String} html
26190 * @event beforepush
26191 * Fires before the iframe editor is updated with content from the textarea. Return false
26192 * to cancel the push.
26193 * @param {HtmlEditor} this
26194 * @param {String} html
26199 * Fires when the textarea is updated with content from the editor iframe.
26200 * @param {HtmlEditor} this
26201 * @param {String} html
26206 * Fires when the iframe editor is updated with content from the textarea.
26207 * @param {HtmlEditor} this
26208 * @param {String} html
26212 * @event editmodechange
26213 * Fires when the editor switches edit modes
26214 * @param {HtmlEditor} this
26215 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26217 editmodechange: true,
26219 * @event editorevent
26220 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26221 * @param {HtmlEditor} this
26225 * @event firstfocus
26226 * Fires when on first focus - needed by toolbars..
26227 * @param {HtmlEditor} this
26232 * Auto save the htmlEditor value as a file into Events
26233 * @param {HtmlEditor} this
26237 * @event savedpreview
26238 * preview the saved version of htmlEditor
26239 * @param {HtmlEditor} this
26246 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26250 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26255 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26260 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26265 * @cfg {Number} height (in pixels)
26269 * @cfg {Number} width (in pixels)
26274 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26277 stylesheets: false,
26282 // private properties
26283 validationEvent : false,
26285 initialized : false,
26288 onFocus : Roo.emptyFn,
26290 hideMode:'offsets',
26292 tbContainer : false,
26296 toolbarContainer :function() {
26297 return this.wrap.select('.x-html-editor-tb',true).first();
26301 * Protected method that will not generally be called directly. It
26302 * is called when the editor creates its toolbar. Override this method if you need to
26303 * add custom toolbar buttons.
26304 * @param {HtmlEditor} editor
26306 createToolbar : function(){
26307 Roo.log('renewing');
26308 Roo.log("create toolbars");
26310 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26311 this.toolbars[0].render(this.toolbarContainer());
26315 // if (!editor.toolbars || !editor.toolbars.length) {
26316 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26319 // for (var i =0 ; i < editor.toolbars.length;i++) {
26320 // editor.toolbars[i] = Roo.factory(
26321 // typeof(editor.toolbars[i]) == 'string' ?
26322 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26323 // Roo.bootstrap.HtmlEditor);
26324 // editor.toolbars[i].init(editor);
26330 onRender : function(ct, position)
26332 // Roo.log("Call onRender: " + this.xtype);
26334 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26336 this.wrap = this.inputEl().wrap({
26337 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26340 this.editorcore.onRender(ct, position);
26342 if (this.resizable) {
26343 this.resizeEl = new Roo.Resizable(this.wrap, {
26347 minHeight : this.height,
26348 height: this.height,
26349 handles : this.resizable,
26352 resize : function(r, w, h) {
26353 _t.onResize(w,h); // -something
26359 this.createToolbar(this);
26362 if(!this.width && this.resizable){
26363 this.setSize(this.wrap.getSize());
26365 if (this.resizeEl) {
26366 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26367 // should trigger onReize..
26373 onResize : function(w, h)
26375 Roo.log('resize: ' +w + ',' + h );
26376 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26380 if(this.inputEl() ){
26381 if(typeof w == 'number'){
26382 var aw = w - this.wrap.getFrameWidth('lr');
26383 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26386 if(typeof h == 'number'){
26387 var tbh = -11; // fixme it needs to tool bar size!
26388 for (var i =0; i < this.toolbars.length;i++) {
26389 // fixme - ask toolbars for heights?
26390 tbh += this.toolbars[i].el.getHeight();
26391 //if (this.toolbars[i].footer) {
26392 // tbh += this.toolbars[i].footer.el.getHeight();
26400 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26401 ah -= 5; // knock a few pixes off for look..
26402 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26406 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26407 this.editorcore.onResize(ew,eh);
26412 * Toggles the editor between standard and source edit mode.
26413 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26415 toggleSourceEdit : function(sourceEditMode)
26417 this.editorcore.toggleSourceEdit(sourceEditMode);
26419 if(this.editorcore.sourceEditMode){
26420 Roo.log('editor - showing textarea');
26423 // Roo.log(this.syncValue());
26425 this.inputEl().removeClass(['hide', 'x-hidden']);
26426 this.inputEl().dom.removeAttribute('tabIndex');
26427 this.inputEl().focus();
26429 Roo.log('editor - hiding textarea');
26431 // Roo.log(this.pushValue());
26434 this.inputEl().addClass(['hide', 'x-hidden']);
26435 this.inputEl().dom.setAttribute('tabIndex', -1);
26436 //this.deferFocus();
26439 if(this.resizable){
26440 this.setSize(this.wrap.getSize());
26443 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26446 // private (for BoxComponent)
26447 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26449 // private (for BoxComponent)
26450 getResizeEl : function(){
26454 // private (for BoxComponent)
26455 getPositionEl : function(){
26460 initEvents : function(){
26461 this.originalValue = this.getValue();
26465 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26468 // markInvalid : Roo.emptyFn,
26470 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26473 // clearInvalid : Roo.emptyFn,
26475 setValue : function(v){
26476 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26477 this.editorcore.pushValue();
26482 deferFocus : function(){
26483 this.focus.defer(10, this);
26487 focus : function(){
26488 this.editorcore.focus();
26494 onDestroy : function(){
26500 for (var i =0; i < this.toolbars.length;i++) {
26501 // fixme - ask toolbars for heights?
26502 this.toolbars[i].onDestroy();
26505 this.wrap.dom.innerHTML = '';
26506 this.wrap.remove();
26511 onFirstFocus : function(){
26512 //Roo.log("onFirstFocus");
26513 this.editorcore.onFirstFocus();
26514 for (var i =0; i < this.toolbars.length;i++) {
26515 this.toolbars[i].onFirstFocus();
26521 syncValue : function()
26523 this.editorcore.syncValue();
26526 pushValue : function()
26528 this.editorcore.pushValue();
26532 // hide stuff that is not compatible
26546 * @event specialkey
26550 * @cfg {String} fieldClass @hide
26553 * @cfg {String} focusClass @hide
26556 * @cfg {String} autoCreate @hide
26559 * @cfg {String} inputType @hide
26563 * @cfg {String} invalidText @hide
26566 * @cfg {String} msgFx @hide
26569 * @cfg {String} validateOnBlur @hide
26578 Roo.namespace('Roo.bootstrap.htmleditor');
26580 * @class Roo.bootstrap.HtmlEditorToolbar1
26586 new Roo.bootstrap.HtmlEditor({
26589 new Roo.bootstrap.HtmlEditorToolbar1({
26590 disable : { fonts: 1 , format: 1, ..., ... , ...],
26596 * @cfg {Object} disable List of elements to disable..
26597 * @cfg {Array} btns List of additional buttons.
26601 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26604 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26607 Roo.apply(this, config);
26609 // default disabled, based on 'good practice'..
26610 this.disable = this.disable || {};
26611 Roo.applyIf(this.disable, {
26614 specialElements : true
26616 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26618 this.editor = config.editor;
26619 this.editorcore = config.editor.editorcore;
26621 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26623 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26624 // dont call parent... till later.
26626 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26631 editorcore : false,
26636 "h1","h2","h3","h4","h5","h6",
26638 "abbr", "acronym", "address", "cite", "samp", "var",
26642 onRender : function(ct, position)
26644 // Roo.log("Call onRender: " + this.xtype);
26646 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26648 this.el.dom.style.marginBottom = '0';
26650 var editorcore = this.editorcore;
26651 var editor= this.editor;
26654 var btn = function(id,cmd , toggle, handler, html){
26656 var event = toggle ? 'toggle' : 'click';
26661 xns: Roo.bootstrap,
26665 enableToggle:toggle !== false,
26667 pressed : toggle ? false : null,
26670 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26671 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26677 // var cb_box = function...
26682 xns: Roo.bootstrap,
26687 xns: Roo.bootstrap,
26691 Roo.each(this.formats, function(f) {
26692 style.menu.items.push({
26694 xns: Roo.bootstrap,
26695 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26700 editorcore.insertTag(this.tagname);
26707 children.push(style);
26709 btn('bold',false,true);
26710 btn('italic',false,true);
26711 btn('align-left', 'justifyleft',true);
26712 btn('align-center', 'justifycenter',true);
26713 btn('align-right' , 'justifyright',true);
26714 btn('link', false, false, function(btn) {
26715 //Roo.log("create link?");
26716 var url = prompt(this.createLinkText, this.defaultLinkValue);
26717 if(url && url != 'http:/'+'/'){
26718 this.editorcore.relayCmd('createlink', url);
26721 btn('list','insertunorderedlist',true);
26722 btn('pencil', false,true, function(btn){
26724 this.toggleSourceEdit(btn.pressed);
26727 if (this.editor.btns.length > 0) {
26728 for (var i = 0; i<this.editor.btns.length; i++) {
26729 children.push(this.editor.btns[i]);
26737 xns: Roo.bootstrap,
26742 xns: Roo.bootstrap,
26747 cog.menu.items.push({
26749 xns: Roo.bootstrap,
26750 html : Clean styles,
26755 editorcore.insertTag(this.tagname);
26764 this.xtype = 'NavSimplebar';
26766 for(var i=0;i< children.length;i++) {
26768 this.buttons.add(this.addxtypeChild(children[i]));
26772 editor.on('editorevent', this.updateToolbar, this);
26774 onBtnClick : function(id)
26776 this.editorcore.relayCmd(id);
26777 this.editorcore.focus();
26781 * Protected method that will not generally be called directly. It triggers
26782 * a toolbar update by reading the markup state of the current selection in the editor.
26784 updateToolbar: function(){
26786 if(!this.editorcore.activated){
26787 this.editor.onFirstFocus(); // is this neeed?
26791 var btns = this.buttons;
26792 var doc = this.editorcore.doc;
26793 btns.get('bold').setActive(doc.queryCommandState('bold'));
26794 btns.get('italic').setActive(doc.queryCommandState('italic'));
26795 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26797 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26798 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26799 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26801 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26802 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26805 var ans = this.editorcore.getAllAncestors();
26806 if (this.formatCombo) {
26809 var store = this.formatCombo.store;
26810 this.formatCombo.setValue("");
26811 for (var i =0; i < ans.length;i++) {
26812 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26814 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26822 // hides menus... - so this cant be on a menu...
26823 Roo.bootstrap.MenuMgr.hideAll();
26825 Roo.bootstrap.MenuMgr.hideAll();
26826 //this.editorsyncValue();
26828 onFirstFocus: function() {
26829 this.buttons.each(function(item){
26833 toggleSourceEdit : function(sourceEditMode){
26836 if(sourceEditMode){
26837 Roo.log("disabling buttons");
26838 this.buttons.each( function(item){
26839 if(item.cmd != 'pencil'){
26845 Roo.log("enabling buttons");
26846 if(this.editorcore.initialized){
26847 this.buttons.each( function(item){
26853 Roo.log("calling toggole on editor");
26854 // tell the editor that it's been pressed..
26855 this.editor.toggleSourceEdit(sourceEditMode);
26869 * @class Roo.bootstrap.Markdown
26870 * @extends Roo.bootstrap.TextArea
26871 * Bootstrap Showdown editable area
26872 * @cfg {string} content
26875 * Create a new Showdown
26878 Roo.bootstrap.Markdown = function(config){
26879 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26883 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26887 initEvents : function()
26890 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26891 this.markdownEl = this.el.createChild({
26892 cls : 'roo-markdown-area'
26894 this.inputEl().addClass('d-none');
26895 if (this.getValue() == '') {
26896 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26899 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26901 this.markdownEl.on('click', this.toggleTextEdit, this);
26902 this.on('blur', this.toggleTextEdit, this);
26903 this.on('specialkey', this.resizeTextArea, this);
26906 toggleTextEdit : function()
26908 var sh = this.markdownEl.getHeight();
26909 this.inputEl().addClass('d-none');
26910 this.markdownEl.addClass('d-none');
26911 if (!this.editing) {
26913 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26914 this.inputEl().removeClass('d-none');
26915 this.inputEl().focus();
26916 this.editing = true;
26919 // show showdown...
26920 this.updateMarkdown();
26921 this.markdownEl.removeClass('d-none');
26922 this.editing = false;
26925 updateMarkdown : function()
26927 if (this.getValue() == '') {
26928 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26932 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26935 resizeTextArea: function () {
26938 Roo.log([sh, this.getValue().split("\n").length * 30]);
26939 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26941 setValue : function(val)
26943 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26944 if (!this.editing) {
26945 this.updateMarkdown();
26951 if (!this.editing) {
26952 this.toggleTextEdit();
26960 * @class Roo.bootstrap.Table.AbstractSelectionModel
26961 * @extends Roo.util.Observable
26962 * Abstract base class for grid SelectionModels. It provides the interface that should be
26963 * implemented by descendant classes. This class should not be directly instantiated.
26966 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26967 this.locked = false;
26968 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26972 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26973 /** @ignore Called by the grid automatically. Do not call directly. */
26974 init : function(grid){
26980 * Locks the selections.
26983 this.locked = true;
26987 * Unlocks the selections.
26989 unlock : function(){
26990 this.locked = false;
26994 * Returns true if the selections are locked.
26995 * @return {Boolean}
26997 isLocked : function(){
26998 return this.locked;
27002 initEvents : function ()
27008 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27009 * @class Roo.bootstrap.Table.RowSelectionModel
27010 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27011 * It supports multiple selections and keyboard selection/navigation.
27013 * @param {Object} config
27016 Roo.bootstrap.Table.RowSelectionModel = function(config){
27017 Roo.apply(this, config);
27018 this.selections = new Roo.util.MixedCollection(false, function(o){
27023 this.lastActive = false;
27027 * @event selectionchange
27028 * Fires when the selection changes
27029 * @param {SelectionModel} this
27031 "selectionchange" : true,
27033 * @event afterselectionchange
27034 * Fires after the selection changes (eg. by key press or clicking)
27035 * @param {SelectionModel} this
27037 "afterselectionchange" : true,
27039 * @event beforerowselect
27040 * Fires when a row is selected being selected, return false to cancel.
27041 * @param {SelectionModel} this
27042 * @param {Number} rowIndex The selected index
27043 * @param {Boolean} keepExisting False if other selections will be cleared
27045 "beforerowselect" : true,
27048 * Fires when a row is selected.
27049 * @param {SelectionModel} this
27050 * @param {Number} rowIndex The selected index
27051 * @param {Roo.data.Record} r The record
27053 "rowselect" : true,
27055 * @event rowdeselect
27056 * Fires when a row is deselected.
27057 * @param {SelectionModel} this
27058 * @param {Number} rowIndex The selected index
27060 "rowdeselect" : true
27062 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27063 this.locked = false;
27066 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27068 * @cfg {Boolean} singleSelect
27069 * True to allow selection of only one row at a time (defaults to false)
27071 singleSelect : false,
27074 initEvents : function()
27077 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27078 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27079 //}else{ // allow click to work like normal
27080 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27082 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27083 this.grid.on("rowclick", this.handleMouseDown, this);
27085 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27086 "up" : function(e){
27088 this.selectPrevious(e.shiftKey);
27089 }else if(this.last !== false && this.lastActive !== false){
27090 var last = this.last;
27091 this.selectRange(this.last, this.lastActive-1);
27092 this.grid.getView().focusRow(this.lastActive);
27093 if(last !== false){
27097 this.selectFirstRow();
27099 this.fireEvent("afterselectionchange", this);
27101 "down" : function(e){
27103 this.selectNext(e.shiftKey);
27104 }else if(this.last !== false && this.lastActive !== false){
27105 var last = this.last;
27106 this.selectRange(this.last, this.lastActive+1);
27107 this.grid.getView().focusRow(this.lastActive);
27108 if(last !== false){
27112 this.selectFirstRow();
27114 this.fireEvent("afterselectionchange", this);
27118 this.grid.store.on('load', function(){
27119 this.selections.clear();
27122 var view = this.grid.view;
27123 view.on("refresh", this.onRefresh, this);
27124 view.on("rowupdated", this.onRowUpdated, this);
27125 view.on("rowremoved", this.onRemove, this);
27130 onRefresh : function()
27132 var ds = this.grid.store, i, v = this.grid.view;
27133 var s = this.selections;
27134 s.each(function(r){
27135 if((i = ds.indexOfId(r.id)) != -1){
27144 onRemove : function(v, index, r){
27145 this.selections.remove(r);
27149 onRowUpdated : function(v, index, r){
27150 if(this.isSelected(r)){
27151 v.onRowSelect(index);
27157 * @param {Array} records The records to select
27158 * @param {Boolean} keepExisting (optional) True to keep existing selections
27160 selectRecords : function(records, keepExisting)
27163 this.clearSelections();
27165 var ds = this.grid.store;
27166 for(var i = 0, len = records.length; i < len; i++){
27167 this.selectRow(ds.indexOf(records[i]), true);
27172 * Gets the number of selected rows.
27175 getCount : function(){
27176 return this.selections.length;
27180 * Selects the first row in the grid.
27182 selectFirstRow : function(){
27187 * Select the last row.
27188 * @param {Boolean} keepExisting (optional) True to keep existing selections
27190 selectLastRow : function(keepExisting){
27191 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27192 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27196 * Selects the row immediately following the last selected row.
27197 * @param {Boolean} keepExisting (optional) True to keep existing selections
27199 selectNext : function(keepExisting)
27201 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27202 this.selectRow(this.last+1, keepExisting);
27203 this.grid.getView().focusRow(this.last);
27208 * Selects the row that precedes the last selected row.
27209 * @param {Boolean} keepExisting (optional) True to keep existing selections
27211 selectPrevious : function(keepExisting){
27213 this.selectRow(this.last-1, keepExisting);
27214 this.grid.getView().focusRow(this.last);
27219 * Returns the selected records
27220 * @return {Array} Array of selected records
27222 getSelections : function(){
27223 return [].concat(this.selections.items);
27227 * Returns the first selected record.
27230 getSelected : function(){
27231 return this.selections.itemAt(0);
27236 * Clears all selections.
27238 clearSelections : function(fast)
27244 var ds = this.grid.store;
27245 var s = this.selections;
27246 s.each(function(r){
27247 this.deselectRow(ds.indexOfId(r.id));
27251 this.selections.clear();
27258 * Selects all rows.
27260 selectAll : function(){
27264 this.selections.clear();
27265 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27266 this.selectRow(i, true);
27271 * Returns True if there is a selection.
27272 * @return {Boolean}
27274 hasSelection : function(){
27275 return this.selections.length > 0;
27279 * Returns True if the specified row is selected.
27280 * @param {Number/Record} record The record or index of the record to check
27281 * @return {Boolean}
27283 isSelected : function(index){
27284 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27285 return (r && this.selections.key(r.id) ? true : false);
27289 * Returns True if the specified record id is selected.
27290 * @param {String} id The id of record to check
27291 * @return {Boolean}
27293 isIdSelected : function(id){
27294 return (this.selections.key(id) ? true : false);
27299 handleMouseDBClick : function(e, t){
27303 handleMouseDown : function(e, t)
27305 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27306 if(this.isLocked() || rowIndex < 0 ){
27309 if(e.shiftKey && this.last !== false){
27310 var last = this.last;
27311 this.selectRange(last, rowIndex, e.ctrlKey);
27312 this.last = last; // reset the last
27316 var isSelected = this.isSelected(rowIndex);
27317 //Roo.log("select row:" + rowIndex);
27319 this.deselectRow(rowIndex);
27321 this.selectRow(rowIndex, true);
27325 if(e.button !== 0 && isSelected){
27326 alert('rowIndex 2: ' + rowIndex);
27327 view.focusRow(rowIndex);
27328 }else if(e.ctrlKey && isSelected){
27329 this.deselectRow(rowIndex);
27330 }else if(!isSelected){
27331 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27332 view.focusRow(rowIndex);
27336 this.fireEvent("afterselectionchange", this);
27339 handleDragableRowClick : function(grid, rowIndex, e)
27341 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27342 this.selectRow(rowIndex, false);
27343 grid.view.focusRow(rowIndex);
27344 this.fireEvent("afterselectionchange", this);
27349 * Selects multiple rows.
27350 * @param {Array} rows Array of the indexes of the row to select
27351 * @param {Boolean} keepExisting (optional) True to keep existing selections
27353 selectRows : function(rows, keepExisting){
27355 this.clearSelections();
27357 for(var i = 0, len = rows.length; i < len; i++){
27358 this.selectRow(rows[i], true);
27363 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27364 * @param {Number} startRow The index of the first row in the range
27365 * @param {Number} endRow The index of the last row in the range
27366 * @param {Boolean} keepExisting (optional) True to retain existing selections
27368 selectRange : function(startRow, endRow, keepExisting){
27373 this.clearSelections();
27375 if(startRow <= endRow){
27376 for(var i = startRow; i <= endRow; i++){
27377 this.selectRow(i, true);
27380 for(var i = startRow; i >= endRow; i--){
27381 this.selectRow(i, true);
27387 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27388 * @param {Number} startRow The index of the first row in the range
27389 * @param {Number} endRow The index of the last row in the range
27391 deselectRange : function(startRow, endRow, preventViewNotify){
27395 for(var i = startRow; i <= endRow; i++){
27396 this.deselectRow(i, preventViewNotify);
27402 * @param {Number} row The index of the row to select
27403 * @param {Boolean} keepExisting (optional) True to keep existing selections
27405 selectRow : function(index, keepExisting, preventViewNotify)
27407 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27410 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27411 if(!keepExisting || this.singleSelect){
27412 this.clearSelections();
27415 var r = this.grid.store.getAt(index);
27416 //console.log('selectRow - record id :' + r.id);
27418 this.selections.add(r);
27419 this.last = this.lastActive = index;
27420 if(!preventViewNotify){
27421 var proxy = new Roo.Element(
27422 this.grid.getRowDom(index)
27424 proxy.addClass('bg-info info');
27426 this.fireEvent("rowselect", this, index, r);
27427 this.fireEvent("selectionchange", this);
27433 * @param {Number} row The index of the row to deselect
27435 deselectRow : function(index, preventViewNotify)
27440 if(this.last == index){
27443 if(this.lastActive == index){
27444 this.lastActive = false;
27447 var r = this.grid.store.getAt(index);
27452 this.selections.remove(r);
27453 //.console.log('deselectRow - record id :' + r.id);
27454 if(!preventViewNotify){
27456 var proxy = new Roo.Element(
27457 this.grid.getRowDom(index)
27459 proxy.removeClass('bg-info info');
27461 this.fireEvent("rowdeselect", this, index);
27462 this.fireEvent("selectionchange", this);
27466 restoreLast : function(){
27468 this.last = this._last;
27473 acceptsNav : function(row, col, cm){
27474 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27478 onEditorKey : function(field, e){
27479 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27484 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27486 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27488 }else if(k == e.ENTER && !e.ctrlKey){
27492 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27494 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27496 }else if(k == e.ESC){
27500 g.startEditing(newCell[0], newCell[1]);
27506 * Ext JS Library 1.1.1
27507 * Copyright(c) 2006-2007, Ext JS, LLC.
27509 * Originally Released Under LGPL - original licence link has changed is not relivant.
27512 * <script type="text/javascript">
27516 * @class Roo.bootstrap.PagingToolbar
27517 * @extends Roo.bootstrap.NavSimplebar
27518 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27520 * Create a new PagingToolbar
27521 * @param {Object} config The config object
27522 * @param {Roo.data.Store} store
27524 Roo.bootstrap.PagingToolbar = function(config)
27526 // old args format still supported... - xtype is prefered..
27527 // created from xtype...
27529 this.ds = config.dataSource;
27531 if (config.store && !this.ds) {
27532 this.store= Roo.factory(config.store, Roo.data);
27533 this.ds = this.store;
27534 this.ds.xmodule = this.xmodule || false;
27537 this.toolbarItems = [];
27538 if (config.items) {
27539 this.toolbarItems = config.items;
27542 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27547 this.bind(this.ds);
27550 if (Roo.bootstrap.version == 4) {
27551 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27553 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27558 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27560 * @cfg {Roo.data.Store} dataSource
27561 * The underlying data store providing the paged data
27564 * @cfg {String/HTMLElement/Element} container
27565 * container The id or element that will contain the toolbar
27568 * @cfg {Boolean} displayInfo
27569 * True to display the displayMsg (defaults to false)
27572 * @cfg {Number} pageSize
27573 * The number of records to display per page (defaults to 20)
27577 * @cfg {String} displayMsg
27578 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27580 displayMsg : 'Displaying {0} - {1} of {2}',
27582 * @cfg {String} emptyMsg
27583 * The message to display when no records are found (defaults to "No data to display")
27585 emptyMsg : 'No data to display',
27587 * Customizable piece of the default paging text (defaults to "Page")
27590 beforePageText : "Page",
27592 * Customizable piece of the default paging text (defaults to "of %0")
27595 afterPageText : "of {0}",
27597 * Customizable piece of the default paging text (defaults to "First Page")
27600 firstText : "First Page",
27602 * Customizable piece of the default paging text (defaults to "Previous Page")
27605 prevText : "Previous Page",
27607 * Customizable piece of the default paging text (defaults to "Next Page")
27610 nextText : "Next Page",
27612 * Customizable piece of the default paging text (defaults to "Last Page")
27615 lastText : "Last Page",
27617 * Customizable piece of the default paging text (defaults to "Refresh")
27620 refreshText : "Refresh",
27624 onRender : function(ct, position)
27626 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27627 this.navgroup.parentId = this.id;
27628 this.navgroup.onRender(this.el, null);
27629 // add the buttons to the navgroup
27631 if(this.displayInfo){
27632 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27633 this.displayEl = this.el.select('.x-paging-info', true).first();
27634 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27635 // this.displayEl = navel.el.select('span',true).first();
27641 Roo.each(_this.buttons, function(e){ // this might need to use render????
27642 Roo.factory(e).render(_this.el);
27646 Roo.each(_this.toolbarItems, function(e) {
27647 _this.navgroup.addItem(e);
27651 this.first = this.navgroup.addItem({
27652 tooltip: this.firstText,
27653 cls: "prev btn-outline-secondary",
27654 html : ' <i class="fa fa-step-backward"></i>',
27656 preventDefault: true,
27657 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27660 this.prev = this.navgroup.addItem({
27661 tooltip: this.prevText,
27662 cls: "prev btn-outline-secondary",
27663 html : ' <i class="fa fa-backward"></i>',
27665 preventDefault: true,
27666 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27668 //this.addSeparator();
27671 var field = this.navgroup.addItem( {
27673 cls : 'x-paging-position btn-outline-secondary',
27675 html : this.beforePageText +
27676 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27677 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27680 this.field = field.el.select('input', true).first();
27681 this.field.on("keydown", this.onPagingKeydown, this);
27682 this.field.on("focus", function(){this.dom.select();});
27685 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27686 //this.field.setHeight(18);
27687 //this.addSeparator();
27688 this.next = this.navgroup.addItem({
27689 tooltip: this.nextText,
27690 cls: "next btn-outline-secondary",
27691 html : ' <i class="fa fa-forward"></i>',
27693 preventDefault: true,
27694 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27696 this.last = this.navgroup.addItem({
27697 tooltip: this.lastText,
27698 html : ' <i class="fa fa-step-forward"></i>',
27699 cls: "next btn-outline-secondary",
27701 preventDefault: true,
27702 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27704 //this.addSeparator();
27705 this.loading = this.navgroup.addItem({
27706 tooltip: this.refreshText,
27707 cls: "btn-outline-secondary",
27708 html : ' <i class="fa fa-refresh"></i>',
27709 preventDefault: true,
27710 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27716 updateInfo : function(){
27717 if(this.displayEl){
27718 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27719 var msg = count == 0 ?
27723 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27725 this.displayEl.update(msg);
27730 onLoad : function(ds, r, o)
27732 this.cursor = o.params && o.params.start ? o.params.start : 0;
27734 var d = this.getPageData(),
27739 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27740 this.field.dom.value = ap;
27741 this.first.setDisabled(ap == 1);
27742 this.prev.setDisabled(ap == 1);
27743 this.next.setDisabled(ap == ps);
27744 this.last.setDisabled(ap == ps);
27745 this.loading.enable();
27750 getPageData : function(){
27751 var total = this.ds.getTotalCount();
27754 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27755 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27760 onLoadError : function(){
27761 this.loading.enable();
27765 onPagingKeydown : function(e){
27766 var k = e.getKey();
27767 var d = this.getPageData();
27769 var v = this.field.dom.value, pageNum;
27770 if(!v || isNaN(pageNum = parseInt(v, 10))){
27771 this.field.dom.value = d.activePage;
27774 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27775 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27778 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))
27780 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27781 this.field.dom.value = pageNum;
27782 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27785 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27787 var v = this.field.dom.value, pageNum;
27788 var increment = (e.shiftKey) ? 10 : 1;
27789 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27792 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27793 this.field.dom.value = d.activePage;
27796 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27798 this.field.dom.value = parseInt(v, 10) + increment;
27799 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27800 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27807 beforeLoad : function(){
27809 this.loading.disable();
27814 onClick : function(which){
27823 ds.load({params:{start: 0, limit: this.pageSize}});
27826 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27829 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27832 var total = ds.getTotalCount();
27833 var extra = total % this.pageSize;
27834 var lastStart = extra ? (total - extra) : total-this.pageSize;
27835 ds.load({params:{start: lastStart, limit: this.pageSize}});
27838 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27844 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27845 * @param {Roo.data.Store} store The data store to unbind
27847 unbind : function(ds){
27848 ds.un("beforeload", this.beforeLoad, this);
27849 ds.un("load", this.onLoad, this);
27850 ds.un("loadexception", this.onLoadError, this);
27851 ds.un("remove", this.updateInfo, this);
27852 ds.un("add", this.updateInfo, this);
27853 this.ds = undefined;
27857 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27858 * @param {Roo.data.Store} store The data store to bind
27860 bind : function(ds){
27861 ds.on("beforeload", this.beforeLoad, this);
27862 ds.on("load", this.onLoad, this);
27863 ds.on("loadexception", this.onLoadError, this);
27864 ds.on("remove", this.updateInfo, this);
27865 ds.on("add", this.updateInfo, this);
27876 * @class Roo.bootstrap.MessageBar
27877 * @extends Roo.bootstrap.Component
27878 * Bootstrap MessageBar class
27879 * @cfg {String} html contents of the MessageBar
27880 * @cfg {String} weight (info | success | warning | danger) default info
27881 * @cfg {String} beforeClass insert the bar before the given class
27882 * @cfg {Boolean} closable (true | false) default false
27883 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27886 * Create a new Element
27887 * @param {Object} config The config object
27890 Roo.bootstrap.MessageBar = function(config){
27891 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27894 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27900 beforeClass: 'bootstrap-sticky-wrap',
27902 getAutoCreate : function(){
27906 cls: 'alert alert-dismissable alert-' + this.weight,
27911 html: this.html || ''
27917 cfg.cls += ' alert-messages-fixed';
27931 onRender : function(ct, position)
27933 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27936 var cfg = Roo.apply({}, this.getAutoCreate());
27940 cfg.cls += ' ' + this.cls;
27943 cfg.style = this.style;
27945 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27947 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27950 this.el.select('>button.close').on('click', this.hide, this);
27956 if (!this.rendered) {
27962 this.fireEvent('show', this);
27968 if (!this.rendered) {
27974 this.fireEvent('hide', this);
27977 update : function()
27979 // var e = this.el.dom.firstChild;
27981 // if(this.closable){
27982 // e = e.nextSibling;
27985 // e.data = this.html || '';
27987 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28003 * @class Roo.bootstrap.Graph
28004 * @extends Roo.bootstrap.Component
28005 * Bootstrap Graph class
28009 @cfg {String} graphtype bar | vbar | pie
28010 @cfg {number} g_x coodinator | centre x (pie)
28011 @cfg {number} g_y coodinator | centre y (pie)
28012 @cfg {number} g_r radius (pie)
28013 @cfg {number} g_height height of the chart (respected by all elements in the set)
28014 @cfg {number} g_width width of the chart (respected by all elements in the set)
28015 @cfg {Object} title The title of the chart
28018 -opts (object) options for the chart
28020 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28021 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28023 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.
28024 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28026 o stretch (boolean)
28028 -opts (object) options for the pie
28031 o startAngle (number)
28032 o endAngle (number)
28036 * Create a new Input
28037 * @param {Object} config The config object
28040 Roo.bootstrap.Graph = function(config){
28041 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28047 * The img click event for the img.
28048 * @param {Roo.EventObject} e
28054 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28065 //g_colors: this.colors,
28072 getAutoCreate : function(){
28083 onRender : function(ct,position){
28086 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28088 if (typeof(Raphael) == 'undefined') {
28089 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28093 this.raphael = Raphael(this.el.dom);
28095 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28096 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28097 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28098 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28100 r.text(160, 10, "Single Series Chart").attr(txtattr);
28101 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28102 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28103 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28105 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28106 r.barchart(330, 10, 300, 220, data1);
28107 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28108 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28111 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28112 // r.barchart(30, 30, 560, 250, xdata, {
28113 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28114 // axis : "0 0 1 1",
28115 // axisxlabels : xdata
28116 // //yvalues : cols,
28119 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28121 // this.load(null,xdata,{
28122 // axis : "0 0 1 1",
28123 // axisxlabels : xdata
28128 load : function(graphtype,xdata,opts)
28130 this.raphael.clear();
28132 graphtype = this.graphtype;
28137 var r = this.raphael,
28138 fin = function () {
28139 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28141 fout = function () {
28142 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28144 pfin = function() {
28145 this.sector.stop();
28146 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28149 this.label[0].stop();
28150 this.label[0].attr({ r: 7.5 });
28151 this.label[1].attr({ "font-weight": 800 });
28154 pfout = function() {
28155 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28158 this.label[0].animate({ r: 5 }, 500, "bounce");
28159 this.label[1].attr({ "font-weight": 400 });
28165 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28168 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28171 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28172 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28174 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28181 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28186 setTitle: function(o)
28191 initEvents: function() {
28194 this.el.on('click', this.onClick, this);
28198 onClick : function(e)
28200 Roo.log('img onclick');
28201 this.fireEvent('click', this, e);
28213 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28216 * @class Roo.bootstrap.dash.NumberBox
28217 * @extends Roo.bootstrap.Component
28218 * Bootstrap NumberBox class
28219 * @cfg {String} headline Box headline
28220 * @cfg {String} content Box content
28221 * @cfg {String} icon Box icon
28222 * @cfg {String} footer Footer text
28223 * @cfg {String} fhref Footer href
28226 * Create a new NumberBox
28227 * @param {Object} config The config object
28231 Roo.bootstrap.dash.NumberBox = function(config){
28232 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28236 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28245 getAutoCreate : function(){
28249 cls : 'small-box ',
28257 cls : 'roo-headline',
28258 html : this.headline
28262 cls : 'roo-content',
28263 html : this.content
28277 cls : 'ion ' + this.icon
28286 cls : 'small-box-footer',
28287 href : this.fhref || '#',
28291 cfg.cn.push(footer);
28298 onRender : function(ct,position){
28299 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28306 setHeadline: function (value)
28308 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28311 setFooter: function (value, href)
28313 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28316 this.el.select('a.small-box-footer',true).first().attr('href', href);
28321 setContent: function (value)
28323 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28326 initEvents: function()
28340 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28343 * @class Roo.bootstrap.dash.TabBox
28344 * @extends Roo.bootstrap.Component
28345 * Bootstrap TabBox class
28346 * @cfg {String} title Title of the TabBox
28347 * @cfg {String} icon Icon of the TabBox
28348 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28349 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28352 * Create a new TabBox
28353 * @param {Object} config The config object
28357 Roo.bootstrap.dash.TabBox = function(config){
28358 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28363 * When a pane is added
28364 * @param {Roo.bootstrap.dash.TabPane} pane
28368 * @event activatepane
28369 * When a pane is activated
28370 * @param {Roo.bootstrap.dash.TabPane} pane
28372 "activatepane" : true
28380 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28385 tabScrollable : false,
28387 getChildContainer : function()
28389 return this.el.select('.tab-content', true).first();
28392 getAutoCreate : function(){
28396 cls: 'pull-left header',
28404 cls: 'fa ' + this.icon
28410 cls: 'nav nav-tabs pull-right',
28416 if(this.tabScrollable){
28423 cls: 'nav nav-tabs pull-right',
28434 cls: 'nav-tabs-custom',
28439 cls: 'tab-content no-padding',
28447 initEvents : function()
28449 //Roo.log('add add pane handler');
28450 this.on('addpane', this.onAddPane, this);
28453 * Updates the box title
28454 * @param {String} html to set the title to.
28456 setTitle : function(value)
28458 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28460 onAddPane : function(pane)
28462 this.panes.push(pane);
28463 //Roo.log('addpane');
28465 // tabs are rendere left to right..
28466 if(!this.showtabs){
28470 var ctr = this.el.select('.nav-tabs', true).first();
28473 var existing = ctr.select('.nav-tab',true);
28474 var qty = existing.getCount();;
28477 var tab = ctr.createChild({
28479 cls : 'nav-tab' + (qty ? '' : ' active'),
28487 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28490 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28492 pane.el.addClass('active');
28497 onTabClick : function(ev,un,ob,pane)
28499 //Roo.log('tab - prev default');
28500 ev.preventDefault();
28503 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28504 pane.tab.addClass('active');
28505 //Roo.log(pane.title);
28506 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28507 // technically we should have a deactivate event.. but maybe add later.
28508 // and it should not de-activate the selected tab...
28509 this.fireEvent('activatepane', pane);
28510 pane.el.addClass('active');
28511 pane.fireEvent('activate');
28516 getActivePane : function()
28519 Roo.each(this.panes, function(p) {
28520 if(p.el.hasClass('active')){
28541 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28543 * @class Roo.bootstrap.TabPane
28544 * @extends Roo.bootstrap.Component
28545 * Bootstrap TabPane class
28546 * @cfg {Boolean} active (false | true) Default false
28547 * @cfg {String} title title of panel
28551 * Create a new TabPane
28552 * @param {Object} config The config object
28555 Roo.bootstrap.dash.TabPane = function(config){
28556 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28562 * When a pane is activated
28563 * @param {Roo.bootstrap.dash.TabPane} pane
28570 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28575 // the tabBox that this is attached to.
28578 getAutoCreate : function()
28586 cfg.cls += ' active';
28591 initEvents : function()
28593 //Roo.log('trigger add pane handler');
28594 this.parent().fireEvent('addpane', this)
28598 * Updates the tab title
28599 * @param {String} html to set the title to.
28601 setTitle: function(str)
28607 this.tab.select('a', true).first().dom.innerHTML = str;
28624 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28627 * @class Roo.bootstrap.menu.Menu
28628 * @extends Roo.bootstrap.Component
28629 * Bootstrap Menu class - container for Menu
28630 * @cfg {String} html Text of the menu
28631 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28632 * @cfg {String} icon Font awesome icon
28633 * @cfg {String} pos Menu align to (top | bottom) default bottom
28637 * Create a new Menu
28638 * @param {Object} config The config object
28642 Roo.bootstrap.menu.Menu = function(config){
28643 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28647 * @event beforeshow
28648 * Fires before this menu is displayed
28649 * @param {Roo.bootstrap.menu.Menu} this
28653 * @event beforehide
28654 * Fires before this menu is hidden
28655 * @param {Roo.bootstrap.menu.Menu} this
28660 * Fires after this menu is displayed
28661 * @param {Roo.bootstrap.menu.Menu} this
28666 * Fires after this menu is hidden
28667 * @param {Roo.bootstrap.menu.Menu} this
28672 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28673 * @param {Roo.bootstrap.menu.Menu} this
28674 * @param {Roo.EventObject} e
28681 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28685 weight : 'default',
28690 getChildContainer : function() {
28691 if(this.isSubMenu){
28695 return this.el.select('ul.dropdown-menu', true).first();
28698 getAutoCreate : function()
28703 cls : 'roo-menu-text',
28711 cls : 'fa ' + this.icon
28722 cls : 'dropdown-button btn btn-' + this.weight,
28727 cls : 'dropdown-toggle btn btn-' + this.weight,
28737 cls : 'dropdown-menu'
28743 if(this.pos == 'top'){
28744 cfg.cls += ' dropup';
28747 if(this.isSubMenu){
28750 cls : 'dropdown-menu'
28757 onRender : function(ct, position)
28759 this.isSubMenu = ct.hasClass('dropdown-submenu');
28761 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28764 initEvents : function()
28766 if(this.isSubMenu){
28770 this.hidden = true;
28772 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28773 this.triggerEl.on('click', this.onTriggerPress, this);
28775 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28776 this.buttonEl.on('click', this.onClick, this);
28782 if(this.isSubMenu){
28786 return this.el.select('ul.dropdown-menu', true).first();
28789 onClick : function(e)
28791 this.fireEvent("click", this, e);
28794 onTriggerPress : function(e)
28796 if (this.isVisible()) {
28803 isVisible : function(){
28804 return !this.hidden;
28809 this.fireEvent("beforeshow", this);
28811 this.hidden = false;
28812 this.el.addClass('open');
28814 Roo.get(document).on("mouseup", this.onMouseUp, this);
28816 this.fireEvent("show", this);
28823 this.fireEvent("beforehide", this);
28825 this.hidden = true;
28826 this.el.removeClass('open');
28828 Roo.get(document).un("mouseup", this.onMouseUp);
28830 this.fireEvent("hide", this);
28833 onMouseUp : function()
28847 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28850 * @class Roo.bootstrap.menu.Item
28851 * @extends Roo.bootstrap.Component
28852 * Bootstrap MenuItem class
28853 * @cfg {Boolean} submenu (true | false) default false
28854 * @cfg {String} html text of the item
28855 * @cfg {String} href the link
28856 * @cfg {Boolean} disable (true | false) default false
28857 * @cfg {Boolean} preventDefault (true | false) default true
28858 * @cfg {String} icon Font awesome icon
28859 * @cfg {String} pos Submenu align to (left | right) default right
28863 * Create a new Item
28864 * @param {Object} config The config object
28868 Roo.bootstrap.menu.Item = function(config){
28869 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28873 * Fires when the mouse is hovering over this menu
28874 * @param {Roo.bootstrap.menu.Item} this
28875 * @param {Roo.EventObject} e
28880 * Fires when the mouse exits this menu
28881 * @param {Roo.bootstrap.menu.Item} this
28882 * @param {Roo.EventObject} e
28888 * The raw click event for the entire grid.
28889 * @param {Roo.EventObject} e
28895 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28900 preventDefault: true,
28905 getAutoCreate : function()
28910 cls : 'roo-menu-item-text',
28918 cls : 'fa ' + this.icon
28927 href : this.href || '#',
28934 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28938 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28940 if(this.pos == 'left'){
28941 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28948 initEvents : function()
28950 this.el.on('mouseover', this.onMouseOver, this);
28951 this.el.on('mouseout', this.onMouseOut, this);
28953 this.el.select('a', true).first().on('click', this.onClick, this);
28957 onClick : function(e)
28959 if(this.preventDefault){
28960 e.preventDefault();
28963 this.fireEvent("click", this, e);
28966 onMouseOver : function(e)
28968 if(this.submenu && this.pos == 'left'){
28969 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28972 this.fireEvent("mouseover", this, e);
28975 onMouseOut : function(e)
28977 this.fireEvent("mouseout", this, e);
28989 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28992 * @class Roo.bootstrap.menu.Separator
28993 * @extends Roo.bootstrap.Component
28994 * Bootstrap Separator class
28997 * Create a new Separator
28998 * @param {Object} config The config object
29002 Roo.bootstrap.menu.Separator = function(config){
29003 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29006 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29008 getAutoCreate : function(){
29011 cls: 'dropdown-divider divider'
29029 * @class Roo.bootstrap.Tooltip
29030 * Bootstrap Tooltip class
29031 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29032 * to determine which dom element triggers the tooltip.
29034 * It needs to add support for additional attributes like tooltip-position
29037 * Create a new Toolti
29038 * @param {Object} config The config object
29041 Roo.bootstrap.Tooltip = function(config){
29042 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29044 this.alignment = Roo.bootstrap.Tooltip.alignment;
29046 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29047 this.alignment = config.alignment;
29052 Roo.apply(Roo.bootstrap.Tooltip, {
29054 * @function init initialize tooltip monitoring.
29058 currentTip : false,
29059 currentRegion : false,
29065 Roo.get(document).on('mouseover', this.enter ,this);
29066 Roo.get(document).on('mouseout', this.leave, this);
29069 this.currentTip = new Roo.bootstrap.Tooltip();
29072 enter : function(ev)
29074 var dom = ev.getTarget();
29076 //Roo.log(['enter',dom]);
29077 var el = Roo.fly(dom);
29078 if (this.currentEl) {
29080 //Roo.log(this.currentEl);
29081 //Roo.log(this.currentEl.contains(dom));
29082 if (this.currentEl == el) {
29085 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29091 if (this.currentTip.el) {
29092 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29096 if(!el || el.dom == document){
29102 if (!el.attr('tooltip')) {
29103 pel = el.findParent("[tooltip]");
29105 bindEl = Roo.get(pel);
29111 // you can not look for children, as if el is the body.. then everythign is the child..
29112 if (!pel && !el.attr('tooltip')) { //
29113 if (!el.select("[tooltip]").elements.length) {
29116 // is the mouse over this child...?
29117 bindEl = el.select("[tooltip]").first();
29118 var xy = ev.getXY();
29119 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29120 //Roo.log("not in region.");
29123 //Roo.log("child element over..");
29126 this.currentEl = el;
29127 this.currentTip.bind(bindEl);
29128 this.currentRegion = Roo.lib.Region.getRegion(dom);
29129 this.currentTip.enter();
29132 leave : function(ev)
29134 var dom = ev.getTarget();
29135 //Roo.log(['leave',dom]);
29136 if (!this.currentEl) {
29141 if (dom != this.currentEl.dom) {
29144 var xy = ev.getXY();
29145 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29148 // only activate leave if mouse cursor is outside... bounding box..
29153 if (this.currentTip) {
29154 this.currentTip.leave();
29156 //Roo.log('clear currentEl');
29157 this.currentEl = false;
29162 'left' : ['r-l', [-2,0], 'right'],
29163 'right' : ['l-r', [2,0], 'left'],
29164 'bottom' : ['t-b', [0,2], 'top'],
29165 'top' : [ 'b-t', [0,-2], 'bottom']
29171 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29176 delay : null, // can be { show : 300 , hide: 500}
29180 hoverState : null, //???
29182 placement : 'bottom',
29186 getAutoCreate : function(){
29193 cls : 'tooltip-arrow arrow'
29196 cls : 'tooltip-inner'
29203 bind : function(el)
29208 initEvents : function()
29210 this.arrowEl = this.el.select('.arrow', true).first();
29211 this.innerEl = this.el.select('.tooltip-inner', true).first();
29214 enter : function () {
29216 if (this.timeout != null) {
29217 clearTimeout(this.timeout);
29220 this.hoverState = 'in';
29221 //Roo.log("enter - show");
29222 if (!this.delay || !this.delay.show) {
29227 this.timeout = setTimeout(function () {
29228 if (_t.hoverState == 'in') {
29231 }, this.delay.show);
29235 clearTimeout(this.timeout);
29237 this.hoverState = 'out';
29238 if (!this.delay || !this.delay.hide) {
29244 this.timeout = setTimeout(function () {
29245 //Roo.log("leave - timeout");
29247 if (_t.hoverState == 'out') {
29249 Roo.bootstrap.Tooltip.currentEl = false;
29254 show : function (msg)
29257 this.render(document.body);
29260 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29262 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29264 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29266 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29267 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29269 var placement = typeof this.placement == 'function' ?
29270 this.placement.call(this, this.el, on_el) :
29273 var autoToken = /\s?auto?\s?/i;
29274 var autoPlace = autoToken.test(placement);
29276 placement = placement.replace(autoToken, '') || 'top';
29280 //this.el.setXY([0,0]);
29282 //this.el.dom.style.display='block';
29284 //this.el.appendTo(on_el);
29286 var p = this.getPosition();
29287 var box = this.el.getBox();
29293 var align = this.alignment[placement];
29295 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29297 if(placement == 'top' || placement == 'bottom'){
29299 placement = 'right';
29302 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29303 placement = 'left';
29306 var scroll = Roo.select('body', true).first().getScroll();
29308 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29312 align = this.alignment[placement];
29314 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29318 var elems = document.getElementsByTagName('div');
29319 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29320 for (var i = 0; i < elems.length; i++) {
29321 var zindex = Number.parseInt(
29322 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29325 if (zindex > highest) {
29332 this.el.dom.style.zIndex = highest;
29334 this.el.alignTo(this.bindEl, align[0],align[1]);
29335 //var arrow = this.el.select('.arrow',true).first();
29336 //arrow.set(align[2],
29338 this.el.addClass(placement);
29339 this.el.addClass("bs-tooltip-"+ placement);
29341 this.el.addClass('in fade show');
29343 this.hoverState = null;
29345 if (this.el.hasClass('fade')) {
29360 //this.el.setXY([0,0]);
29361 this.el.removeClass(['show', 'in']);
29377 * @class Roo.bootstrap.LocationPicker
29378 * @extends Roo.bootstrap.Component
29379 * Bootstrap LocationPicker class
29380 * @cfg {Number} latitude Position when init default 0
29381 * @cfg {Number} longitude Position when init default 0
29382 * @cfg {Number} zoom default 15
29383 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29384 * @cfg {Boolean} mapTypeControl default false
29385 * @cfg {Boolean} disableDoubleClickZoom default false
29386 * @cfg {Boolean} scrollwheel default true
29387 * @cfg {Boolean} streetViewControl default false
29388 * @cfg {Number} radius default 0
29389 * @cfg {String} locationName
29390 * @cfg {Boolean} draggable default true
29391 * @cfg {Boolean} enableAutocomplete default false
29392 * @cfg {Boolean} enableReverseGeocode default true
29393 * @cfg {String} markerTitle
29396 * Create a new LocationPicker
29397 * @param {Object} config The config object
29401 Roo.bootstrap.LocationPicker = function(config){
29403 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29408 * Fires when the picker initialized.
29409 * @param {Roo.bootstrap.LocationPicker} this
29410 * @param {Google Location} location
29414 * @event positionchanged
29415 * Fires when the picker position changed.
29416 * @param {Roo.bootstrap.LocationPicker} this
29417 * @param {Google Location} location
29419 positionchanged : true,
29422 * Fires when the map resize.
29423 * @param {Roo.bootstrap.LocationPicker} this
29428 * Fires when the map show.
29429 * @param {Roo.bootstrap.LocationPicker} this
29434 * Fires when the map hide.
29435 * @param {Roo.bootstrap.LocationPicker} this
29440 * Fires when click the map.
29441 * @param {Roo.bootstrap.LocationPicker} this
29442 * @param {Map event} e
29446 * @event mapRightClick
29447 * Fires when right click the map.
29448 * @param {Roo.bootstrap.LocationPicker} this
29449 * @param {Map event} e
29451 mapRightClick : true,
29453 * @event markerClick
29454 * Fires when click the marker.
29455 * @param {Roo.bootstrap.LocationPicker} this
29456 * @param {Map event} e
29458 markerClick : true,
29460 * @event markerRightClick
29461 * Fires when right click the marker.
29462 * @param {Roo.bootstrap.LocationPicker} this
29463 * @param {Map event} e
29465 markerRightClick : true,
29467 * @event OverlayViewDraw
29468 * Fires when OverlayView Draw
29469 * @param {Roo.bootstrap.LocationPicker} this
29471 OverlayViewDraw : true,
29473 * @event OverlayViewOnAdd
29474 * Fires when OverlayView Draw
29475 * @param {Roo.bootstrap.LocationPicker} this
29477 OverlayViewOnAdd : true,
29479 * @event OverlayViewOnRemove
29480 * Fires when OverlayView Draw
29481 * @param {Roo.bootstrap.LocationPicker} this
29483 OverlayViewOnRemove : true,
29485 * @event OverlayViewShow
29486 * Fires when OverlayView Draw
29487 * @param {Roo.bootstrap.LocationPicker} this
29488 * @param {Pixel} cpx
29490 OverlayViewShow : true,
29492 * @event OverlayViewHide
29493 * Fires when OverlayView Draw
29494 * @param {Roo.bootstrap.LocationPicker} this
29496 OverlayViewHide : true,
29498 * @event loadexception
29499 * Fires when load google lib failed.
29500 * @param {Roo.bootstrap.LocationPicker} this
29502 loadexception : true
29507 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29509 gMapContext: false,
29515 mapTypeControl: false,
29516 disableDoubleClickZoom: false,
29518 streetViewControl: false,
29522 enableAutocomplete: false,
29523 enableReverseGeocode: true,
29526 getAutoCreate: function()
29531 cls: 'roo-location-picker'
29537 initEvents: function(ct, position)
29539 if(!this.el.getWidth() || this.isApplied()){
29543 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29548 initial: function()
29550 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29551 this.fireEvent('loadexception', this);
29555 if(!this.mapTypeId){
29556 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29559 this.gMapContext = this.GMapContext();
29561 this.initOverlayView();
29563 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29567 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29568 _this.setPosition(_this.gMapContext.marker.position);
29571 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29572 _this.fireEvent('mapClick', this, event);
29576 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29577 _this.fireEvent('mapRightClick', this, event);
29581 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29582 _this.fireEvent('markerClick', this, event);
29586 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29587 _this.fireEvent('markerRightClick', this, event);
29591 this.setPosition(this.gMapContext.location);
29593 this.fireEvent('initial', this, this.gMapContext.location);
29596 initOverlayView: function()
29600 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29604 _this.fireEvent('OverlayViewDraw', _this);
29609 _this.fireEvent('OverlayViewOnAdd', _this);
29612 onRemove: function()
29614 _this.fireEvent('OverlayViewOnRemove', _this);
29617 show: function(cpx)
29619 _this.fireEvent('OverlayViewShow', _this, cpx);
29624 _this.fireEvent('OverlayViewHide', _this);
29630 fromLatLngToContainerPixel: function(event)
29632 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29635 isApplied: function()
29637 return this.getGmapContext() == false ? false : true;
29640 getGmapContext: function()
29642 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29645 GMapContext: function()
29647 var position = new google.maps.LatLng(this.latitude, this.longitude);
29649 var _map = new google.maps.Map(this.el.dom, {
29652 mapTypeId: this.mapTypeId,
29653 mapTypeControl: this.mapTypeControl,
29654 disableDoubleClickZoom: this.disableDoubleClickZoom,
29655 scrollwheel: this.scrollwheel,
29656 streetViewControl: this.streetViewControl,
29657 locationName: this.locationName,
29658 draggable: this.draggable,
29659 enableAutocomplete: this.enableAutocomplete,
29660 enableReverseGeocode: this.enableReverseGeocode
29663 var _marker = new google.maps.Marker({
29664 position: position,
29666 title: this.markerTitle,
29667 draggable: this.draggable
29674 location: position,
29675 radius: this.radius,
29676 locationName: this.locationName,
29677 addressComponents: {
29678 formatted_address: null,
29679 addressLine1: null,
29680 addressLine2: null,
29682 streetNumber: null,
29686 stateOrProvince: null
29689 domContainer: this.el.dom,
29690 geodecoder: new google.maps.Geocoder()
29694 drawCircle: function(center, radius, options)
29696 if (this.gMapContext.circle != null) {
29697 this.gMapContext.circle.setMap(null);
29701 options = Roo.apply({}, options, {
29702 strokeColor: "#0000FF",
29703 strokeOpacity: .35,
29705 fillColor: "#0000FF",
29709 options.map = this.gMapContext.map;
29710 options.radius = radius;
29711 options.center = center;
29712 this.gMapContext.circle = new google.maps.Circle(options);
29713 return this.gMapContext.circle;
29719 setPosition: function(location)
29721 this.gMapContext.location = location;
29722 this.gMapContext.marker.setPosition(location);
29723 this.gMapContext.map.panTo(location);
29724 this.drawCircle(location, this.gMapContext.radius, {});
29728 if (this.gMapContext.settings.enableReverseGeocode) {
29729 this.gMapContext.geodecoder.geocode({
29730 latLng: this.gMapContext.location
29731 }, function(results, status) {
29733 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29734 _this.gMapContext.locationName = results[0].formatted_address;
29735 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29737 _this.fireEvent('positionchanged', this, location);
29744 this.fireEvent('positionchanged', this, location);
29749 google.maps.event.trigger(this.gMapContext.map, "resize");
29751 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29753 this.fireEvent('resize', this);
29756 setPositionByLatLng: function(latitude, longitude)
29758 this.setPosition(new google.maps.LatLng(latitude, longitude));
29761 getCurrentPosition: function()
29764 latitude: this.gMapContext.location.lat(),
29765 longitude: this.gMapContext.location.lng()
29769 getAddressName: function()
29771 return this.gMapContext.locationName;
29774 getAddressComponents: function()
29776 return this.gMapContext.addressComponents;
29779 address_component_from_google_geocode: function(address_components)
29783 for (var i = 0; i < address_components.length; i++) {
29784 var component = address_components[i];
29785 if (component.types.indexOf("postal_code") >= 0) {
29786 result.postalCode = component.short_name;
29787 } else if (component.types.indexOf("street_number") >= 0) {
29788 result.streetNumber = component.short_name;
29789 } else if (component.types.indexOf("route") >= 0) {
29790 result.streetName = component.short_name;
29791 } else if (component.types.indexOf("neighborhood") >= 0) {
29792 result.city = component.short_name;
29793 } else if (component.types.indexOf("locality") >= 0) {
29794 result.city = component.short_name;
29795 } else if (component.types.indexOf("sublocality") >= 0) {
29796 result.district = component.short_name;
29797 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29798 result.stateOrProvince = component.short_name;
29799 } else if (component.types.indexOf("country") >= 0) {
29800 result.country = component.short_name;
29804 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29805 result.addressLine2 = "";
29809 setZoomLevel: function(zoom)
29811 this.gMapContext.map.setZoom(zoom);
29824 this.fireEvent('show', this);
29835 this.fireEvent('hide', this);
29840 Roo.apply(Roo.bootstrap.LocationPicker, {
29842 OverlayView : function(map, options)
29844 options = options || {};
29851 * @class Roo.bootstrap.Alert
29852 * @extends Roo.bootstrap.Component
29853 * Bootstrap Alert class - shows an alert area box
29855 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29856 Enter a valid email address
29859 * @cfg {String} title The title of alert
29860 * @cfg {String} html The content of alert
29861 * @cfg {String} weight ( success | info | warning | danger )
29862 * @cfg {String} fa font-awesomeicon
29863 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29864 * @cfg {Boolean} close true to show a x closer
29868 * Create a new alert
29869 * @param {Object} config The config object
29873 Roo.bootstrap.Alert = function(config){
29874 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29878 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29884 faicon: false, // BC
29888 getAutoCreate : function()
29900 style : this.close ? '' : 'display:none'
29904 cls : 'roo-alert-icon'
29909 cls : 'roo-alert-title',
29914 cls : 'roo-alert-text',
29921 cfg.cn[0].cls += ' fa ' + this.faicon;
29924 cfg.cn[0].cls += ' fa ' + this.fa;
29928 cfg.cls += ' alert-' + this.weight;
29934 initEvents: function()
29936 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29937 this.titleEl = this.el.select('.roo-alert-title',true).first();
29938 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29939 if (this.seconds > 0) {
29940 this.hide.defer(this.seconds, this);
29944 setTitle : function(str)
29946 this.titleEl.dom.innerHTML = str;
29949 setText : function(str)
29951 this.titleEl.dom.innerHTML = str;
29954 setWeight : function(weight)
29957 this.el.removeClass('alert-' + this.weight);
29960 this.weight = weight;
29962 this.el.addClass('alert-' + this.weight);
29965 setIcon : function(icon)
29968 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29971 this.faicon = icon;
29973 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29994 * @class Roo.bootstrap.UploadCropbox
29995 * @extends Roo.bootstrap.Component
29996 * Bootstrap UploadCropbox class
29997 * @cfg {String} emptyText show when image has been loaded
29998 * @cfg {String} rotateNotify show when image too small to rotate
29999 * @cfg {Number} errorTimeout default 3000
30000 * @cfg {Number} minWidth default 300
30001 * @cfg {Number} minHeight default 300
30002 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30003 * @cfg {Boolean} isDocument (true|false) default false
30004 * @cfg {String} url action url
30005 * @cfg {String} paramName default 'imageUpload'
30006 * @cfg {String} method default POST
30007 * @cfg {Boolean} loadMask (true|false) default true
30008 * @cfg {Boolean} loadingText default 'Loading...'
30011 * Create a new UploadCropbox
30012 * @param {Object} config The config object
30015 Roo.bootstrap.UploadCropbox = function(config){
30016 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30020 * @event beforeselectfile
30021 * Fire before select file
30022 * @param {Roo.bootstrap.UploadCropbox} this
30024 "beforeselectfile" : true,
30027 * Fire after initEvent
30028 * @param {Roo.bootstrap.UploadCropbox} this
30033 * Fire after initEvent
30034 * @param {Roo.bootstrap.UploadCropbox} this
30035 * @param {String} data
30040 * Fire when preparing the file data
30041 * @param {Roo.bootstrap.UploadCropbox} this
30042 * @param {Object} file
30047 * Fire when get exception
30048 * @param {Roo.bootstrap.UploadCropbox} this
30049 * @param {XMLHttpRequest} xhr
30051 "exception" : true,
30053 * @event beforeloadcanvas
30054 * Fire before load the canvas
30055 * @param {Roo.bootstrap.UploadCropbox} this
30056 * @param {String} src
30058 "beforeloadcanvas" : true,
30061 * Fire when trash image
30062 * @param {Roo.bootstrap.UploadCropbox} this
30067 * Fire when download the image
30068 * @param {Roo.bootstrap.UploadCropbox} this
30072 * @event footerbuttonclick
30073 * Fire when footerbuttonclick
30074 * @param {Roo.bootstrap.UploadCropbox} this
30075 * @param {String} type
30077 "footerbuttonclick" : true,
30081 * @param {Roo.bootstrap.UploadCropbox} this
30086 * Fire when rotate the image
30087 * @param {Roo.bootstrap.UploadCropbox} this
30088 * @param {String} pos
30093 * Fire when inspect the file
30094 * @param {Roo.bootstrap.UploadCropbox} this
30095 * @param {Object} file
30100 * Fire when xhr upload the file
30101 * @param {Roo.bootstrap.UploadCropbox} this
30102 * @param {Object} data
30107 * Fire when arrange the file data
30108 * @param {Roo.bootstrap.UploadCropbox} this
30109 * @param {Object} formData
30114 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30117 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30119 emptyText : 'Click to upload image',
30120 rotateNotify : 'Image is too small to rotate',
30121 errorTimeout : 3000,
30135 cropType : 'image/jpeg',
30137 canvasLoaded : false,
30138 isDocument : false,
30140 paramName : 'imageUpload',
30142 loadingText : 'Loading...',
30145 getAutoCreate : function()
30149 cls : 'roo-upload-cropbox',
30153 cls : 'roo-upload-cropbox-selector',
30158 cls : 'roo-upload-cropbox-body',
30159 style : 'cursor:pointer',
30163 cls : 'roo-upload-cropbox-preview'
30167 cls : 'roo-upload-cropbox-thumb'
30171 cls : 'roo-upload-cropbox-empty-notify',
30172 html : this.emptyText
30176 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30177 html : this.rotateNotify
30183 cls : 'roo-upload-cropbox-footer',
30186 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30196 onRender : function(ct, position)
30198 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30200 if (this.buttons.length) {
30202 Roo.each(this.buttons, function(bb) {
30204 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30206 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30212 this.maskEl = this.el;
30216 initEvents : function()
30218 this.urlAPI = (window.createObjectURL && window) ||
30219 (window.URL && URL.revokeObjectURL && URL) ||
30220 (window.webkitURL && webkitURL);
30222 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30223 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30225 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30226 this.selectorEl.hide();
30228 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30229 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30231 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30232 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30233 this.thumbEl.hide();
30235 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30236 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30238 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30239 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240 this.errorEl.hide();
30242 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30243 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30244 this.footerEl.hide();
30246 this.setThumbBoxSize();
30252 this.fireEvent('initial', this);
30259 window.addEventListener("resize", function() { _this.resize(); } );
30261 this.bodyEl.on('click', this.beforeSelectFile, this);
30264 this.bodyEl.on('touchstart', this.onTouchStart, this);
30265 this.bodyEl.on('touchmove', this.onTouchMove, this);
30266 this.bodyEl.on('touchend', this.onTouchEnd, this);
30270 this.bodyEl.on('mousedown', this.onMouseDown, this);
30271 this.bodyEl.on('mousemove', this.onMouseMove, this);
30272 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30273 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30274 Roo.get(document).on('mouseup', this.onMouseUp, this);
30277 this.selectorEl.on('change', this.onFileSelected, this);
30283 this.baseScale = 1;
30285 this.baseRotate = 1;
30286 this.dragable = false;
30287 this.pinching = false;
30290 this.cropData = false;
30291 this.notifyEl.dom.innerHTML = this.emptyText;
30293 this.selectorEl.dom.value = '';
30297 resize : function()
30299 if(this.fireEvent('resize', this) != false){
30300 this.setThumbBoxPosition();
30301 this.setCanvasPosition();
30305 onFooterButtonClick : function(e, el, o, type)
30308 case 'rotate-left' :
30309 this.onRotateLeft(e);
30311 case 'rotate-right' :
30312 this.onRotateRight(e);
30315 this.beforeSelectFile(e);
30330 this.fireEvent('footerbuttonclick', this, type);
30333 beforeSelectFile : function(e)
30335 e.preventDefault();
30337 if(this.fireEvent('beforeselectfile', this) != false){
30338 this.selectorEl.dom.click();
30342 onFileSelected : function(e)
30344 e.preventDefault();
30346 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30350 var file = this.selectorEl.dom.files[0];
30352 if(this.fireEvent('inspect', this, file) != false){
30353 this.prepare(file);
30358 trash : function(e)
30360 this.fireEvent('trash', this);
30363 download : function(e)
30365 this.fireEvent('download', this);
30368 loadCanvas : function(src)
30370 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30374 this.imageEl = document.createElement('img');
30378 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30380 this.imageEl.src = src;
30384 onLoadCanvas : function()
30386 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30387 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30389 this.bodyEl.un('click', this.beforeSelectFile, this);
30391 this.notifyEl.hide();
30392 this.thumbEl.show();
30393 this.footerEl.show();
30395 this.baseRotateLevel();
30397 if(this.isDocument){
30398 this.setThumbBoxSize();
30401 this.setThumbBoxPosition();
30403 this.baseScaleLevel();
30409 this.canvasLoaded = true;
30412 this.maskEl.unmask();
30417 setCanvasPosition : function()
30419 if(!this.canvasEl){
30423 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30424 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30426 this.previewEl.setLeft(pw);
30427 this.previewEl.setTop(ph);
30431 onMouseDown : function(e)
30435 this.dragable = true;
30436 this.pinching = false;
30438 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30439 this.dragable = false;
30443 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30444 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30448 onMouseMove : function(e)
30452 if(!this.canvasLoaded){
30456 if (!this.dragable){
30460 var minX = Math.ceil(this.thumbEl.getLeft(true));
30461 var minY = Math.ceil(this.thumbEl.getTop(true));
30463 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30464 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30466 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30467 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30469 x = x - this.mouseX;
30470 y = y - this.mouseY;
30472 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30473 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30475 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30476 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30478 this.previewEl.setLeft(bgX);
30479 this.previewEl.setTop(bgY);
30481 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30482 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30485 onMouseUp : function(e)
30489 this.dragable = false;
30492 onMouseWheel : function(e)
30496 this.startScale = this.scale;
30498 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30500 if(!this.zoomable()){
30501 this.scale = this.startScale;
30510 zoomable : function()
30512 var minScale = this.thumbEl.getWidth() / this.minWidth;
30514 if(this.minWidth < this.minHeight){
30515 minScale = this.thumbEl.getHeight() / this.minHeight;
30518 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30519 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30523 (this.rotate == 0 || this.rotate == 180) &&
30525 width > this.imageEl.OriginWidth ||
30526 height > this.imageEl.OriginHeight ||
30527 (width < this.minWidth && height < this.minHeight)
30535 (this.rotate == 90 || this.rotate == 270) &&
30537 width > this.imageEl.OriginWidth ||
30538 height > this.imageEl.OriginHeight ||
30539 (width < this.minHeight && height < this.minWidth)
30546 !this.isDocument &&
30547 (this.rotate == 0 || this.rotate == 180) &&
30549 width < this.minWidth ||
30550 width > this.imageEl.OriginWidth ||
30551 height < this.minHeight ||
30552 height > this.imageEl.OriginHeight
30559 !this.isDocument &&
30560 (this.rotate == 90 || this.rotate == 270) &&
30562 width < this.minHeight ||
30563 width > this.imageEl.OriginWidth ||
30564 height < this.minWidth ||
30565 height > this.imageEl.OriginHeight
30575 onRotateLeft : function(e)
30577 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30579 var minScale = this.thumbEl.getWidth() / this.minWidth;
30581 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30582 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30584 this.startScale = this.scale;
30586 while (this.getScaleLevel() < minScale){
30588 this.scale = this.scale + 1;
30590 if(!this.zoomable()){
30595 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30596 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30601 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30608 this.scale = this.startScale;
30610 this.onRotateFail();
30615 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30617 if(this.isDocument){
30618 this.setThumbBoxSize();
30619 this.setThumbBoxPosition();
30620 this.setCanvasPosition();
30625 this.fireEvent('rotate', this, 'left');
30629 onRotateRight : function(e)
30631 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30633 var minScale = this.thumbEl.getWidth() / this.minWidth;
30635 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30636 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30638 this.startScale = this.scale;
30640 while (this.getScaleLevel() < minScale){
30642 this.scale = this.scale + 1;
30644 if(!this.zoomable()){
30649 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30650 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30655 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30662 this.scale = this.startScale;
30664 this.onRotateFail();
30669 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30671 if(this.isDocument){
30672 this.setThumbBoxSize();
30673 this.setThumbBoxPosition();
30674 this.setCanvasPosition();
30679 this.fireEvent('rotate', this, 'right');
30682 onRotateFail : function()
30684 this.errorEl.show(true);
30688 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30693 this.previewEl.dom.innerHTML = '';
30695 var canvasEl = document.createElement("canvas");
30697 var contextEl = canvasEl.getContext("2d");
30699 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30700 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30701 var center = this.imageEl.OriginWidth / 2;
30703 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30704 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30705 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30706 center = this.imageEl.OriginHeight / 2;
30709 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30711 contextEl.translate(center, center);
30712 contextEl.rotate(this.rotate * Math.PI / 180);
30714 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30716 this.canvasEl = document.createElement("canvas");
30718 this.contextEl = this.canvasEl.getContext("2d");
30720 switch (this.rotate) {
30723 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30724 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30726 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30731 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30732 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30734 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30735 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);
30739 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30744 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30745 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30747 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30748 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);
30752 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);
30757 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30758 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30760 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30761 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30765 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);
30772 this.previewEl.appendChild(this.canvasEl);
30774 this.setCanvasPosition();
30779 if(!this.canvasLoaded){
30783 var imageCanvas = document.createElement("canvas");
30785 var imageContext = imageCanvas.getContext("2d");
30787 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30788 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30790 var center = imageCanvas.width / 2;
30792 imageContext.translate(center, center);
30794 imageContext.rotate(this.rotate * Math.PI / 180);
30796 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30798 var canvas = document.createElement("canvas");
30800 var context = canvas.getContext("2d");
30802 canvas.width = this.minWidth;
30803 canvas.height = this.minHeight;
30805 switch (this.rotate) {
30808 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30809 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30811 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30812 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30814 var targetWidth = this.minWidth - 2 * x;
30815 var targetHeight = this.minHeight - 2 * y;
30819 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30820 scale = targetWidth / width;
30823 if(x > 0 && y == 0){
30824 scale = targetHeight / height;
30827 if(x > 0 && y > 0){
30828 scale = targetWidth / width;
30830 if(width < height){
30831 scale = targetHeight / height;
30835 context.scale(scale, scale);
30837 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30838 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30840 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30841 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30843 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30848 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30849 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30851 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30852 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30854 var targetWidth = this.minWidth - 2 * x;
30855 var targetHeight = this.minHeight - 2 * y;
30859 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30860 scale = targetWidth / width;
30863 if(x > 0 && y == 0){
30864 scale = targetHeight / height;
30867 if(x > 0 && y > 0){
30868 scale = targetWidth / width;
30870 if(width < height){
30871 scale = targetHeight / height;
30875 context.scale(scale, scale);
30877 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30878 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30880 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30881 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30883 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30885 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30890 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30891 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30893 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30894 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30896 var targetWidth = this.minWidth - 2 * x;
30897 var targetHeight = this.minHeight - 2 * y;
30901 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30902 scale = targetWidth / width;
30905 if(x > 0 && y == 0){
30906 scale = targetHeight / height;
30909 if(x > 0 && y > 0){
30910 scale = targetWidth / width;
30912 if(width < height){
30913 scale = targetHeight / height;
30917 context.scale(scale, scale);
30919 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30920 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30922 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30923 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30925 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30926 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30928 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30933 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30934 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30936 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30937 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30939 var targetWidth = this.minWidth - 2 * x;
30940 var targetHeight = this.minHeight - 2 * y;
30944 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30945 scale = targetWidth / width;
30948 if(x > 0 && y == 0){
30949 scale = targetHeight / height;
30952 if(x > 0 && y > 0){
30953 scale = targetWidth / width;
30955 if(width < height){
30956 scale = targetHeight / height;
30960 context.scale(scale, scale);
30962 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30963 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30965 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30966 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30968 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30970 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30977 this.cropData = canvas.toDataURL(this.cropType);
30979 if(this.fireEvent('crop', this, this.cropData) !== false){
30980 this.process(this.file, this.cropData);
30987 setThumbBoxSize : function()
30991 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30992 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30993 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30995 this.minWidth = width;
30996 this.minHeight = height;
30998 if(this.rotate == 90 || this.rotate == 270){
30999 this.minWidth = height;
31000 this.minHeight = width;
31005 width = Math.ceil(this.minWidth * height / this.minHeight);
31007 if(this.minWidth > this.minHeight){
31009 height = Math.ceil(this.minHeight * width / this.minWidth);
31012 this.thumbEl.setStyle({
31013 width : width + 'px',
31014 height : height + 'px'
31021 setThumbBoxPosition : function()
31023 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31024 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31026 this.thumbEl.setLeft(x);
31027 this.thumbEl.setTop(y);
31031 baseRotateLevel : function()
31033 this.baseRotate = 1;
31036 typeof(this.exif) != 'undefined' &&
31037 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31038 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31040 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31043 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31047 baseScaleLevel : function()
31051 if(this.isDocument){
31053 if(this.baseRotate == 6 || this.baseRotate == 8){
31055 height = this.thumbEl.getHeight();
31056 this.baseScale = height / this.imageEl.OriginWidth;
31058 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31059 width = this.thumbEl.getWidth();
31060 this.baseScale = width / this.imageEl.OriginHeight;
31066 height = this.thumbEl.getHeight();
31067 this.baseScale = height / this.imageEl.OriginHeight;
31069 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31070 width = this.thumbEl.getWidth();
31071 this.baseScale = width / this.imageEl.OriginWidth;
31077 if(this.baseRotate == 6 || this.baseRotate == 8){
31079 width = this.thumbEl.getHeight();
31080 this.baseScale = width / this.imageEl.OriginHeight;
31082 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31083 height = this.thumbEl.getWidth();
31084 this.baseScale = height / this.imageEl.OriginHeight;
31087 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31088 height = this.thumbEl.getWidth();
31089 this.baseScale = height / this.imageEl.OriginHeight;
31091 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31092 width = this.thumbEl.getHeight();
31093 this.baseScale = width / this.imageEl.OriginWidth;
31100 width = this.thumbEl.getWidth();
31101 this.baseScale = width / this.imageEl.OriginWidth;
31103 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31104 height = this.thumbEl.getHeight();
31105 this.baseScale = height / this.imageEl.OriginHeight;
31108 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31110 height = this.thumbEl.getHeight();
31111 this.baseScale = height / this.imageEl.OriginHeight;
31113 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31114 width = this.thumbEl.getWidth();
31115 this.baseScale = width / this.imageEl.OriginWidth;
31123 getScaleLevel : function()
31125 return this.baseScale * Math.pow(1.1, this.scale);
31128 onTouchStart : function(e)
31130 if(!this.canvasLoaded){
31131 this.beforeSelectFile(e);
31135 var touches = e.browserEvent.touches;
31141 if(touches.length == 1){
31142 this.onMouseDown(e);
31146 if(touches.length != 2){
31152 for(var i = 0, finger; finger = touches[i]; i++){
31153 coords.push(finger.pageX, finger.pageY);
31156 var x = Math.pow(coords[0] - coords[2], 2);
31157 var y = Math.pow(coords[1] - coords[3], 2);
31159 this.startDistance = Math.sqrt(x + y);
31161 this.startScale = this.scale;
31163 this.pinching = true;
31164 this.dragable = false;
31168 onTouchMove : function(e)
31170 if(!this.pinching && !this.dragable){
31174 var touches = e.browserEvent.touches;
31181 this.onMouseMove(e);
31187 for(var i = 0, finger; finger = touches[i]; i++){
31188 coords.push(finger.pageX, finger.pageY);
31191 var x = Math.pow(coords[0] - coords[2], 2);
31192 var y = Math.pow(coords[1] - coords[3], 2);
31194 this.endDistance = Math.sqrt(x + y);
31196 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31198 if(!this.zoomable()){
31199 this.scale = this.startScale;
31207 onTouchEnd : function(e)
31209 this.pinching = false;
31210 this.dragable = false;
31214 process : function(file, crop)
31217 this.maskEl.mask(this.loadingText);
31220 this.xhr = new XMLHttpRequest();
31222 file.xhr = this.xhr;
31224 this.xhr.open(this.method, this.url, true);
31227 "Accept": "application/json",
31228 "Cache-Control": "no-cache",
31229 "X-Requested-With": "XMLHttpRequest"
31232 for (var headerName in headers) {
31233 var headerValue = headers[headerName];
31235 this.xhr.setRequestHeader(headerName, headerValue);
31241 this.xhr.onload = function()
31243 _this.xhrOnLoad(_this.xhr);
31246 this.xhr.onerror = function()
31248 _this.xhrOnError(_this.xhr);
31251 var formData = new FormData();
31253 formData.append('returnHTML', 'NO');
31256 formData.append('crop', crop);
31259 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31260 formData.append(this.paramName, file, file.name);
31263 if(typeof(file.filename) != 'undefined'){
31264 formData.append('filename', file.filename);
31267 if(typeof(file.mimetype) != 'undefined'){
31268 formData.append('mimetype', file.mimetype);
31271 if(this.fireEvent('arrange', this, formData) != false){
31272 this.xhr.send(formData);
31276 xhrOnLoad : function(xhr)
31279 this.maskEl.unmask();
31282 if (xhr.readyState !== 4) {
31283 this.fireEvent('exception', this, xhr);
31287 var response = Roo.decode(xhr.responseText);
31289 if(!response.success){
31290 this.fireEvent('exception', this, xhr);
31294 var response = Roo.decode(xhr.responseText);
31296 this.fireEvent('upload', this, response);
31300 xhrOnError : function()
31303 this.maskEl.unmask();
31306 Roo.log('xhr on error');
31308 var response = Roo.decode(xhr.responseText);
31314 prepare : function(file)
31317 this.maskEl.mask(this.loadingText);
31323 if(typeof(file) === 'string'){
31324 this.loadCanvas(file);
31328 if(!file || !this.urlAPI){
31333 this.cropType = file.type;
31337 if(this.fireEvent('prepare', this, this.file) != false){
31339 var reader = new FileReader();
31341 reader.onload = function (e) {
31342 if (e.target.error) {
31343 Roo.log(e.target.error);
31347 var buffer = e.target.result,
31348 dataView = new DataView(buffer),
31350 maxOffset = dataView.byteLength - 4,
31354 if (dataView.getUint16(0) === 0xffd8) {
31355 while (offset < maxOffset) {
31356 markerBytes = dataView.getUint16(offset);
31358 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31359 markerLength = dataView.getUint16(offset + 2) + 2;
31360 if (offset + markerLength > dataView.byteLength) {
31361 Roo.log('Invalid meta data: Invalid segment size.');
31365 if(markerBytes == 0xffe1){
31366 _this.parseExifData(
31373 offset += markerLength;
31383 var url = _this.urlAPI.createObjectURL(_this.file);
31385 _this.loadCanvas(url);
31390 reader.readAsArrayBuffer(this.file);
31396 parseExifData : function(dataView, offset, length)
31398 var tiffOffset = offset + 10,
31402 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31403 // No Exif data, might be XMP data instead
31407 // Check for the ASCII code for "Exif" (0x45786966):
31408 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31409 // No Exif data, might be XMP data instead
31412 if (tiffOffset + 8 > dataView.byteLength) {
31413 Roo.log('Invalid Exif data: Invalid segment size.');
31416 // Check for the two null bytes:
31417 if (dataView.getUint16(offset + 8) !== 0x0000) {
31418 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31421 // Check the byte alignment:
31422 switch (dataView.getUint16(tiffOffset)) {
31424 littleEndian = true;
31427 littleEndian = false;
31430 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31433 // Check for the TIFF tag marker (0x002A):
31434 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31435 Roo.log('Invalid Exif data: Missing TIFF marker.');
31438 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31439 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31441 this.parseExifTags(
31444 tiffOffset + dirOffset,
31449 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31454 if (dirOffset + 6 > dataView.byteLength) {
31455 Roo.log('Invalid Exif data: Invalid directory offset.');
31458 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31459 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31460 if (dirEndOffset + 4 > dataView.byteLength) {
31461 Roo.log('Invalid Exif data: Invalid directory size.');
31464 for (i = 0; i < tagsNumber; i += 1) {
31468 dirOffset + 2 + 12 * i, // tag offset
31472 // Return the offset to the next directory:
31473 return dataView.getUint32(dirEndOffset, littleEndian);
31476 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31478 var tag = dataView.getUint16(offset, littleEndian);
31480 this.exif[tag] = this.getExifValue(
31484 dataView.getUint16(offset + 2, littleEndian), // tag type
31485 dataView.getUint32(offset + 4, littleEndian), // tag length
31490 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31492 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31501 Roo.log('Invalid Exif data: Invalid tag type.');
31505 tagSize = tagType.size * length;
31506 // Determine if the value is contained in the dataOffset bytes,
31507 // or if the value at the dataOffset is a pointer to the actual data:
31508 dataOffset = tagSize > 4 ?
31509 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31510 if (dataOffset + tagSize > dataView.byteLength) {
31511 Roo.log('Invalid Exif data: Invalid data offset.');
31514 if (length === 1) {
31515 return tagType.getValue(dataView, dataOffset, littleEndian);
31518 for (i = 0; i < length; i += 1) {
31519 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31522 if (tagType.ascii) {
31524 // Concatenate the chars:
31525 for (i = 0; i < values.length; i += 1) {
31527 // Ignore the terminating NULL byte(s):
31528 if (c === '\u0000') {
31540 Roo.apply(Roo.bootstrap.UploadCropbox, {
31542 'Orientation': 0x0112
31546 1: 0, //'top-left',
31548 3: 180, //'bottom-right',
31549 // 4: 'bottom-left',
31551 6: 90, //'right-top',
31552 // 7: 'right-bottom',
31553 8: 270 //'left-bottom'
31557 // byte, 8-bit unsigned int:
31559 getValue: function (dataView, dataOffset) {
31560 return dataView.getUint8(dataOffset);
31564 // ascii, 8-bit byte:
31566 getValue: function (dataView, dataOffset) {
31567 return String.fromCharCode(dataView.getUint8(dataOffset));
31572 // short, 16 bit int:
31574 getValue: function (dataView, dataOffset, littleEndian) {
31575 return dataView.getUint16(dataOffset, littleEndian);
31579 // long, 32 bit int:
31581 getValue: function (dataView, dataOffset, littleEndian) {
31582 return dataView.getUint32(dataOffset, littleEndian);
31586 // rational = two long values, first is numerator, second is denominator:
31588 getValue: function (dataView, dataOffset, littleEndian) {
31589 return dataView.getUint32(dataOffset, littleEndian) /
31590 dataView.getUint32(dataOffset + 4, littleEndian);
31594 // slong, 32 bit signed int:
31596 getValue: function (dataView, dataOffset, littleEndian) {
31597 return dataView.getInt32(dataOffset, littleEndian);
31601 // srational, two slongs, first is numerator, second is denominator:
31603 getValue: function (dataView, dataOffset, littleEndian) {
31604 return dataView.getInt32(dataOffset, littleEndian) /
31605 dataView.getInt32(dataOffset + 4, littleEndian);
31615 cls : 'btn-group roo-upload-cropbox-rotate-left',
31616 action : 'rotate-left',
31620 cls : 'btn btn-default',
31621 html : '<i class="fa fa-undo"></i>'
31627 cls : 'btn-group roo-upload-cropbox-picture',
31628 action : 'picture',
31632 cls : 'btn btn-default',
31633 html : '<i class="fa fa-picture-o"></i>'
31639 cls : 'btn-group roo-upload-cropbox-rotate-right',
31640 action : 'rotate-right',
31644 cls : 'btn btn-default',
31645 html : '<i class="fa fa-repeat"></i>'
31653 cls : 'btn-group roo-upload-cropbox-rotate-left',
31654 action : 'rotate-left',
31658 cls : 'btn btn-default',
31659 html : '<i class="fa fa-undo"></i>'
31665 cls : 'btn-group roo-upload-cropbox-download',
31666 action : 'download',
31670 cls : 'btn btn-default',
31671 html : '<i class="fa fa-download"></i>'
31677 cls : 'btn-group roo-upload-cropbox-crop',
31682 cls : 'btn btn-default',
31683 html : '<i class="fa fa-crop"></i>'
31689 cls : 'btn-group roo-upload-cropbox-trash',
31694 cls : 'btn btn-default',
31695 html : '<i class="fa fa-trash"></i>'
31701 cls : 'btn-group roo-upload-cropbox-rotate-right',
31702 action : 'rotate-right',
31706 cls : 'btn btn-default',
31707 html : '<i class="fa fa-repeat"></i>'
31715 cls : 'btn-group roo-upload-cropbox-rotate-left',
31716 action : 'rotate-left',
31720 cls : 'btn btn-default',
31721 html : '<i class="fa fa-undo"></i>'
31727 cls : 'btn-group roo-upload-cropbox-rotate-right',
31728 action : 'rotate-right',
31732 cls : 'btn btn-default',
31733 html : '<i class="fa fa-repeat"></i>'
31746 * @class Roo.bootstrap.DocumentManager
31747 * @extends Roo.bootstrap.Component
31748 * Bootstrap DocumentManager class
31749 * @cfg {String} paramName default 'imageUpload'
31750 * @cfg {String} toolTipName default 'filename'
31751 * @cfg {String} method default POST
31752 * @cfg {String} url action url
31753 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31754 * @cfg {Boolean} multiple multiple upload default true
31755 * @cfg {Number} thumbSize default 300
31756 * @cfg {String} fieldLabel
31757 * @cfg {Number} labelWidth default 4
31758 * @cfg {String} labelAlign (left|top) default left
31759 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31760 * @cfg {Number} labellg set the width of label (1-12)
31761 * @cfg {Number} labelmd set the width of label (1-12)
31762 * @cfg {Number} labelsm set the width of label (1-12)
31763 * @cfg {Number} labelxs set the width of label (1-12)
31766 * Create a new DocumentManager
31767 * @param {Object} config The config object
31770 Roo.bootstrap.DocumentManager = function(config){
31771 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31774 this.delegates = [];
31779 * Fire when initial the DocumentManager
31780 * @param {Roo.bootstrap.DocumentManager} this
31785 * inspect selected file
31786 * @param {Roo.bootstrap.DocumentManager} this
31787 * @param {File} file
31792 * Fire when xhr load exception
31793 * @param {Roo.bootstrap.DocumentManager} this
31794 * @param {XMLHttpRequest} xhr
31796 "exception" : true,
31798 * @event afterupload
31799 * Fire when xhr load exception
31800 * @param {Roo.bootstrap.DocumentManager} this
31801 * @param {XMLHttpRequest} xhr
31803 "afterupload" : true,
31806 * prepare the form data
31807 * @param {Roo.bootstrap.DocumentManager} this
31808 * @param {Object} formData
31813 * Fire when remove the file
31814 * @param {Roo.bootstrap.DocumentManager} this
31815 * @param {Object} file
31820 * Fire after refresh the file
31821 * @param {Roo.bootstrap.DocumentManager} this
31826 * Fire after click the image
31827 * @param {Roo.bootstrap.DocumentManager} this
31828 * @param {Object} file
31833 * Fire when upload a image and editable set to true
31834 * @param {Roo.bootstrap.DocumentManager} this
31835 * @param {Object} file
31839 * @event beforeselectfile
31840 * Fire before select file
31841 * @param {Roo.bootstrap.DocumentManager} this
31843 "beforeselectfile" : true,
31846 * Fire before process file
31847 * @param {Roo.bootstrap.DocumentManager} this
31848 * @param {Object} file
31852 * @event previewrendered
31853 * Fire when preview rendered
31854 * @param {Roo.bootstrap.DocumentManager} this
31855 * @param {Object} file
31857 "previewrendered" : true,
31860 "previewResize" : true
31865 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31874 paramName : 'imageUpload',
31875 toolTipName : 'filename',
31878 labelAlign : 'left',
31888 getAutoCreate : function()
31890 var managerWidget = {
31892 cls : 'roo-document-manager',
31896 cls : 'roo-document-manager-selector',
31901 cls : 'roo-document-manager-uploader',
31905 cls : 'roo-document-manager-upload-btn',
31906 html : '<i class="fa fa-plus"></i>'
31917 cls : 'column col-md-12',
31922 if(this.fieldLabel.length){
31927 cls : 'column col-md-12',
31928 html : this.fieldLabel
31932 cls : 'column col-md-12',
31937 if(this.labelAlign == 'left'){
31942 html : this.fieldLabel
31951 if(this.labelWidth > 12){
31952 content[0].style = "width: " + this.labelWidth + 'px';
31955 if(this.labelWidth < 13 && this.labelmd == 0){
31956 this.labelmd = this.labelWidth;
31959 if(this.labellg > 0){
31960 content[0].cls += ' col-lg-' + this.labellg;
31961 content[1].cls += ' col-lg-' + (12 - this.labellg);
31964 if(this.labelmd > 0){
31965 content[0].cls += ' col-md-' + this.labelmd;
31966 content[1].cls += ' col-md-' + (12 - this.labelmd);
31969 if(this.labelsm > 0){
31970 content[0].cls += ' col-sm-' + this.labelsm;
31971 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31974 if(this.labelxs > 0){
31975 content[0].cls += ' col-xs-' + this.labelxs;
31976 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31984 cls : 'row clearfix',
31992 initEvents : function()
31994 this.managerEl = this.el.select('.roo-document-manager', true).first();
31995 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31997 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31998 this.selectorEl.hide();
32001 this.selectorEl.attr('multiple', 'multiple');
32004 this.selectorEl.on('change', this.onFileSelected, this);
32006 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32007 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32009 this.uploader.on('click', this.onUploaderClick, this);
32011 this.renderProgressDialog();
32015 window.addEventListener("resize", function() { _this.refresh(); } );
32017 this.fireEvent('initial', this);
32020 renderProgressDialog : function()
32024 this.progressDialog = new Roo.bootstrap.Modal({
32025 cls : 'roo-document-manager-progress-dialog',
32026 allow_close : false,
32037 btnclick : function() {
32038 _this.uploadCancel();
32044 this.progressDialog.render(Roo.get(document.body));
32046 this.progress = new Roo.bootstrap.Progress({
32047 cls : 'roo-document-manager-progress',
32052 this.progress.render(this.progressDialog.getChildContainer());
32054 this.progressBar = new Roo.bootstrap.ProgressBar({
32055 cls : 'roo-document-manager-progress-bar',
32058 aria_valuemax : 12,
32062 this.progressBar.render(this.progress.getChildContainer());
32065 onUploaderClick : function(e)
32067 e.preventDefault();
32069 if(this.fireEvent('beforeselectfile', this) != false){
32070 this.selectorEl.dom.click();
32075 onFileSelected : function(e)
32077 e.preventDefault();
32079 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32083 Roo.each(this.selectorEl.dom.files, function(file){
32084 if(this.fireEvent('inspect', this, file) != false){
32085 this.files.push(file);
32095 this.selectorEl.dom.value = '';
32097 if(!this.files || !this.files.length){
32101 if(this.boxes > 0 && this.files.length > this.boxes){
32102 this.files = this.files.slice(0, this.boxes);
32105 this.uploader.show();
32107 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32108 this.uploader.hide();
32117 Roo.each(this.files, function(file){
32119 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32120 var f = this.renderPreview(file);
32125 if(file.type.indexOf('image') != -1){
32126 this.delegates.push(
32128 _this.process(file);
32129 }).createDelegate(this)
32137 _this.process(file);
32138 }).createDelegate(this)
32143 this.files = files;
32145 this.delegates = this.delegates.concat(docs);
32147 if(!this.delegates.length){
32152 this.progressBar.aria_valuemax = this.delegates.length;
32159 arrange : function()
32161 if(!this.delegates.length){
32162 this.progressDialog.hide();
32167 var delegate = this.delegates.shift();
32169 this.progressDialog.show();
32171 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32173 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32178 refresh : function()
32180 this.uploader.show();
32182 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32183 this.uploader.hide();
32186 Roo.isTouch ? this.closable(false) : this.closable(true);
32188 this.fireEvent('refresh', this);
32191 onRemove : function(e, el, o)
32193 e.preventDefault();
32195 this.fireEvent('remove', this, o);
32199 remove : function(o)
32203 Roo.each(this.files, function(file){
32204 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32213 this.files = files;
32220 Roo.each(this.files, function(file){
32225 file.target.remove();
32234 onClick : function(e, el, o)
32236 e.preventDefault();
32238 this.fireEvent('click', this, o);
32242 closable : function(closable)
32244 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32246 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32258 xhrOnLoad : function(xhr)
32260 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32264 if (xhr.readyState !== 4) {
32266 this.fireEvent('exception', this, xhr);
32270 var response = Roo.decode(xhr.responseText);
32272 if(!response.success){
32274 this.fireEvent('exception', this, xhr);
32278 var file = this.renderPreview(response.data);
32280 this.files.push(file);
32284 this.fireEvent('afterupload', this, xhr);
32288 xhrOnError : function(xhr)
32290 Roo.log('xhr on error');
32292 var response = Roo.decode(xhr.responseText);
32299 process : function(file)
32301 if(this.fireEvent('process', this, file) !== false){
32302 if(this.editable && file.type.indexOf('image') != -1){
32303 this.fireEvent('edit', this, file);
32307 this.uploadStart(file, false);
32314 uploadStart : function(file, crop)
32316 this.xhr = new XMLHttpRequest();
32318 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32323 file.xhr = this.xhr;
32325 this.managerEl.createChild({
32327 cls : 'roo-document-manager-loading',
32331 tooltip : file.name,
32332 cls : 'roo-document-manager-thumb',
32333 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32339 this.xhr.open(this.method, this.url, true);
32342 "Accept": "application/json",
32343 "Cache-Control": "no-cache",
32344 "X-Requested-With": "XMLHttpRequest"
32347 for (var headerName in headers) {
32348 var headerValue = headers[headerName];
32350 this.xhr.setRequestHeader(headerName, headerValue);
32356 this.xhr.onload = function()
32358 _this.xhrOnLoad(_this.xhr);
32361 this.xhr.onerror = function()
32363 _this.xhrOnError(_this.xhr);
32366 var formData = new FormData();
32368 formData.append('returnHTML', 'NO');
32371 formData.append('crop', crop);
32374 formData.append(this.paramName, file, file.name);
32381 if(this.fireEvent('prepare', this, formData, options) != false){
32383 if(options.manually){
32387 this.xhr.send(formData);
32391 this.uploadCancel();
32394 uploadCancel : function()
32400 this.delegates = [];
32402 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32409 renderPreview : function(file)
32411 if(typeof(file.target) != 'undefined' && file.target){
32415 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32417 var previewEl = this.managerEl.createChild({
32419 cls : 'roo-document-manager-preview',
32423 tooltip : file[this.toolTipName],
32424 cls : 'roo-document-manager-thumb',
32425 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32430 html : '<i class="fa fa-times-circle"></i>'
32435 var close = previewEl.select('button.close', true).first();
32437 close.on('click', this.onRemove, this, file);
32439 file.target = previewEl;
32441 var image = previewEl.select('img', true).first();
32445 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32447 image.on('click', this.onClick, this, file);
32449 this.fireEvent('previewrendered', this, file);
32455 onPreviewLoad : function(file, image)
32457 if(typeof(file.target) == 'undefined' || !file.target){
32461 var width = image.dom.naturalWidth || image.dom.width;
32462 var height = image.dom.naturalHeight || image.dom.height;
32464 if(!this.previewResize) {
32468 if(width > height){
32469 file.target.addClass('wide');
32473 file.target.addClass('tall');
32478 uploadFromSource : function(file, crop)
32480 this.xhr = new XMLHttpRequest();
32482 this.managerEl.createChild({
32484 cls : 'roo-document-manager-loading',
32488 tooltip : file.name,
32489 cls : 'roo-document-manager-thumb',
32490 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32496 this.xhr.open(this.method, this.url, true);
32499 "Accept": "application/json",
32500 "Cache-Control": "no-cache",
32501 "X-Requested-With": "XMLHttpRequest"
32504 for (var headerName in headers) {
32505 var headerValue = headers[headerName];
32507 this.xhr.setRequestHeader(headerName, headerValue);
32513 this.xhr.onload = function()
32515 _this.xhrOnLoad(_this.xhr);
32518 this.xhr.onerror = function()
32520 _this.xhrOnError(_this.xhr);
32523 var formData = new FormData();
32525 formData.append('returnHTML', 'NO');
32527 formData.append('crop', crop);
32529 if(typeof(file.filename) != 'undefined'){
32530 formData.append('filename', file.filename);
32533 if(typeof(file.mimetype) != 'undefined'){
32534 formData.append('mimetype', file.mimetype);
32539 if(this.fireEvent('prepare', this, formData) != false){
32540 this.xhr.send(formData);
32550 * @class Roo.bootstrap.DocumentViewer
32551 * @extends Roo.bootstrap.Component
32552 * Bootstrap DocumentViewer class
32553 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32554 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32557 * Create a new DocumentViewer
32558 * @param {Object} config The config object
32561 Roo.bootstrap.DocumentViewer = function(config){
32562 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32567 * Fire after initEvent
32568 * @param {Roo.bootstrap.DocumentViewer} this
32574 * @param {Roo.bootstrap.DocumentViewer} this
32579 * Fire after download button
32580 * @param {Roo.bootstrap.DocumentViewer} this
32585 * Fire after trash button
32586 * @param {Roo.bootstrap.DocumentViewer} this
32593 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32595 showDownload : true,
32599 getAutoCreate : function()
32603 cls : 'roo-document-viewer',
32607 cls : 'roo-document-viewer-body',
32611 cls : 'roo-document-viewer-thumb',
32615 cls : 'roo-document-viewer-image'
32623 cls : 'roo-document-viewer-footer',
32626 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32630 cls : 'btn-group roo-document-viewer-download',
32634 cls : 'btn btn-default',
32635 html : '<i class="fa fa-download"></i>'
32641 cls : 'btn-group roo-document-viewer-trash',
32645 cls : 'btn btn-default',
32646 html : '<i class="fa fa-trash"></i>'
32659 initEvents : function()
32661 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32662 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32664 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32665 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32667 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32668 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32670 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32671 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32673 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32674 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32676 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32677 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32679 this.bodyEl.on('click', this.onClick, this);
32680 this.downloadBtn.on('click', this.onDownload, this);
32681 this.trashBtn.on('click', this.onTrash, this);
32683 this.downloadBtn.hide();
32684 this.trashBtn.hide();
32686 if(this.showDownload){
32687 this.downloadBtn.show();
32690 if(this.showTrash){
32691 this.trashBtn.show();
32694 if(!this.showDownload && !this.showTrash) {
32695 this.footerEl.hide();
32700 initial : function()
32702 this.fireEvent('initial', this);
32706 onClick : function(e)
32708 e.preventDefault();
32710 this.fireEvent('click', this);
32713 onDownload : function(e)
32715 e.preventDefault();
32717 this.fireEvent('download', this);
32720 onTrash : function(e)
32722 e.preventDefault();
32724 this.fireEvent('trash', this);
32736 * @class Roo.bootstrap.NavProgressBar
32737 * @extends Roo.bootstrap.Component
32738 * Bootstrap NavProgressBar class
32741 * Create a new nav progress bar
32742 * @param {Object} config The config object
32745 Roo.bootstrap.NavProgressBar = function(config){
32746 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32748 this.bullets = this.bullets || [];
32750 // Roo.bootstrap.NavProgressBar.register(this);
32754 * Fires when the active item changes
32755 * @param {Roo.bootstrap.NavProgressBar} this
32756 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32757 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32764 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32769 getAutoCreate : function()
32771 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32775 cls : 'roo-navigation-bar-group',
32779 cls : 'roo-navigation-top-bar'
32783 cls : 'roo-navigation-bullets-bar',
32787 cls : 'roo-navigation-bar'
32794 cls : 'roo-navigation-bottom-bar'
32804 initEvents: function()
32809 onRender : function(ct, position)
32811 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32813 if(this.bullets.length){
32814 Roo.each(this.bullets, function(b){
32823 addItem : function(cfg)
32825 var item = new Roo.bootstrap.NavProgressItem(cfg);
32827 item.parentId = this.id;
32828 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32831 var top = new Roo.bootstrap.Element({
32833 cls : 'roo-navigation-bar-text'
32836 var bottom = new Roo.bootstrap.Element({
32838 cls : 'roo-navigation-bar-text'
32841 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32842 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32844 var topText = new Roo.bootstrap.Element({
32846 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32849 var bottomText = new Roo.bootstrap.Element({
32851 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32854 topText.onRender(top.el, null);
32855 bottomText.onRender(bottom.el, null);
32858 item.bottomEl = bottom;
32861 this.barItems.push(item);
32866 getActive : function()
32868 var active = false;
32870 Roo.each(this.barItems, function(v){
32872 if (!v.isActive()) {
32884 setActiveItem : function(item)
32888 Roo.each(this.barItems, function(v){
32889 if (v.rid == item.rid) {
32893 if (v.isActive()) {
32894 v.setActive(false);
32899 item.setActive(true);
32901 this.fireEvent('changed', this, item, prev);
32904 getBarItem: function(rid)
32908 Roo.each(this.barItems, function(e) {
32909 if (e.rid != rid) {
32920 indexOfItem : function(item)
32924 Roo.each(this.barItems, function(v, i){
32926 if (v.rid != item.rid) {
32937 setActiveNext : function()
32939 var i = this.indexOfItem(this.getActive());
32941 if (i > this.barItems.length) {
32945 this.setActiveItem(this.barItems[i+1]);
32948 setActivePrev : function()
32950 var i = this.indexOfItem(this.getActive());
32956 this.setActiveItem(this.barItems[i-1]);
32959 format : function()
32961 if(!this.barItems.length){
32965 var width = 100 / this.barItems.length;
32967 Roo.each(this.barItems, function(i){
32968 i.el.setStyle('width', width + '%');
32969 i.topEl.el.setStyle('width', width + '%');
32970 i.bottomEl.el.setStyle('width', width + '%');
32979 * Nav Progress Item
32984 * @class Roo.bootstrap.NavProgressItem
32985 * @extends Roo.bootstrap.Component
32986 * Bootstrap NavProgressItem class
32987 * @cfg {String} rid the reference id
32988 * @cfg {Boolean} active (true|false) Is item active default false
32989 * @cfg {Boolean} disabled (true|false) Is item active default false
32990 * @cfg {String} html
32991 * @cfg {String} position (top|bottom) text position default bottom
32992 * @cfg {String} icon show icon instead of number
32995 * Create a new NavProgressItem
32996 * @param {Object} config The config object
32998 Roo.bootstrap.NavProgressItem = function(config){
32999 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33004 * The raw click event for the entire grid.
33005 * @param {Roo.bootstrap.NavProgressItem} this
33006 * @param {Roo.EventObject} e
33013 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33019 position : 'bottom',
33022 getAutoCreate : function()
33024 var iconCls = 'roo-navigation-bar-item-icon';
33026 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33030 cls: 'roo-navigation-bar-item',
33040 cfg.cls += ' active';
33043 cfg.cls += ' disabled';
33049 disable : function()
33051 this.setDisabled(true);
33054 enable : function()
33056 this.setDisabled(false);
33059 initEvents: function()
33061 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33063 this.iconEl.on('click', this.onClick, this);
33066 onClick : function(e)
33068 e.preventDefault();
33074 if(this.fireEvent('click', this, e) === false){
33078 this.parent().setActiveItem(this);
33081 isActive: function ()
33083 return this.active;
33086 setActive : function(state)
33088 if(this.active == state){
33092 this.active = state;
33095 this.el.addClass('active');
33099 this.el.removeClass('active');
33104 setDisabled : function(state)
33106 if(this.disabled == state){
33110 this.disabled = state;
33113 this.el.addClass('disabled');
33117 this.el.removeClass('disabled');
33120 tooltipEl : function()
33122 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33135 * @class Roo.bootstrap.FieldLabel
33136 * @extends Roo.bootstrap.Component
33137 * Bootstrap FieldLabel class
33138 * @cfg {String} html contents of the element
33139 * @cfg {String} tag tag of the element default label
33140 * @cfg {String} cls class of the element
33141 * @cfg {String} target label target
33142 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33143 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33144 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33145 * @cfg {String} iconTooltip default "This field is required"
33146 * @cfg {String} indicatorpos (left|right) default left
33149 * Create a new FieldLabel
33150 * @param {Object} config The config object
33153 Roo.bootstrap.FieldLabel = function(config){
33154 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33159 * Fires after the field has been marked as invalid.
33160 * @param {Roo.form.FieldLabel} this
33161 * @param {String} msg The validation message
33166 * Fires after the field has been validated with no errors.
33167 * @param {Roo.form.FieldLabel} this
33173 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33180 invalidClass : 'has-warning',
33181 validClass : 'has-success',
33182 iconTooltip : 'This field is required',
33183 indicatorpos : 'left',
33185 getAutoCreate : function(){
33188 if (!this.allowBlank) {
33194 cls : 'roo-bootstrap-field-label ' + this.cls,
33199 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33200 tooltip : this.iconTooltip
33209 if(this.indicatorpos == 'right'){
33212 cls : 'roo-bootstrap-field-label ' + this.cls,
33221 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33222 tooltip : this.iconTooltip
33231 initEvents: function()
33233 Roo.bootstrap.Element.superclass.initEvents.call(this);
33235 this.indicator = this.indicatorEl();
33237 if(this.indicator){
33238 this.indicator.removeClass('visible');
33239 this.indicator.addClass('invisible');
33242 Roo.bootstrap.FieldLabel.register(this);
33245 indicatorEl : function()
33247 var indicator = this.el.select('i.roo-required-indicator',true).first();
33258 * Mark this field as valid
33260 markValid : function()
33262 if(this.indicator){
33263 this.indicator.removeClass('visible');
33264 this.indicator.addClass('invisible');
33266 if (Roo.bootstrap.version == 3) {
33267 this.el.removeClass(this.invalidClass);
33268 this.el.addClass(this.validClass);
33270 this.el.removeClass('is-invalid');
33271 this.el.addClass('is-valid');
33275 this.fireEvent('valid', this);
33279 * Mark this field as invalid
33280 * @param {String} msg The validation message
33282 markInvalid : function(msg)
33284 if(this.indicator){
33285 this.indicator.removeClass('invisible');
33286 this.indicator.addClass('visible');
33288 if (Roo.bootstrap.version == 3) {
33289 this.el.removeClass(this.validClass);
33290 this.el.addClass(this.invalidClass);
33292 this.el.removeClass('is-valid');
33293 this.el.addClass('is-invalid');
33297 this.fireEvent('invalid', this, msg);
33303 Roo.apply(Roo.bootstrap.FieldLabel, {
33308 * register a FieldLabel Group
33309 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33311 register : function(label)
33313 if(this.groups.hasOwnProperty(label.target)){
33317 this.groups[label.target] = label;
33321 * fetch a FieldLabel Group based on the target
33322 * @param {string} target
33323 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33325 get: function(target) {
33326 if (typeof(this.groups[target]) == 'undefined') {
33330 return this.groups[target] ;
33339 * page DateSplitField.
33345 * @class Roo.bootstrap.DateSplitField
33346 * @extends Roo.bootstrap.Component
33347 * Bootstrap DateSplitField class
33348 * @cfg {string} fieldLabel - the label associated
33349 * @cfg {Number} labelWidth set the width of label (0-12)
33350 * @cfg {String} labelAlign (top|left)
33351 * @cfg {Boolean} dayAllowBlank (true|false) default false
33352 * @cfg {Boolean} monthAllowBlank (true|false) default false
33353 * @cfg {Boolean} yearAllowBlank (true|false) default false
33354 * @cfg {string} dayPlaceholder
33355 * @cfg {string} monthPlaceholder
33356 * @cfg {string} yearPlaceholder
33357 * @cfg {string} dayFormat default 'd'
33358 * @cfg {string} monthFormat default 'm'
33359 * @cfg {string} yearFormat default 'Y'
33360 * @cfg {Number} labellg set the width of label (1-12)
33361 * @cfg {Number} labelmd set the width of label (1-12)
33362 * @cfg {Number} labelsm set the width of label (1-12)
33363 * @cfg {Number} labelxs set the width of label (1-12)
33367 * Create a new DateSplitField
33368 * @param {Object} config The config object
33371 Roo.bootstrap.DateSplitField = function(config){
33372 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33378 * getting the data of years
33379 * @param {Roo.bootstrap.DateSplitField} this
33380 * @param {Object} years
33385 * getting the data of days
33386 * @param {Roo.bootstrap.DateSplitField} this
33387 * @param {Object} days
33392 * Fires after the field has been marked as invalid.
33393 * @param {Roo.form.Field} this
33394 * @param {String} msg The validation message
33399 * Fires after the field has been validated with no errors.
33400 * @param {Roo.form.Field} this
33406 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33409 labelAlign : 'top',
33411 dayAllowBlank : false,
33412 monthAllowBlank : false,
33413 yearAllowBlank : false,
33414 dayPlaceholder : '',
33415 monthPlaceholder : '',
33416 yearPlaceholder : '',
33420 isFormField : true,
33426 getAutoCreate : function()
33430 cls : 'row roo-date-split-field-group',
33435 cls : 'form-hidden-field roo-date-split-field-group-value',
33441 var labelCls = 'col-md-12';
33442 var contentCls = 'col-md-4';
33444 if(this.fieldLabel){
33448 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33452 html : this.fieldLabel
33457 if(this.labelAlign == 'left'){
33459 if(this.labelWidth > 12){
33460 label.style = "width: " + this.labelWidth + 'px';
33463 if(this.labelWidth < 13 && this.labelmd == 0){
33464 this.labelmd = this.labelWidth;
33467 if(this.labellg > 0){
33468 labelCls = ' col-lg-' + this.labellg;
33469 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33472 if(this.labelmd > 0){
33473 labelCls = ' col-md-' + this.labelmd;
33474 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33477 if(this.labelsm > 0){
33478 labelCls = ' col-sm-' + this.labelsm;
33479 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33482 if(this.labelxs > 0){
33483 labelCls = ' col-xs-' + this.labelxs;
33484 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33488 label.cls += ' ' + labelCls;
33490 cfg.cn.push(label);
33493 Roo.each(['day', 'month', 'year'], function(t){
33496 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33503 inputEl: function ()
33505 return this.el.select('.roo-date-split-field-group-value', true).first();
33508 onRender : function(ct, position)
33512 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33514 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33516 this.dayField = new Roo.bootstrap.ComboBox({
33517 allowBlank : this.dayAllowBlank,
33518 alwaysQuery : true,
33519 displayField : 'value',
33522 forceSelection : true,
33524 placeholder : this.dayPlaceholder,
33525 selectOnFocus : true,
33526 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33527 triggerAction : 'all',
33529 valueField : 'value',
33530 store : new Roo.data.SimpleStore({
33531 data : (function() {
33533 _this.fireEvent('days', _this, days);
33536 fields : [ 'value' ]
33539 select : function (_self, record, index)
33541 _this.setValue(_this.getValue());
33546 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33548 this.monthField = new Roo.bootstrap.MonthField({
33549 after : '<i class=\"fa fa-calendar\"></i>',
33550 allowBlank : this.monthAllowBlank,
33551 placeholder : this.monthPlaceholder,
33554 render : function (_self)
33556 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33557 e.preventDefault();
33561 select : function (_self, oldvalue, newvalue)
33563 _this.setValue(_this.getValue());
33568 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33570 this.yearField = new Roo.bootstrap.ComboBox({
33571 allowBlank : this.yearAllowBlank,
33572 alwaysQuery : true,
33573 displayField : 'value',
33576 forceSelection : true,
33578 placeholder : this.yearPlaceholder,
33579 selectOnFocus : true,
33580 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33581 triggerAction : 'all',
33583 valueField : 'value',
33584 store : new Roo.data.SimpleStore({
33585 data : (function() {
33587 _this.fireEvent('years', _this, years);
33590 fields : [ 'value' ]
33593 select : function (_self, record, index)
33595 _this.setValue(_this.getValue());
33600 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33603 setValue : function(v, format)
33605 this.inputEl.dom.value = v;
33607 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33609 var d = Date.parseDate(v, f);
33616 this.setDay(d.format(this.dayFormat));
33617 this.setMonth(d.format(this.monthFormat));
33618 this.setYear(d.format(this.yearFormat));
33625 setDay : function(v)
33627 this.dayField.setValue(v);
33628 this.inputEl.dom.value = this.getValue();
33633 setMonth : function(v)
33635 this.monthField.setValue(v, true);
33636 this.inputEl.dom.value = this.getValue();
33641 setYear : function(v)
33643 this.yearField.setValue(v);
33644 this.inputEl.dom.value = this.getValue();
33649 getDay : function()
33651 return this.dayField.getValue();
33654 getMonth : function()
33656 return this.monthField.getValue();
33659 getYear : function()
33661 return this.yearField.getValue();
33664 getValue : function()
33666 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33668 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33678 this.inputEl.dom.value = '';
33683 validate : function()
33685 var d = this.dayField.validate();
33686 var m = this.monthField.validate();
33687 var y = this.yearField.validate();
33692 (!this.dayAllowBlank && !d) ||
33693 (!this.monthAllowBlank && !m) ||
33694 (!this.yearAllowBlank && !y)
33699 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33708 this.markInvalid();
33713 markValid : function()
33716 var label = this.el.select('label', true).first();
33717 var icon = this.el.select('i.fa-star', true).first();
33723 this.fireEvent('valid', this);
33727 * Mark this field as invalid
33728 * @param {String} msg The validation message
33730 markInvalid : function(msg)
33733 var label = this.el.select('label', true).first();
33734 var icon = this.el.select('i.fa-star', true).first();
33736 if(label && !icon){
33737 this.el.select('.roo-date-split-field-label', true).createChild({
33739 cls : 'text-danger fa fa-lg fa-star',
33740 tooltip : 'This field is required',
33741 style : 'margin-right:5px;'
33745 this.fireEvent('invalid', this, msg);
33748 clearInvalid : function()
33750 var label = this.el.select('label', true).first();
33751 var icon = this.el.select('i.fa-star', true).first();
33757 this.fireEvent('valid', this);
33760 getName: function()
33770 * http://masonry.desandro.com
33772 * The idea is to render all the bricks based on vertical width...
33774 * The original code extends 'outlayer' - we might need to use that....
33780 * @class Roo.bootstrap.LayoutMasonry
33781 * @extends Roo.bootstrap.Component
33782 * Bootstrap Layout Masonry class
33785 * Create a new Element
33786 * @param {Object} config The config object
33789 Roo.bootstrap.LayoutMasonry = function(config){
33791 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33795 Roo.bootstrap.LayoutMasonry.register(this);
33801 * Fire after layout the items
33802 * @param {Roo.bootstrap.LayoutMasonry} this
33803 * @param {Roo.EventObject} e
33810 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33813 * @cfg {Boolean} isLayoutInstant = no animation?
33815 isLayoutInstant : false, // needed?
33818 * @cfg {Number} boxWidth width of the columns
33823 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33828 * @cfg {Number} padWidth padding below box..
33833 * @cfg {Number} gutter gutter width..
33838 * @cfg {Number} maxCols maximum number of columns
33844 * @cfg {Boolean} isAutoInitial defalut true
33846 isAutoInitial : true,
33851 * @cfg {Boolean} isHorizontal defalut false
33853 isHorizontal : false,
33855 currentSize : null,
33861 bricks: null, //CompositeElement
33865 _isLayoutInited : false,
33867 // isAlternative : false, // only use for vertical layout...
33870 * @cfg {Number} alternativePadWidth padding below box..
33872 alternativePadWidth : 50,
33874 selectedBrick : [],
33876 getAutoCreate : function(){
33878 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33882 cls: 'blog-masonary-wrapper ' + this.cls,
33884 cls : 'mas-boxes masonary'
33891 getChildContainer: function( )
33893 if (this.boxesEl) {
33894 return this.boxesEl;
33897 this.boxesEl = this.el.select('.mas-boxes').first();
33899 return this.boxesEl;
33903 initEvents : function()
33907 if(this.isAutoInitial){
33908 Roo.log('hook children rendered');
33909 this.on('childrenrendered', function() {
33910 Roo.log('children rendered');
33916 initial : function()
33918 this.selectedBrick = [];
33920 this.currentSize = this.el.getBox(true);
33922 Roo.EventManager.onWindowResize(this.resize, this);
33924 if(!this.isAutoInitial){
33932 //this.layout.defer(500,this);
33936 resize : function()
33938 var cs = this.el.getBox(true);
33941 this.currentSize.width == cs.width &&
33942 this.currentSize.x == cs.x &&
33943 this.currentSize.height == cs.height &&
33944 this.currentSize.y == cs.y
33946 Roo.log("no change in with or X or Y");
33950 this.currentSize = cs;
33956 layout : function()
33958 this._resetLayout();
33960 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33962 this.layoutItems( isInstant );
33964 this._isLayoutInited = true;
33966 this.fireEvent('layout', this);
33970 _resetLayout : function()
33972 if(this.isHorizontal){
33973 this.horizontalMeasureColumns();
33977 this.verticalMeasureColumns();
33981 verticalMeasureColumns : function()
33983 this.getContainerWidth();
33985 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33986 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33990 var boxWidth = this.boxWidth + this.padWidth;
33992 if(this.containerWidth < this.boxWidth){
33993 boxWidth = this.containerWidth
33996 var containerWidth = this.containerWidth;
33998 var cols = Math.floor(containerWidth / boxWidth);
34000 this.cols = Math.max( cols, 1 );
34002 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34004 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34006 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34008 this.colWidth = boxWidth + avail - this.padWidth;
34010 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34011 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34014 horizontalMeasureColumns : function()
34016 this.getContainerWidth();
34018 var boxWidth = this.boxWidth;
34020 if(this.containerWidth < boxWidth){
34021 boxWidth = this.containerWidth;
34024 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34026 this.el.setHeight(boxWidth);
34030 getContainerWidth : function()
34032 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34035 layoutItems : function( isInstant )
34037 Roo.log(this.bricks);
34039 var items = Roo.apply([], this.bricks);
34041 if(this.isHorizontal){
34042 this._horizontalLayoutItems( items , isInstant );
34046 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34047 // this._verticalAlternativeLayoutItems( items , isInstant );
34051 this._verticalLayoutItems( items , isInstant );
34055 _verticalLayoutItems : function ( items , isInstant)
34057 if ( !items || !items.length ) {
34062 ['xs', 'xs', 'xs', 'tall'],
34063 ['xs', 'xs', 'tall'],
34064 ['xs', 'xs', 'sm'],
34065 ['xs', 'xs', 'xs'],
34071 ['sm', 'xs', 'xs'],
34075 ['tall', 'xs', 'xs', 'xs'],
34076 ['tall', 'xs', 'xs'],
34088 Roo.each(items, function(item, k){
34090 switch (item.size) {
34091 // these layouts take up a full box,
34102 boxes.push([item]);
34125 var filterPattern = function(box, length)
34133 var pattern = box.slice(0, length);
34137 Roo.each(pattern, function(i){
34138 format.push(i.size);
34141 Roo.each(standard, function(s){
34143 if(String(s) != String(format)){
34152 if(!match && length == 1){
34157 filterPattern(box, length - 1);
34161 queue.push(pattern);
34163 box = box.slice(length, box.length);
34165 filterPattern(box, 4);
34171 Roo.each(boxes, function(box, k){
34177 if(box.length == 1){
34182 filterPattern(box, 4);
34186 this._processVerticalLayoutQueue( queue, isInstant );
34190 // _verticalAlternativeLayoutItems : function( items , isInstant )
34192 // if ( !items || !items.length ) {
34196 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34200 _horizontalLayoutItems : function ( items , isInstant)
34202 if ( !items || !items.length || items.length < 3) {
34208 var eItems = items.slice(0, 3);
34210 items = items.slice(3, items.length);
34213 ['xs', 'xs', 'xs', 'wide'],
34214 ['xs', 'xs', 'wide'],
34215 ['xs', 'xs', 'sm'],
34216 ['xs', 'xs', 'xs'],
34222 ['sm', 'xs', 'xs'],
34226 ['wide', 'xs', 'xs', 'xs'],
34227 ['wide', 'xs', 'xs'],
34240 Roo.each(items, function(item, k){
34242 switch (item.size) {
34253 boxes.push([item]);
34277 var filterPattern = function(box, length)
34285 var pattern = box.slice(0, length);
34289 Roo.each(pattern, function(i){
34290 format.push(i.size);
34293 Roo.each(standard, function(s){
34295 if(String(s) != String(format)){
34304 if(!match && length == 1){
34309 filterPattern(box, length - 1);
34313 queue.push(pattern);
34315 box = box.slice(length, box.length);
34317 filterPattern(box, 4);
34323 Roo.each(boxes, function(box, k){
34329 if(box.length == 1){
34334 filterPattern(box, 4);
34341 var pos = this.el.getBox(true);
34345 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34347 var hit_end = false;
34349 Roo.each(queue, function(box){
34353 Roo.each(box, function(b){
34355 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34365 Roo.each(box, function(b){
34367 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34370 mx = Math.max(mx, b.x);
34374 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34378 Roo.each(box, function(b){
34380 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34394 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34397 /** Sets position of item in DOM
34398 * @param {Element} item
34399 * @param {Number} x - horizontal position
34400 * @param {Number} y - vertical position
34401 * @param {Boolean} isInstant - disables transitions
34403 _processVerticalLayoutQueue : function( queue, isInstant )
34405 var pos = this.el.getBox(true);
34410 for (var i = 0; i < this.cols; i++){
34414 Roo.each(queue, function(box, k){
34416 var col = k % this.cols;
34418 Roo.each(box, function(b,kk){
34420 b.el.position('absolute');
34422 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34423 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34425 if(b.size == 'md-left' || b.size == 'md-right'){
34426 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34427 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34430 b.el.setWidth(width);
34431 b.el.setHeight(height);
34433 b.el.select('iframe',true).setSize(width,height);
34437 for (var i = 0; i < this.cols; i++){
34439 if(maxY[i] < maxY[col]){
34444 col = Math.min(col, i);
34448 x = pos.x + col * (this.colWidth + this.padWidth);
34452 var positions = [];
34454 switch (box.length){
34456 positions = this.getVerticalOneBoxColPositions(x, y, box);
34459 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34462 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34465 positions = this.getVerticalFourBoxColPositions(x, y, box);
34471 Roo.each(box, function(b,kk){
34473 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34475 var sz = b.el.getSize();
34477 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34485 for (var i = 0; i < this.cols; i++){
34486 mY = Math.max(mY, maxY[i]);
34489 this.el.setHeight(mY - pos.y);
34493 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34495 // var pos = this.el.getBox(true);
34498 // var maxX = pos.right;
34500 // var maxHeight = 0;
34502 // Roo.each(items, function(item, k){
34506 // item.el.position('absolute');
34508 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34510 // item.el.setWidth(width);
34512 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34514 // item.el.setHeight(height);
34517 // item.el.setXY([x, y], isInstant ? false : true);
34519 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34522 // y = y + height + this.alternativePadWidth;
34524 // maxHeight = maxHeight + height + this.alternativePadWidth;
34528 // this.el.setHeight(maxHeight);
34532 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34534 var pos = this.el.getBox(true);
34539 var maxX = pos.right;
34541 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34543 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34545 Roo.each(queue, function(box, k){
34547 Roo.each(box, function(b, kk){
34549 b.el.position('absolute');
34551 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34552 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34554 if(b.size == 'md-left' || b.size == 'md-right'){
34555 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34556 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34559 b.el.setWidth(width);
34560 b.el.setHeight(height);
34568 var positions = [];
34570 switch (box.length){
34572 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34575 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34578 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34581 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34587 Roo.each(box, function(b,kk){
34589 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34591 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34599 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34601 Roo.each(eItems, function(b,k){
34603 b.size = (k == 0) ? 'sm' : 'xs';
34604 b.x = (k == 0) ? 2 : 1;
34605 b.y = (k == 0) ? 2 : 1;
34607 b.el.position('absolute');
34609 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34611 b.el.setWidth(width);
34613 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34615 b.el.setHeight(height);
34619 var positions = [];
34622 x : maxX - this.unitWidth * 2 - this.gutter,
34627 x : maxX - this.unitWidth,
34628 y : minY + (this.unitWidth + this.gutter) * 2
34632 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34636 Roo.each(eItems, function(b,k){
34638 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34644 getVerticalOneBoxColPositions : function(x, y, box)
34648 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34650 if(box[0].size == 'md-left'){
34654 if(box[0].size == 'md-right'){
34659 x : x + (this.unitWidth + this.gutter) * rand,
34666 getVerticalTwoBoxColPositions : function(x, y, box)
34670 if(box[0].size == 'xs'){
34674 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34678 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34692 x : x + (this.unitWidth + this.gutter) * 2,
34693 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34700 getVerticalThreeBoxColPositions : function(x, y, box)
34704 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34712 x : x + (this.unitWidth + this.gutter) * 1,
34717 x : x + (this.unitWidth + this.gutter) * 2,
34725 if(box[0].size == 'xs' && box[1].size == 'xs'){
34734 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34738 x : x + (this.unitWidth + this.gutter) * 1,
34752 x : x + (this.unitWidth + this.gutter) * 2,
34757 x : x + (this.unitWidth + this.gutter) * 2,
34758 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34765 getVerticalFourBoxColPositions : function(x, y, box)
34769 if(box[0].size == 'xs'){
34778 y : y + (this.unitHeight + this.gutter) * 1
34783 y : y + (this.unitHeight + this.gutter) * 2
34787 x : x + (this.unitWidth + this.gutter) * 1,
34801 x : x + (this.unitWidth + this.gutter) * 2,
34806 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34807 y : y + (this.unitHeight + this.gutter) * 1
34811 x : x + (this.unitWidth + this.gutter) * 2,
34812 y : y + (this.unitWidth + this.gutter) * 2
34819 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34823 if(box[0].size == 'md-left'){
34825 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34832 if(box[0].size == 'md-right'){
34834 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34835 y : minY + (this.unitWidth + this.gutter) * 1
34841 var rand = Math.floor(Math.random() * (4 - box[0].y));
34844 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34845 y : minY + (this.unitWidth + this.gutter) * rand
34852 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34856 if(box[0].size == 'xs'){
34859 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34864 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34865 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34879 y : minY + (this.unitWidth + this.gutter) * 2
34886 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34890 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34893 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34899 y : minY + (this.unitWidth + this.gutter) * 1
34903 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34904 y : minY + (this.unitWidth + this.gutter) * 2
34911 if(box[0].size == 'xs' && box[1].size == 'xs'){
34914 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34919 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34924 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34925 y : minY + (this.unitWidth + this.gutter) * 1
34933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34938 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34939 y : minY + (this.unitWidth + this.gutter) * 2
34943 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34944 y : minY + (this.unitWidth + this.gutter) * 2
34951 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34955 if(box[0].size == 'xs'){
34958 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34963 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34968 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),
34973 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34974 y : minY + (this.unitWidth + this.gutter) * 1
34982 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34987 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34988 y : minY + (this.unitWidth + this.gutter) * 2
34992 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34993 y : minY + (this.unitWidth + this.gutter) * 2
34997 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),
34998 y : minY + (this.unitWidth + this.gutter) * 2
35006 * remove a Masonry Brick
35007 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35009 removeBrick : function(brick_id)
35015 for (var i = 0; i<this.bricks.length; i++) {
35016 if (this.bricks[i].id == brick_id) {
35017 this.bricks.splice(i,1);
35018 this.el.dom.removeChild(Roo.get(brick_id).dom);
35025 * adds a Masonry Brick
35026 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35028 addBrick : function(cfg)
35030 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35031 //this.register(cn);
35032 cn.parentId = this.id;
35033 cn.render(this.el);
35038 * register a Masonry Brick
35039 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35042 register : function(brick)
35044 this.bricks.push(brick);
35045 brick.masonryId = this.id;
35049 * clear all the Masonry Brick
35051 clearAll : function()
35054 //this.getChildContainer().dom.innerHTML = "";
35055 this.el.dom.innerHTML = '';
35058 getSelected : function()
35060 if (!this.selectedBrick) {
35064 return this.selectedBrick;
35068 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35072 * register a Masonry Layout
35073 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35076 register : function(layout)
35078 this.groups[layout.id] = layout;
35081 * fetch a Masonry Layout based on the masonry layout ID
35082 * @param {string} the masonry layout to add
35083 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35086 get: function(layout_id) {
35087 if (typeof(this.groups[layout_id]) == 'undefined') {
35090 return this.groups[layout_id] ;
35102 * http://masonry.desandro.com
35104 * The idea is to render all the bricks based on vertical width...
35106 * The original code extends 'outlayer' - we might need to use that....
35112 * @class Roo.bootstrap.LayoutMasonryAuto
35113 * @extends Roo.bootstrap.Component
35114 * Bootstrap Layout Masonry class
35117 * Create a new Element
35118 * @param {Object} config The config object
35121 Roo.bootstrap.LayoutMasonryAuto = function(config){
35122 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35125 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35128 * @cfg {Boolean} isFitWidth - resize the width..
35130 isFitWidth : false, // options..
35132 * @cfg {Boolean} isOriginLeft = left align?
35134 isOriginLeft : true,
35136 * @cfg {Boolean} isOriginTop = top align?
35138 isOriginTop : false,
35140 * @cfg {Boolean} isLayoutInstant = no animation?
35142 isLayoutInstant : false, // needed?
35144 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35146 isResizingContainer : true,
35148 * @cfg {Number} columnWidth width of the columns
35154 * @cfg {Number} maxCols maximum number of columns
35159 * @cfg {Number} padHeight padding below box..
35165 * @cfg {Boolean} isAutoInitial defalut true
35168 isAutoInitial : true,
35174 initialColumnWidth : 0,
35175 currentSize : null,
35177 colYs : null, // array.
35184 bricks: null, //CompositeElement
35185 cols : 0, // array?
35186 // element : null, // wrapped now this.el
35187 _isLayoutInited : null,
35190 getAutoCreate : function(){
35194 cls: 'blog-masonary-wrapper ' + this.cls,
35196 cls : 'mas-boxes masonary'
35203 getChildContainer: function( )
35205 if (this.boxesEl) {
35206 return this.boxesEl;
35209 this.boxesEl = this.el.select('.mas-boxes').first();
35211 return this.boxesEl;
35215 initEvents : function()
35219 if(this.isAutoInitial){
35220 Roo.log('hook children rendered');
35221 this.on('childrenrendered', function() {
35222 Roo.log('children rendered');
35229 initial : function()
35231 this.reloadItems();
35233 this.currentSize = this.el.getBox(true);
35235 /// was window resize... - let's see if this works..
35236 Roo.EventManager.onWindowResize(this.resize, this);
35238 if(!this.isAutoInitial){
35243 this.layout.defer(500,this);
35246 reloadItems: function()
35248 this.bricks = this.el.select('.masonry-brick', true);
35250 this.bricks.each(function(b) {
35251 //Roo.log(b.getSize());
35252 if (!b.attr('originalwidth')) {
35253 b.attr('originalwidth', b.getSize().width);
35258 Roo.log(this.bricks.elements.length);
35261 resize : function()
35264 var cs = this.el.getBox(true);
35266 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35267 Roo.log("no change in with or X");
35270 this.currentSize = cs;
35274 layout : function()
35277 this._resetLayout();
35278 //this._manageStamps();
35280 // don't animate first layout
35281 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35282 this.layoutItems( isInstant );
35284 // flag for initalized
35285 this._isLayoutInited = true;
35288 layoutItems : function( isInstant )
35290 //var items = this._getItemsForLayout( this.items );
35291 // original code supports filtering layout items.. we just ignore it..
35293 this._layoutItems( this.bricks , isInstant );
35295 this._postLayout();
35297 _layoutItems : function ( items , isInstant)
35299 //this.fireEvent( 'layout', this, items );
35302 if ( !items || !items.elements.length ) {
35303 // no items, emit event with empty array
35308 items.each(function(item) {
35309 Roo.log("layout item");
35311 // get x/y object from method
35312 var position = this._getItemLayoutPosition( item );
35314 position.item = item;
35315 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35316 queue.push( position );
35319 this._processLayoutQueue( queue );
35321 /** Sets position of item in DOM
35322 * @param {Element} item
35323 * @param {Number} x - horizontal position
35324 * @param {Number} y - vertical position
35325 * @param {Boolean} isInstant - disables transitions
35327 _processLayoutQueue : function( queue )
35329 for ( var i=0, len = queue.length; i < len; i++ ) {
35330 var obj = queue[i];
35331 obj.item.position('absolute');
35332 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35338 * Any logic you want to do after each layout,
35339 * i.e. size the container
35341 _postLayout : function()
35343 this.resizeContainer();
35346 resizeContainer : function()
35348 if ( !this.isResizingContainer ) {
35351 var size = this._getContainerSize();
35353 this.el.setSize(size.width,size.height);
35354 this.boxesEl.setSize(size.width,size.height);
35360 _resetLayout : function()
35362 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35363 this.colWidth = this.el.getWidth();
35364 //this.gutter = this.el.getWidth();
35366 this.measureColumns();
35372 this.colYs.push( 0 );
35378 measureColumns : function()
35380 this.getContainerWidth();
35381 // if columnWidth is 0, default to outerWidth of first item
35382 if ( !this.columnWidth ) {
35383 var firstItem = this.bricks.first();
35384 Roo.log(firstItem);
35385 this.columnWidth = this.containerWidth;
35386 if (firstItem && firstItem.attr('originalwidth') ) {
35387 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35389 // columnWidth fall back to item of first element
35390 Roo.log("set column width?");
35391 this.initialColumnWidth = this.columnWidth ;
35393 // if first elem has no width, default to size of container
35398 if (this.initialColumnWidth) {
35399 this.columnWidth = this.initialColumnWidth;
35404 // column width is fixed at the top - however if container width get's smaller we should
35407 // this bit calcs how man columns..
35409 var columnWidth = this.columnWidth += this.gutter;
35411 // calculate columns
35412 var containerWidth = this.containerWidth + this.gutter;
35414 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35415 // fix rounding errors, typically with gutters
35416 var excess = columnWidth - containerWidth % columnWidth;
35419 // if overshoot is less than a pixel, round up, otherwise floor it
35420 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35421 cols = Math[ mathMethod ]( cols );
35422 this.cols = Math.max( cols, 1 );
35423 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35425 // padding positioning..
35426 var totalColWidth = this.cols * this.columnWidth;
35427 var padavail = this.containerWidth - totalColWidth;
35428 // so for 2 columns - we need 3 'pads'
35430 var padNeeded = (1+this.cols) * this.padWidth;
35432 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35434 this.columnWidth += padExtra
35435 //this.padWidth = Math.floor(padavail / ( this.cols));
35437 // adjust colum width so that padding is fixed??
35439 // we have 3 columns ... total = width * 3
35440 // we have X left over... that should be used by
35442 //if (this.expandC) {
35450 getContainerWidth : function()
35452 /* // container is parent if fit width
35453 var container = this.isFitWidth ? this.element.parentNode : this.element;
35454 // check that this.size and size are there
35455 // IE8 triggers resize on body size change, so they might not be
35457 var size = getSize( container ); //FIXME
35458 this.containerWidth = size && size.innerWidth; //FIXME
35461 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35465 _getItemLayoutPosition : function( item ) // what is item?
35467 // we resize the item to our columnWidth..
35469 item.setWidth(this.columnWidth);
35470 item.autoBoxAdjust = false;
35472 var sz = item.getSize();
35474 // how many columns does this brick span
35475 var remainder = this.containerWidth % this.columnWidth;
35477 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35478 // round if off by 1 pixel, otherwise use ceil
35479 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35480 colSpan = Math.min( colSpan, this.cols );
35482 // normally this should be '1' as we dont' currently allow multi width columns..
35484 var colGroup = this._getColGroup( colSpan );
35485 // get the minimum Y value from the columns
35486 var minimumY = Math.min.apply( Math, colGroup );
35487 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35489 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35491 // position the brick
35493 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35494 y: this.currentSize.y + minimumY + this.padHeight
35498 // apply setHeight to necessary columns
35499 var setHeight = minimumY + sz.height + this.padHeight;
35500 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35502 var setSpan = this.cols + 1 - colGroup.length;
35503 for ( var i = 0; i < setSpan; i++ ) {
35504 this.colYs[ shortColIndex + i ] = setHeight ;
35511 * @param {Number} colSpan - number of columns the element spans
35512 * @returns {Array} colGroup
35514 _getColGroup : function( colSpan )
35516 if ( colSpan < 2 ) {
35517 // if brick spans only one column, use all the column Ys
35522 // how many different places could this brick fit horizontally
35523 var groupCount = this.cols + 1 - colSpan;
35524 // for each group potential horizontal position
35525 for ( var i = 0; i < groupCount; i++ ) {
35526 // make an array of colY values for that one group
35527 var groupColYs = this.colYs.slice( i, i + colSpan );
35528 // and get the max value of the array
35529 colGroup[i] = Math.max.apply( Math, groupColYs );
35534 _manageStamp : function( stamp )
35536 var stampSize = stamp.getSize();
35537 var offset = stamp.getBox();
35538 // get the columns that this stamp affects
35539 var firstX = this.isOriginLeft ? offset.x : offset.right;
35540 var lastX = firstX + stampSize.width;
35541 var firstCol = Math.floor( firstX / this.columnWidth );
35542 firstCol = Math.max( 0, firstCol );
35544 var lastCol = Math.floor( lastX / this.columnWidth );
35545 // lastCol should not go over if multiple of columnWidth #425
35546 lastCol -= lastX % this.columnWidth ? 0 : 1;
35547 lastCol = Math.min( this.cols - 1, lastCol );
35549 // set colYs to bottom of the stamp
35550 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35553 for ( var i = firstCol; i <= lastCol; i++ ) {
35554 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35559 _getContainerSize : function()
35561 this.maxY = Math.max.apply( Math, this.colYs );
35566 if ( this.isFitWidth ) {
35567 size.width = this._getContainerFitWidth();
35573 _getContainerFitWidth : function()
35575 var unusedCols = 0;
35576 // count unused columns
35579 if ( this.colYs[i] !== 0 ) {
35584 // fit container to columns that have been used
35585 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35588 needsResizeLayout : function()
35590 var previousWidth = this.containerWidth;
35591 this.getContainerWidth();
35592 return previousWidth !== this.containerWidth;
35607 * @class Roo.bootstrap.MasonryBrick
35608 * @extends Roo.bootstrap.Component
35609 * Bootstrap MasonryBrick class
35612 * Create a new MasonryBrick
35613 * @param {Object} config The config object
35616 Roo.bootstrap.MasonryBrick = function(config){
35618 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35620 Roo.bootstrap.MasonryBrick.register(this);
35626 * When a MasonryBrick is clcik
35627 * @param {Roo.bootstrap.MasonryBrick} this
35628 * @param {Roo.EventObject} e
35634 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35637 * @cfg {String} title
35641 * @cfg {String} html
35645 * @cfg {String} bgimage
35649 * @cfg {String} videourl
35653 * @cfg {String} cls
35657 * @cfg {String} href
35661 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35666 * @cfg {String} placetitle (center|bottom)
35671 * @cfg {Boolean} isFitContainer defalut true
35673 isFitContainer : true,
35676 * @cfg {Boolean} preventDefault defalut false
35678 preventDefault : false,
35681 * @cfg {Boolean} inverse defalut false
35683 maskInverse : false,
35685 getAutoCreate : function()
35687 if(!this.isFitContainer){
35688 return this.getSplitAutoCreate();
35691 var cls = 'masonry-brick masonry-brick-full';
35693 if(this.href.length){
35694 cls += ' masonry-brick-link';
35697 if(this.bgimage.length){
35698 cls += ' masonry-brick-image';
35701 if(this.maskInverse){
35702 cls += ' mask-inverse';
35705 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35706 cls += ' enable-mask';
35710 cls += ' masonry-' + this.size + '-brick';
35713 if(this.placetitle.length){
35715 switch (this.placetitle) {
35717 cls += ' masonry-center-title';
35720 cls += ' masonry-bottom-title';
35727 if(!this.html.length && !this.bgimage.length){
35728 cls += ' masonry-center-title';
35731 if(!this.html.length && this.bgimage.length){
35732 cls += ' masonry-bottom-title';
35737 cls += ' ' + this.cls;
35741 tag: (this.href.length) ? 'a' : 'div',
35746 cls: 'masonry-brick-mask'
35750 cls: 'masonry-brick-paragraph',
35756 if(this.href.length){
35757 cfg.href = this.href;
35760 var cn = cfg.cn[1].cn;
35762 if(this.title.length){
35765 cls: 'masonry-brick-title',
35770 if(this.html.length){
35773 cls: 'masonry-brick-text',
35778 if (!this.title.length && !this.html.length) {
35779 cfg.cn[1].cls += ' hide';
35782 if(this.bgimage.length){
35785 cls: 'masonry-brick-image-view',
35790 if(this.videourl.length){
35791 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35792 // youtube support only?
35795 cls: 'masonry-brick-image-view',
35798 allowfullscreen : true
35806 getSplitAutoCreate : function()
35808 var cls = 'masonry-brick masonry-brick-split';
35810 if(this.href.length){
35811 cls += ' masonry-brick-link';
35814 if(this.bgimage.length){
35815 cls += ' masonry-brick-image';
35819 cls += ' masonry-' + this.size + '-brick';
35822 switch (this.placetitle) {
35824 cls += ' masonry-center-title';
35827 cls += ' masonry-bottom-title';
35830 if(!this.bgimage.length){
35831 cls += ' masonry-center-title';
35834 if(this.bgimage.length){
35835 cls += ' masonry-bottom-title';
35841 cls += ' ' + this.cls;
35845 tag: (this.href.length) ? 'a' : 'div',
35850 cls: 'masonry-brick-split-head',
35854 cls: 'masonry-brick-paragraph',
35861 cls: 'masonry-brick-split-body',
35867 if(this.href.length){
35868 cfg.href = this.href;
35871 if(this.title.length){
35872 cfg.cn[0].cn[0].cn.push({
35874 cls: 'masonry-brick-title',
35879 if(this.html.length){
35880 cfg.cn[1].cn.push({
35882 cls: 'masonry-brick-text',
35887 if(this.bgimage.length){
35888 cfg.cn[0].cn.push({
35890 cls: 'masonry-brick-image-view',
35895 if(this.videourl.length){
35896 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35897 // youtube support only?
35898 cfg.cn[0].cn.cn.push({
35900 cls: 'masonry-brick-image-view',
35903 allowfullscreen : true
35910 initEvents: function()
35912 switch (this.size) {
35945 this.el.on('touchstart', this.onTouchStart, this);
35946 this.el.on('touchmove', this.onTouchMove, this);
35947 this.el.on('touchend', this.onTouchEnd, this);
35948 this.el.on('contextmenu', this.onContextMenu, this);
35950 this.el.on('mouseenter' ,this.enter, this);
35951 this.el.on('mouseleave', this.leave, this);
35952 this.el.on('click', this.onClick, this);
35955 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35956 this.parent().bricks.push(this);
35961 onClick: function(e, el)
35963 var time = this.endTimer - this.startTimer;
35964 // Roo.log(e.preventDefault());
35967 e.preventDefault();
35972 if(!this.preventDefault){
35976 e.preventDefault();
35978 if (this.activeClass != '') {
35979 this.selectBrick();
35982 this.fireEvent('click', this, e);
35985 enter: function(e, el)
35987 e.preventDefault();
35989 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35993 if(this.bgimage.length && this.html.length){
35994 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35998 leave: function(e, el)
36000 e.preventDefault();
36002 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36006 if(this.bgimage.length && this.html.length){
36007 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36011 onTouchStart: function(e, el)
36013 // e.preventDefault();
36015 this.touchmoved = false;
36017 if(!this.isFitContainer){
36021 if(!this.bgimage.length || !this.html.length){
36025 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36027 this.timer = new Date().getTime();
36031 onTouchMove: function(e, el)
36033 this.touchmoved = true;
36036 onContextMenu : function(e,el)
36038 e.preventDefault();
36039 e.stopPropagation();
36043 onTouchEnd: function(e, el)
36045 // e.preventDefault();
36047 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36054 if(!this.bgimage.length || !this.html.length){
36056 if(this.href.length){
36057 window.location.href = this.href;
36063 if(!this.isFitContainer){
36067 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36069 window.location.href = this.href;
36072 //selection on single brick only
36073 selectBrick : function() {
36075 if (!this.parentId) {
36079 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36080 var index = m.selectedBrick.indexOf(this.id);
36083 m.selectedBrick.splice(index,1);
36084 this.el.removeClass(this.activeClass);
36088 for(var i = 0; i < m.selectedBrick.length; i++) {
36089 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36090 b.el.removeClass(b.activeClass);
36093 m.selectedBrick = [];
36095 m.selectedBrick.push(this.id);
36096 this.el.addClass(this.activeClass);
36100 isSelected : function(){
36101 return this.el.hasClass(this.activeClass);
36106 Roo.apply(Roo.bootstrap.MasonryBrick, {
36109 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36111 * register a Masonry Brick
36112 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36115 register : function(brick)
36117 //this.groups[brick.id] = brick;
36118 this.groups.add(brick.id, brick);
36121 * fetch a masonry brick based on the masonry brick ID
36122 * @param {string} the masonry brick to add
36123 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36126 get: function(brick_id)
36128 // if (typeof(this.groups[brick_id]) == 'undefined') {
36131 // return this.groups[brick_id] ;
36133 if(this.groups.key(brick_id)) {
36134 return this.groups.key(brick_id);
36152 * @class Roo.bootstrap.Brick
36153 * @extends Roo.bootstrap.Component
36154 * Bootstrap Brick class
36157 * Create a new Brick
36158 * @param {Object} config The config object
36161 Roo.bootstrap.Brick = function(config){
36162 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36168 * When a Brick is click
36169 * @param {Roo.bootstrap.Brick} this
36170 * @param {Roo.EventObject} e
36176 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36179 * @cfg {String} title
36183 * @cfg {String} html
36187 * @cfg {String} bgimage
36191 * @cfg {String} cls
36195 * @cfg {String} href
36199 * @cfg {String} video
36203 * @cfg {Boolean} square
36207 getAutoCreate : function()
36209 var cls = 'roo-brick';
36211 if(this.href.length){
36212 cls += ' roo-brick-link';
36215 if(this.bgimage.length){
36216 cls += ' roo-brick-image';
36219 if(!this.html.length && !this.bgimage.length){
36220 cls += ' roo-brick-center-title';
36223 if(!this.html.length && this.bgimage.length){
36224 cls += ' roo-brick-bottom-title';
36228 cls += ' ' + this.cls;
36232 tag: (this.href.length) ? 'a' : 'div',
36237 cls: 'roo-brick-paragraph',
36243 if(this.href.length){
36244 cfg.href = this.href;
36247 var cn = cfg.cn[0].cn;
36249 if(this.title.length){
36252 cls: 'roo-brick-title',
36257 if(this.html.length){
36260 cls: 'roo-brick-text',
36267 if(this.bgimage.length){
36270 cls: 'roo-brick-image-view',
36278 initEvents: function()
36280 if(this.title.length || this.html.length){
36281 this.el.on('mouseenter' ,this.enter, this);
36282 this.el.on('mouseleave', this.leave, this);
36285 Roo.EventManager.onWindowResize(this.resize, this);
36287 if(this.bgimage.length){
36288 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36289 this.imageEl.on('load', this.onImageLoad, this);
36296 onImageLoad : function()
36301 resize : function()
36303 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36305 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36307 if(this.bgimage.length){
36308 var image = this.el.select('.roo-brick-image-view', true).first();
36310 image.setWidth(paragraph.getWidth());
36313 image.setHeight(paragraph.getWidth());
36316 this.el.setHeight(image.getHeight());
36317 paragraph.setHeight(image.getHeight());
36323 enter: function(e, el)
36325 e.preventDefault();
36327 if(this.bgimage.length){
36328 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36329 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36333 leave: function(e, el)
36335 e.preventDefault();
36337 if(this.bgimage.length){
36338 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36339 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36354 * @class Roo.bootstrap.NumberField
36355 * @extends Roo.bootstrap.Input
36356 * Bootstrap NumberField class
36362 * Create a new NumberField
36363 * @param {Object} config The config object
36366 Roo.bootstrap.NumberField = function(config){
36367 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36370 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36373 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36375 allowDecimals : true,
36377 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36379 decimalSeparator : ".",
36381 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36383 decimalPrecision : 2,
36385 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36387 allowNegative : true,
36390 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36394 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36396 minValue : Number.NEGATIVE_INFINITY,
36398 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36400 maxValue : Number.MAX_VALUE,
36402 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36404 minText : "The minimum value for this field is {0}",
36406 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36408 maxText : "The maximum value for this field is {0}",
36410 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36411 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36413 nanText : "{0} is not a valid number",
36415 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36417 thousandsDelimiter : false,
36419 * @cfg {String} valueAlign alignment of value
36421 valueAlign : "left",
36423 getAutoCreate : function()
36425 var hiddenInput = {
36429 cls: 'hidden-number-input'
36433 hiddenInput.name = this.name;
36438 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36440 this.name = hiddenInput.name;
36442 if(cfg.cn.length > 0) {
36443 cfg.cn.push(hiddenInput);
36450 initEvents : function()
36452 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36454 var allowed = "0123456789";
36456 if(this.allowDecimals){
36457 allowed += this.decimalSeparator;
36460 if(this.allowNegative){
36464 if(this.thousandsDelimiter) {
36468 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36470 var keyPress = function(e){
36472 var k = e.getKey();
36474 var c = e.getCharCode();
36477 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36478 allowed.indexOf(String.fromCharCode(c)) === -1
36484 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36488 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36493 this.el.on("keypress", keyPress, this);
36496 validateValue : function(value)
36499 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36503 var num = this.parseValue(value);
36506 this.markInvalid(String.format(this.nanText, value));
36510 if(num < this.minValue){
36511 this.markInvalid(String.format(this.minText, this.minValue));
36515 if(num > this.maxValue){
36516 this.markInvalid(String.format(this.maxText, this.maxValue));
36523 getValue : function()
36525 var v = this.hiddenEl().getValue();
36527 return this.fixPrecision(this.parseValue(v));
36530 parseValue : function(value)
36532 if(this.thousandsDelimiter) {
36534 r = new RegExp(",", "g");
36535 value = value.replace(r, "");
36538 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36539 return isNaN(value) ? '' : value;
36542 fixPrecision : function(value)
36544 if(this.thousandsDelimiter) {
36546 r = new RegExp(",", "g");
36547 value = value.replace(r, "");
36550 var nan = isNaN(value);
36552 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36553 return nan ? '' : value;
36555 return parseFloat(value).toFixed(this.decimalPrecision);
36558 setValue : function(v)
36560 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36566 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36568 this.inputEl().dom.value = (v == '') ? '' :
36569 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36571 if(!this.allowZero && v === '0') {
36572 this.hiddenEl().dom.value = '';
36573 this.inputEl().dom.value = '';
36580 decimalPrecisionFcn : function(v)
36582 return Math.floor(v);
36585 beforeBlur : function()
36587 var v = this.parseValue(this.getRawValue());
36589 if(v || v === 0 || v === ''){
36594 hiddenEl : function()
36596 return this.el.select('input.hidden-number-input',true).first();
36608 * @class Roo.bootstrap.DocumentSlider
36609 * @extends Roo.bootstrap.Component
36610 * Bootstrap DocumentSlider class
36613 * Create a new DocumentViewer
36614 * @param {Object} config The config object
36617 Roo.bootstrap.DocumentSlider = function(config){
36618 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36625 * Fire after initEvent
36626 * @param {Roo.bootstrap.DocumentSlider} this
36631 * Fire after update
36632 * @param {Roo.bootstrap.DocumentSlider} this
36638 * @param {Roo.bootstrap.DocumentSlider} this
36644 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36650 getAutoCreate : function()
36654 cls : 'roo-document-slider',
36658 cls : 'roo-document-slider-header',
36662 cls : 'roo-document-slider-header-title'
36668 cls : 'roo-document-slider-body',
36672 cls : 'roo-document-slider-prev',
36676 cls : 'fa fa-chevron-left'
36682 cls : 'roo-document-slider-thumb',
36686 cls : 'roo-document-slider-image'
36692 cls : 'roo-document-slider-next',
36696 cls : 'fa fa-chevron-right'
36708 initEvents : function()
36710 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36711 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36713 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36714 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36716 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36717 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36719 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36720 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36722 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36723 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36725 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36726 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36728 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36729 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36731 this.thumbEl.on('click', this.onClick, this);
36733 this.prevIndicator.on('click', this.prev, this);
36735 this.nextIndicator.on('click', this.next, this);
36739 initial : function()
36741 if(this.files.length){
36742 this.indicator = 1;
36746 this.fireEvent('initial', this);
36749 update : function()
36751 this.imageEl.attr('src', this.files[this.indicator - 1]);
36753 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36755 this.prevIndicator.show();
36757 if(this.indicator == 1){
36758 this.prevIndicator.hide();
36761 this.nextIndicator.show();
36763 if(this.indicator == this.files.length){
36764 this.nextIndicator.hide();
36767 this.thumbEl.scrollTo('top');
36769 this.fireEvent('update', this);
36772 onClick : function(e)
36774 e.preventDefault();
36776 this.fireEvent('click', this);
36781 e.preventDefault();
36783 this.indicator = Math.max(1, this.indicator - 1);
36790 e.preventDefault();
36792 this.indicator = Math.min(this.files.length, this.indicator + 1);
36806 * @class Roo.bootstrap.RadioSet
36807 * @extends Roo.bootstrap.Input
36808 * Bootstrap RadioSet class
36809 * @cfg {String} indicatorpos (left|right) default left
36810 * @cfg {Boolean} inline (true|false) inline the element (default true)
36811 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36813 * Create a new RadioSet
36814 * @param {Object} config The config object
36817 Roo.bootstrap.RadioSet = function(config){
36819 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36823 Roo.bootstrap.RadioSet.register(this);
36828 * Fires when the element is checked or unchecked.
36829 * @param {Roo.bootstrap.RadioSet} this This radio
36830 * @param {Roo.bootstrap.Radio} item The checked item
36835 * Fires when the element is click.
36836 * @param {Roo.bootstrap.RadioSet} this This radio set
36837 * @param {Roo.bootstrap.Radio} item The checked item
36838 * @param {Roo.EventObject} e The event object
36845 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36853 indicatorpos : 'left',
36855 getAutoCreate : function()
36859 cls : 'roo-radio-set-label',
36863 html : this.fieldLabel
36867 if (Roo.bootstrap.version == 3) {
36870 if(this.indicatorpos == 'left'){
36873 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36874 tooltip : 'This field is required'
36879 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36880 tooltip : 'This field is required'
36886 cls : 'roo-radio-set-items'
36889 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36891 if (align === 'left' && this.fieldLabel.length) {
36894 cls : "roo-radio-set-right",
36900 if(this.labelWidth > 12){
36901 label.style = "width: " + this.labelWidth + 'px';
36904 if(this.labelWidth < 13 && this.labelmd == 0){
36905 this.labelmd = this.labelWidth;
36908 if(this.labellg > 0){
36909 label.cls += ' col-lg-' + this.labellg;
36910 items.cls += ' col-lg-' + (12 - this.labellg);
36913 if(this.labelmd > 0){
36914 label.cls += ' col-md-' + this.labelmd;
36915 items.cls += ' col-md-' + (12 - this.labelmd);
36918 if(this.labelsm > 0){
36919 label.cls += ' col-sm-' + this.labelsm;
36920 items.cls += ' col-sm-' + (12 - this.labelsm);
36923 if(this.labelxs > 0){
36924 label.cls += ' col-xs-' + this.labelxs;
36925 items.cls += ' col-xs-' + (12 - this.labelxs);
36931 cls : 'roo-radio-set',
36935 cls : 'roo-radio-set-input',
36938 value : this.value ? this.value : ''
36945 if(this.weight.length){
36946 cfg.cls += ' roo-radio-' + this.weight;
36950 cfg.cls += ' roo-radio-set-inline';
36954 ['xs','sm','md','lg'].map(function(size){
36955 if (settings[size]) {
36956 cfg.cls += ' col-' + size + '-' + settings[size];
36964 initEvents : function()
36966 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36967 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36969 if(!this.fieldLabel.length){
36970 this.labelEl.hide();
36973 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36974 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36976 this.indicator = this.indicatorEl();
36978 if(this.indicator){
36979 this.indicator.addClass('invisible');
36982 this.originalValue = this.getValue();
36986 inputEl: function ()
36988 return this.el.select('.roo-radio-set-input', true).first();
36991 getChildContainer : function()
36993 return this.itemsEl;
36996 register : function(item)
36998 this.radioes.push(item);
37002 validate : function()
37004 if(this.getVisibilityEl().hasClass('hidden')){
37010 Roo.each(this.radioes, function(i){
37019 if(this.allowBlank) {
37023 if(this.disabled || valid){
37028 this.markInvalid();
37033 markValid : function()
37035 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37036 this.indicatorEl().removeClass('visible');
37037 this.indicatorEl().addClass('invisible');
37041 if (Roo.bootstrap.version == 3) {
37042 this.el.removeClass([this.invalidClass, this.validClass]);
37043 this.el.addClass(this.validClass);
37045 this.el.removeClass(['is-invalid','is-valid']);
37046 this.el.addClass(['is-valid']);
37048 this.fireEvent('valid', this);
37051 markInvalid : function(msg)
37053 if(this.allowBlank || this.disabled){
37057 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37058 this.indicatorEl().removeClass('invisible');
37059 this.indicatorEl().addClass('visible');
37061 if (Roo.bootstrap.version == 3) {
37062 this.el.removeClass([this.invalidClass, this.validClass]);
37063 this.el.addClass(this.invalidClass);
37065 this.el.removeClass(['is-invalid','is-valid']);
37066 this.el.addClass(['is-invalid']);
37069 this.fireEvent('invalid', this, msg);
37073 setValue : function(v, suppressEvent)
37075 if(this.value === v){
37082 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37085 Roo.each(this.radioes, function(i){
37087 i.el.removeClass('checked');
37090 Roo.each(this.radioes, function(i){
37092 if(i.value === v || i.value.toString() === v.toString()){
37094 i.el.addClass('checked');
37096 if(suppressEvent !== true){
37097 this.fireEvent('check', this, i);
37108 clearInvalid : function(){
37110 if(!this.el || this.preventMark){
37114 this.el.removeClass([this.invalidClass]);
37116 this.fireEvent('valid', this);
37121 Roo.apply(Roo.bootstrap.RadioSet, {
37125 register : function(set)
37127 this.groups[set.name] = set;
37130 get: function(name)
37132 if (typeof(this.groups[name]) == 'undefined') {
37136 return this.groups[name] ;
37142 * Ext JS Library 1.1.1
37143 * Copyright(c) 2006-2007, Ext JS, LLC.
37145 * Originally Released Under LGPL - original licence link has changed is not relivant.
37148 * <script type="text/javascript">
37153 * @class Roo.bootstrap.SplitBar
37154 * @extends Roo.util.Observable
37155 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37159 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37160 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37161 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37162 split.minSize = 100;
37163 split.maxSize = 600;
37164 split.animate = true;
37165 split.on('moved', splitterMoved);
37168 * Create a new SplitBar
37169 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37170 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37171 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37172 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37173 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37174 position of the SplitBar).
37176 Roo.bootstrap.SplitBar = function(cfg){
37181 // dragElement : elm
37182 // resizingElement: el,
37184 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37185 // placement : Roo.bootstrap.SplitBar.LEFT ,
37186 // existingProxy ???
37189 this.el = Roo.get(cfg.dragElement, true);
37190 this.el.dom.unselectable = "on";
37192 this.resizingEl = Roo.get(cfg.resizingElement, true);
37196 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37197 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37200 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37203 * The minimum size of the resizing element. (Defaults to 0)
37209 * The maximum size of the resizing element. (Defaults to 2000)
37212 this.maxSize = 2000;
37215 * Whether to animate the transition to the new size
37218 this.animate = false;
37221 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37224 this.useShim = false;
37229 if(!cfg.existingProxy){
37231 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37233 this.proxy = Roo.get(cfg.existingProxy).dom;
37236 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37239 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37242 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37245 this.dragSpecs = {};
37248 * @private The adapter to use to positon and resize elements
37250 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37251 this.adapter.init(this);
37253 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37255 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37256 this.el.addClass("roo-splitbar-h");
37259 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37260 this.el.addClass("roo-splitbar-v");
37266 * Fires when the splitter is moved (alias for {@link #event-moved})
37267 * @param {Roo.bootstrap.SplitBar} this
37268 * @param {Number} newSize the new width or height
37273 * Fires when the splitter is moved
37274 * @param {Roo.bootstrap.SplitBar} this
37275 * @param {Number} newSize the new width or height
37279 * @event beforeresize
37280 * Fires before the splitter is dragged
37281 * @param {Roo.bootstrap.SplitBar} this
37283 "beforeresize" : true,
37285 "beforeapply" : true
37288 Roo.util.Observable.call(this);
37291 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37292 onStartProxyDrag : function(x, y){
37293 this.fireEvent("beforeresize", this);
37295 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37297 o.enableDisplayMode("block");
37298 // all splitbars share the same overlay
37299 Roo.bootstrap.SplitBar.prototype.overlay = o;
37301 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37302 this.overlay.show();
37303 Roo.get(this.proxy).setDisplayed("block");
37304 var size = this.adapter.getElementSize(this);
37305 this.activeMinSize = this.getMinimumSize();;
37306 this.activeMaxSize = this.getMaximumSize();;
37307 var c1 = size - this.activeMinSize;
37308 var c2 = Math.max(this.activeMaxSize - size, 0);
37309 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37310 this.dd.resetConstraints();
37311 this.dd.setXConstraint(
37312 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37313 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37315 this.dd.setYConstraint(0, 0);
37317 this.dd.resetConstraints();
37318 this.dd.setXConstraint(0, 0);
37319 this.dd.setYConstraint(
37320 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37321 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37324 this.dragSpecs.startSize = size;
37325 this.dragSpecs.startPoint = [x, y];
37326 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37330 * @private Called after the drag operation by the DDProxy
37332 onEndProxyDrag : function(e){
37333 Roo.get(this.proxy).setDisplayed(false);
37334 var endPoint = Roo.lib.Event.getXY(e);
37336 this.overlay.hide();
37339 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37340 newSize = this.dragSpecs.startSize +
37341 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37342 endPoint[0] - this.dragSpecs.startPoint[0] :
37343 this.dragSpecs.startPoint[0] - endPoint[0]
37346 newSize = this.dragSpecs.startSize +
37347 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37348 endPoint[1] - this.dragSpecs.startPoint[1] :
37349 this.dragSpecs.startPoint[1] - endPoint[1]
37352 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37353 if(newSize != this.dragSpecs.startSize){
37354 if(this.fireEvent('beforeapply', this, newSize) !== false){
37355 this.adapter.setElementSize(this, newSize);
37356 this.fireEvent("moved", this, newSize);
37357 this.fireEvent("resize", this, newSize);
37363 * Get the adapter this SplitBar uses
37364 * @return The adapter object
37366 getAdapter : function(){
37367 return this.adapter;
37371 * Set the adapter this SplitBar uses
37372 * @param {Object} adapter A SplitBar adapter object
37374 setAdapter : function(adapter){
37375 this.adapter = adapter;
37376 this.adapter.init(this);
37380 * Gets the minimum size for the resizing element
37381 * @return {Number} The minimum size
37383 getMinimumSize : function(){
37384 return this.minSize;
37388 * Sets the minimum size for the resizing element
37389 * @param {Number} minSize The minimum size
37391 setMinimumSize : function(minSize){
37392 this.minSize = minSize;
37396 * Gets the maximum size for the resizing element
37397 * @return {Number} The maximum size
37399 getMaximumSize : function(){
37400 return this.maxSize;
37404 * Sets the maximum size for the resizing element
37405 * @param {Number} maxSize The maximum size
37407 setMaximumSize : function(maxSize){
37408 this.maxSize = maxSize;
37412 * Sets the initialize size for the resizing element
37413 * @param {Number} size The initial size
37415 setCurrentSize : function(size){
37416 var oldAnimate = this.animate;
37417 this.animate = false;
37418 this.adapter.setElementSize(this, size);
37419 this.animate = oldAnimate;
37423 * Destroy this splitbar.
37424 * @param {Boolean} removeEl True to remove the element
37426 destroy : function(removeEl){
37428 this.shim.remove();
37431 this.proxy.parentNode.removeChild(this.proxy);
37439 * @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.
37441 Roo.bootstrap.SplitBar.createProxy = function(dir){
37442 var proxy = new Roo.Element(document.createElement("div"));
37443 proxy.unselectable();
37444 var cls = 'roo-splitbar-proxy';
37445 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37446 document.body.appendChild(proxy.dom);
37451 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37452 * Default Adapter. It assumes the splitter and resizing element are not positioned
37453 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37455 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37458 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37459 // do nothing for now
37460 init : function(s){
37464 * Called before drag operations to get the current size of the resizing element.
37465 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37467 getElementSize : function(s){
37468 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37469 return s.resizingEl.getWidth();
37471 return s.resizingEl.getHeight();
37476 * Called after drag operations to set the size of the resizing element.
37477 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37478 * @param {Number} newSize The new size to set
37479 * @param {Function} onComplete A function to be invoked when resizing is complete
37481 setElementSize : function(s, newSize, onComplete){
37482 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37484 s.resizingEl.setWidth(newSize);
37486 onComplete(s, newSize);
37489 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37494 s.resizingEl.setHeight(newSize);
37496 onComplete(s, newSize);
37499 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37506 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37507 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37508 * Adapter that moves the splitter element to align with the resized sizing element.
37509 * Used with an absolute positioned SplitBar.
37510 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37511 * document.body, make sure you assign an id to the body element.
37513 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37514 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37515 this.container = Roo.get(container);
37518 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37519 init : function(s){
37520 this.basic.init(s);
37523 getElementSize : function(s){
37524 return this.basic.getElementSize(s);
37527 setElementSize : function(s, newSize, onComplete){
37528 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37531 moveSplitter : function(s){
37532 var yes = Roo.bootstrap.SplitBar;
37533 switch(s.placement){
37535 s.el.setX(s.resizingEl.getRight());
37538 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37541 s.el.setY(s.resizingEl.getBottom());
37544 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37551 * Orientation constant - Create a vertical SplitBar
37555 Roo.bootstrap.SplitBar.VERTICAL = 1;
37558 * Orientation constant - Create a horizontal SplitBar
37562 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37565 * Placement constant - The resizing element is to the left of the splitter element
37569 Roo.bootstrap.SplitBar.LEFT = 1;
37572 * Placement constant - The resizing element is to the right of the splitter element
37576 Roo.bootstrap.SplitBar.RIGHT = 2;
37579 * Placement constant - The resizing element is positioned above the splitter element
37583 Roo.bootstrap.SplitBar.TOP = 3;
37586 * Placement constant - The resizing element is positioned under splitter element
37590 Roo.bootstrap.SplitBar.BOTTOM = 4;
37591 Roo.namespace("Roo.bootstrap.layout");/*
37593 * Ext JS Library 1.1.1
37594 * Copyright(c) 2006-2007, Ext JS, LLC.
37596 * Originally Released Under LGPL - original licence link has changed is not relivant.
37599 * <script type="text/javascript">
37603 * @class Roo.bootstrap.layout.Manager
37604 * @extends Roo.bootstrap.Component
37605 * Base class for layout managers.
37607 Roo.bootstrap.layout.Manager = function(config)
37609 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37615 /** false to disable window resize monitoring @type Boolean */
37616 this.monitorWindowResize = true;
37621 * Fires when a layout is performed.
37622 * @param {Roo.LayoutManager} this
37626 * @event regionresized
37627 * Fires when the user resizes a region.
37628 * @param {Roo.LayoutRegion} region The resized region
37629 * @param {Number} newSize The new size (width for east/west, height for north/south)
37631 "regionresized" : true,
37633 * @event regioncollapsed
37634 * Fires when a region is collapsed.
37635 * @param {Roo.LayoutRegion} region The collapsed region
37637 "regioncollapsed" : true,
37639 * @event regionexpanded
37640 * Fires when a region is expanded.
37641 * @param {Roo.LayoutRegion} region The expanded region
37643 "regionexpanded" : true
37645 this.updating = false;
37648 this.el = Roo.get(config.el);
37654 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37659 monitorWindowResize : true,
37665 onRender : function(ct, position)
37668 this.el = Roo.get(ct);
37671 //this.fireEvent('render',this);
37675 initEvents: function()
37679 // ie scrollbar fix
37680 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37681 document.body.scroll = "no";
37682 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37683 this.el.position('relative');
37685 this.id = this.el.id;
37686 this.el.addClass("roo-layout-container");
37687 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37688 if(this.el.dom != document.body ) {
37689 this.el.on('resize', this.layout,this);
37690 this.el.on('show', this.layout,this);
37696 * Returns true if this layout is currently being updated
37697 * @return {Boolean}
37699 isUpdating : function(){
37700 return this.updating;
37704 * Suspend the LayoutManager from doing auto-layouts while
37705 * making multiple add or remove calls
37707 beginUpdate : function(){
37708 this.updating = true;
37712 * Restore auto-layouts and optionally disable the manager from performing a layout
37713 * @param {Boolean} noLayout true to disable a layout update
37715 endUpdate : function(noLayout){
37716 this.updating = false;
37722 layout: function(){
37726 onRegionResized : function(region, newSize){
37727 this.fireEvent("regionresized", region, newSize);
37731 onRegionCollapsed : function(region){
37732 this.fireEvent("regioncollapsed", region);
37735 onRegionExpanded : function(region){
37736 this.fireEvent("regionexpanded", region);
37740 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37741 * performs box-model adjustments.
37742 * @return {Object} The size as an object {width: (the width), height: (the height)}
37744 getViewSize : function()
37747 if(this.el.dom != document.body){
37748 size = this.el.getSize();
37750 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37752 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37753 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37758 * Returns the Element this layout is bound to.
37759 * @return {Roo.Element}
37761 getEl : function(){
37766 * Returns the specified region.
37767 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37768 * @return {Roo.LayoutRegion}
37770 getRegion : function(target){
37771 return this.regions[target.toLowerCase()];
37774 onWindowResize : function(){
37775 if(this.monitorWindowResize){
37782 * Ext JS Library 1.1.1
37783 * Copyright(c) 2006-2007, Ext JS, LLC.
37785 * Originally Released Under LGPL - original licence link has changed is not relivant.
37788 * <script type="text/javascript">
37791 * @class Roo.bootstrap.layout.Border
37792 * @extends Roo.bootstrap.layout.Manager
37793 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37794 * please see: examples/bootstrap/nested.html<br><br>
37796 <b>The container the layout is rendered into can be either the body element or any other element.
37797 If it is not the body element, the container needs to either be an absolute positioned element,
37798 or you will need to add "position:relative" to the css of the container. You will also need to specify
37799 the container size if it is not the body element.</b>
37802 * Create a new Border
37803 * @param {Object} config Configuration options
37805 Roo.bootstrap.layout.Border = function(config){
37806 config = config || {};
37807 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37811 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37812 if(config[region]){
37813 config[region].region = region;
37814 this.addRegion(config[region]);
37820 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37822 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37824 parent : false, // this might point to a 'nest' or a ???
37827 * Creates and adds a new region if it doesn't already exist.
37828 * @param {String} target The target region key (north, south, east, west or center).
37829 * @param {Object} config The regions config object
37830 * @return {BorderLayoutRegion} The new region
37832 addRegion : function(config)
37834 if(!this.regions[config.region]){
37835 var r = this.factory(config);
37836 this.bindRegion(r);
37838 return this.regions[config.region];
37842 bindRegion : function(r){
37843 this.regions[r.config.region] = r;
37845 r.on("visibilitychange", this.layout, this);
37846 r.on("paneladded", this.layout, this);
37847 r.on("panelremoved", this.layout, this);
37848 r.on("invalidated", this.layout, this);
37849 r.on("resized", this.onRegionResized, this);
37850 r.on("collapsed", this.onRegionCollapsed, this);
37851 r.on("expanded", this.onRegionExpanded, this);
37855 * Performs a layout update.
37857 layout : function()
37859 if(this.updating) {
37863 // render all the rebions if they have not been done alreayd?
37864 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37865 if(this.regions[region] && !this.regions[region].bodyEl){
37866 this.regions[region].onRender(this.el)
37870 var size = this.getViewSize();
37871 var w = size.width;
37872 var h = size.height;
37877 //var x = 0, y = 0;
37879 var rs = this.regions;
37880 var north = rs["north"];
37881 var south = rs["south"];
37882 var west = rs["west"];
37883 var east = rs["east"];
37884 var center = rs["center"];
37885 //if(this.hideOnLayout){ // not supported anymore
37886 //c.el.setStyle("display", "none");
37888 if(north && north.isVisible()){
37889 var b = north.getBox();
37890 var m = north.getMargins();
37891 b.width = w - (m.left+m.right);
37894 centerY = b.height + b.y + m.bottom;
37895 centerH -= centerY;
37896 north.updateBox(this.safeBox(b));
37898 if(south && south.isVisible()){
37899 var b = south.getBox();
37900 var m = south.getMargins();
37901 b.width = w - (m.left+m.right);
37903 var totalHeight = (b.height + m.top + m.bottom);
37904 b.y = h - totalHeight + m.top;
37905 centerH -= totalHeight;
37906 south.updateBox(this.safeBox(b));
37908 if(west && west.isVisible()){
37909 var b = west.getBox();
37910 var m = west.getMargins();
37911 b.height = centerH - (m.top+m.bottom);
37913 b.y = centerY + m.top;
37914 var totalWidth = (b.width + m.left + m.right);
37915 centerX += totalWidth;
37916 centerW -= totalWidth;
37917 west.updateBox(this.safeBox(b));
37919 if(east && east.isVisible()){
37920 var b = east.getBox();
37921 var m = east.getMargins();
37922 b.height = centerH - (m.top+m.bottom);
37923 var totalWidth = (b.width + m.left + m.right);
37924 b.x = w - totalWidth + m.left;
37925 b.y = centerY + m.top;
37926 centerW -= totalWidth;
37927 east.updateBox(this.safeBox(b));
37930 var m = center.getMargins();
37932 x: centerX + m.left,
37933 y: centerY + m.top,
37934 width: centerW - (m.left+m.right),
37935 height: centerH - (m.top+m.bottom)
37937 //if(this.hideOnLayout){
37938 //center.el.setStyle("display", "block");
37940 center.updateBox(this.safeBox(centerBox));
37943 this.fireEvent("layout", this);
37947 safeBox : function(box){
37948 box.width = Math.max(0, box.width);
37949 box.height = Math.max(0, box.height);
37954 * Adds a ContentPanel (or subclass) to this layout.
37955 * @param {String} target The target region key (north, south, east, west or center).
37956 * @param {Roo.ContentPanel} panel The panel to add
37957 * @return {Roo.ContentPanel} The added panel
37959 add : function(target, panel){
37961 target = target.toLowerCase();
37962 return this.regions[target].add(panel);
37966 * Remove a ContentPanel (or subclass) to this layout.
37967 * @param {String} target The target region key (north, south, east, west or center).
37968 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37969 * @return {Roo.ContentPanel} The removed panel
37971 remove : function(target, panel){
37972 target = target.toLowerCase();
37973 return this.regions[target].remove(panel);
37977 * Searches all regions for a panel with the specified id
37978 * @param {String} panelId
37979 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37981 findPanel : function(panelId){
37982 var rs = this.regions;
37983 for(var target in rs){
37984 if(typeof rs[target] != "function"){
37985 var p = rs[target].getPanel(panelId);
37995 * Searches all regions for a panel with the specified id and activates (shows) it.
37996 * @param {String/ContentPanel} panelId The panels id or the panel itself
37997 * @return {Roo.ContentPanel} The shown panel or null
37999 showPanel : function(panelId) {
38000 var rs = this.regions;
38001 for(var target in rs){
38002 var r = rs[target];
38003 if(typeof r != "function"){
38004 if(r.hasPanel(panelId)){
38005 return r.showPanel(panelId);
38013 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38014 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38017 restoreState : function(provider){
38019 provider = Roo.state.Manager;
38021 var sm = new Roo.LayoutStateManager();
38022 sm.init(this, provider);
38028 * Adds a xtype elements to the layout.
38032 xtype : 'ContentPanel',
38039 xtype : 'NestedLayoutPanel',
38045 items : [ ... list of content panels or nested layout panels.. ]
38049 * @param {Object} cfg Xtype definition of item to add.
38051 addxtype : function(cfg)
38053 // basically accepts a pannel...
38054 // can accept a layout region..!?!?
38055 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38058 // theory? children can only be panels??
38060 //if (!cfg.xtype.match(/Panel$/)) {
38065 if (typeof(cfg.region) == 'undefined') {
38066 Roo.log("Failed to add Panel, region was not set");
38070 var region = cfg.region;
38076 xitems = cfg.items;
38081 if ( region == 'center') {
38082 Roo.log("Center: " + cfg.title);
38088 case 'Content': // ContentPanel (el, cfg)
38089 case 'Scroll': // ContentPanel (el, cfg)
38091 cfg.autoCreate = cfg.autoCreate || true;
38092 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38094 // var el = this.el.createChild();
38095 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38098 this.add(region, ret);
38102 case 'TreePanel': // our new panel!
38103 cfg.el = this.el.createChild();
38104 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38105 this.add(region, ret);
38110 // create a new Layout (which is a Border Layout...
38112 var clayout = cfg.layout;
38113 clayout.el = this.el.createChild();
38114 clayout.items = clayout.items || [];
38118 // replace this exitems with the clayout ones..
38119 xitems = clayout.items;
38121 // force background off if it's in center...
38122 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38123 cfg.background = false;
38125 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38128 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38129 //console.log('adding nested layout panel ' + cfg.toSource());
38130 this.add(region, ret);
38131 nb = {}; /// find first...
38136 // needs grid and region
38138 //var el = this.getRegion(region).el.createChild();
38140 *var el = this.el.createChild();
38141 // create the grid first...
38142 cfg.grid.container = el;
38143 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38146 if (region == 'center' && this.active ) {
38147 cfg.background = false;
38150 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38152 this.add(region, ret);
38154 if (cfg.background) {
38155 // render grid on panel activation (if panel background)
38156 ret.on('activate', function(gp) {
38157 if (!gp.grid.rendered) {
38158 // gp.grid.render(el);
38162 // cfg.grid.render(el);
38168 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38169 // it was the old xcomponent building that caused this before.
38170 // espeically if border is the top element in the tree.
38180 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38182 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38183 this.add(region, ret);
38187 throw "Can not add '" + cfg.xtype + "' to Border";
38193 this.beginUpdate();
38197 Roo.each(xitems, function(i) {
38198 region = nb && i.region ? i.region : false;
38200 var add = ret.addxtype(i);
38203 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38204 if (!i.background) {
38205 abn[region] = nb[region] ;
38212 // make the last non-background panel active..
38213 //if (nb) { Roo.log(abn); }
38216 for(var r in abn) {
38217 region = this.getRegion(r);
38219 // tried using nb[r], but it does not work..
38221 region.showPanel(abn[r]);
38232 factory : function(cfg)
38235 var validRegions = Roo.bootstrap.layout.Border.regions;
38237 var target = cfg.region;
38240 var r = Roo.bootstrap.layout;
38244 return new r.North(cfg);
38246 return new r.South(cfg);
38248 return new r.East(cfg);
38250 return new r.West(cfg);
38252 return new r.Center(cfg);
38254 throw 'Layout region "'+target+'" not supported.';
38261 * Ext JS Library 1.1.1
38262 * Copyright(c) 2006-2007, Ext JS, LLC.
38264 * Originally Released Under LGPL - original licence link has changed is not relivant.
38267 * <script type="text/javascript">
38271 * @class Roo.bootstrap.layout.Basic
38272 * @extends Roo.util.Observable
38273 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38274 * and does not have a titlebar, tabs or any other features. All it does is size and position
38275 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38276 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38277 * @cfg {string} region the region that it inhabits..
38278 * @cfg {bool} skipConfig skip config?
38282 Roo.bootstrap.layout.Basic = function(config){
38284 this.mgr = config.mgr;
38286 this.position = config.region;
38288 var skipConfig = config.skipConfig;
38292 * @scope Roo.BasicLayoutRegion
38296 * @event beforeremove
38297 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38298 * @param {Roo.LayoutRegion} this
38299 * @param {Roo.ContentPanel} panel The panel
38300 * @param {Object} e The cancel event object
38302 "beforeremove" : true,
38304 * @event invalidated
38305 * Fires when the layout for this region is changed.
38306 * @param {Roo.LayoutRegion} this
38308 "invalidated" : true,
38310 * @event visibilitychange
38311 * Fires when this region is shown or hidden
38312 * @param {Roo.LayoutRegion} this
38313 * @param {Boolean} visibility true or false
38315 "visibilitychange" : true,
38317 * @event paneladded
38318 * Fires when a panel is added.
38319 * @param {Roo.LayoutRegion} this
38320 * @param {Roo.ContentPanel} panel The panel
38322 "paneladded" : true,
38324 * @event panelremoved
38325 * Fires when a panel is removed.
38326 * @param {Roo.LayoutRegion} this
38327 * @param {Roo.ContentPanel} panel The panel
38329 "panelremoved" : true,
38331 * @event beforecollapse
38332 * Fires when this region before collapse.
38333 * @param {Roo.LayoutRegion} this
38335 "beforecollapse" : true,
38338 * Fires when this region is collapsed.
38339 * @param {Roo.LayoutRegion} this
38341 "collapsed" : true,
38344 * Fires when this region is expanded.
38345 * @param {Roo.LayoutRegion} this
38350 * Fires when this region is slid into view.
38351 * @param {Roo.LayoutRegion} this
38353 "slideshow" : true,
38356 * Fires when this region slides out of view.
38357 * @param {Roo.LayoutRegion} this
38359 "slidehide" : true,
38361 * @event panelactivated
38362 * Fires when a panel is activated.
38363 * @param {Roo.LayoutRegion} this
38364 * @param {Roo.ContentPanel} panel The activated panel
38366 "panelactivated" : true,
38369 * Fires when the user resizes this region.
38370 * @param {Roo.LayoutRegion} this
38371 * @param {Number} newSize The new size (width for east/west, height for north/south)
38375 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38376 this.panels = new Roo.util.MixedCollection();
38377 this.panels.getKey = this.getPanelId.createDelegate(this);
38379 this.activePanel = null;
38380 // ensure listeners are added...
38382 if (config.listeners || config.events) {
38383 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38384 listeners : config.listeners || {},
38385 events : config.events || {}
38389 if(skipConfig !== true){
38390 this.applyConfig(config);
38394 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38396 getPanelId : function(p){
38400 applyConfig : function(config){
38401 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38402 this.config = config;
38407 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38408 * the width, for horizontal (north, south) the height.
38409 * @param {Number} newSize The new width or height
38411 resizeTo : function(newSize){
38412 var el = this.el ? this.el :
38413 (this.activePanel ? this.activePanel.getEl() : null);
38415 switch(this.position){
38418 el.setWidth(newSize);
38419 this.fireEvent("resized", this, newSize);
38423 el.setHeight(newSize);
38424 this.fireEvent("resized", this, newSize);
38430 getBox : function(){
38431 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38434 getMargins : function(){
38435 return this.margins;
38438 updateBox : function(box){
38440 var el = this.activePanel.getEl();
38441 el.dom.style.left = box.x + "px";
38442 el.dom.style.top = box.y + "px";
38443 this.activePanel.setSize(box.width, box.height);
38447 * Returns the container element for this region.
38448 * @return {Roo.Element}
38450 getEl : function(){
38451 return this.activePanel;
38455 * Returns true if this region is currently visible.
38456 * @return {Boolean}
38458 isVisible : function(){
38459 return this.activePanel ? true : false;
38462 setActivePanel : function(panel){
38463 panel = this.getPanel(panel);
38464 if(this.activePanel && this.activePanel != panel){
38465 this.activePanel.setActiveState(false);
38466 this.activePanel.getEl().setLeftTop(-10000,-10000);
38468 this.activePanel = panel;
38469 panel.setActiveState(true);
38471 panel.setSize(this.box.width, this.box.height);
38473 this.fireEvent("panelactivated", this, panel);
38474 this.fireEvent("invalidated");
38478 * Show the specified panel.
38479 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38480 * @return {Roo.ContentPanel} The shown panel or null
38482 showPanel : function(panel){
38483 panel = this.getPanel(panel);
38485 this.setActivePanel(panel);
38491 * Get the active panel for this region.
38492 * @return {Roo.ContentPanel} The active panel or null
38494 getActivePanel : function(){
38495 return this.activePanel;
38499 * Add the passed ContentPanel(s)
38500 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38501 * @return {Roo.ContentPanel} The panel added (if only one was added)
38503 add : function(panel){
38504 if(arguments.length > 1){
38505 for(var i = 0, len = arguments.length; i < len; i++) {
38506 this.add(arguments[i]);
38510 if(this.hasPanel(panel)){
38511 this.showPanel(panel);
38514 var el = panel.getEl();
38515 if(el.dom.parentNode != this.mgr.el.dom){
38516 this.mgr.el.dom.appendChild(el.dom);
38518 if(panel.setRegion){
38519 panel.setRegion(this);
38521 this.panels.add(panel);
38522 el.setStyle("position", "absolute");
38523 if(!panel.background){
38524 this.setActivePanel(panel);
38525 if(this.config.initialSize && this.panels.getCount()==1){
38526 this.resizeTo(this.config.initialSize);
38529 this.fireEvent("paneladded", this, panel);
38534 * Returns true if the panel is in this region.
38535 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38536 * @return {Boolean}
38538 hasPanel : function(panel){
38539 if(typeof panel == "object"){ // must be panel obj
38540 panel = panel.getId();
38542 return this.getPanel(panel) ? true : false;
38546 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38547 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38548 * @param {Boolean} preservePanel Overrides the config preservePanel option
38549 * @return {Roo.ContentPanel} The panel that was removed
38551 remove : function(panel, preservePanel){
38552 panel = this.getPanel(panel);
38557 this.fireEvent("beforeremove", this, panel, e);
38558 if(e.cancel === true){
38561 var panelId = panel.getId();
38562 this.panels.removeKey(panelId);
38567 * Returns the panel specified or null if it's not in this region.
38568 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38569 * @return {Roo.ContentPanel}
38571 getPanel : function(id){
38572 if(typeof id == "object"){ // must be panel obj
38575 return this.panels.get(id);
38579 * Returns this regions position (north/south/east/west/center).
38582 getPosition: function(){
38583 return this.position;
38587 * Ext JS Library 1.1.1
38588 * Copyright(c) 2006-2007, Ext JS, LLC.
38590 * Originally Released Under LGPL - original licence link has changed is not relivant.
38593 * <script type="text/javascript">
38597 * @class Roo.bootstrap.layout.Region
38598 * @extends Roo.bootstrap.layout.Basic
38599 * This class represents a region in a layout manager.
38601 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38602 * @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})
38603 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38604 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38605 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38606 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38607 * @cfg {String} title The title for the region (overrides panel titles)
38608 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38609 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38610 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38611 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38612 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38613 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38614 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38615 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38616 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38617 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38619 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38620 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38621 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38622 * @cfg {Number} width For East/West panels
38623 * @cfg {Number} height For North/South panels
38624 * @cfg {Boolean} split To show the splitter
38625 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38627 * @cfg {string} cls Extra CSS classes to add to region
38629 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38630 * @cfg {string} region the region that it inhabits..
38633 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38634 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38636 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38637 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38638 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38640 Roo.bootstrap.layout.Region = function(config)
38642 this.applyConfig(config);
38644 var mgr = config.mgr;
38645 var pos = config.region;
38646 config.skipConfig = true;
38647 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38650 this.onRender(mgr.el);
38653 this.visible = true;
38654 this.collapsed = false;
38655 this.unrendered_panels = [];
38658 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38660 position: '', // set by wrapper (eg. north/south etc..)
38661 unrendered_panels : null, // unrendered panels.
38663 tabPosition : false,
38665 mgr: false, // points to 'Border'
38668 createBody : function(){
38669 /** This region's body element
38670 * @type Roo.Element */
38671 this.bodyEl = this.el.createChild({
38673 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38677 onRender: function(ctr, pos)
38679 var dh = Roo.DomHelper;
38680 /** This region's container element
38681 * @type Roo.Element */
38682 this.el = dh.append(ctr.dom, {
38684 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38686 /** This region's title element
38687 * @type Roo.Element */
38689 this.titleEl = dh.append(this.el.dom, {
38691 unselectable: "on",
38692 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38694 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38695 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38699 this.titleEl.enableDisplayMode();
38700 /** This region's title text element
38701 * @type HTMLElement */
38702 this.titleTextEl = this.titleEl.dom.firstChild;
38703 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38705 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38706 this.closeBtn.enableDisplayMode();
38707 this.closeBtn.on("click", this.closeClicked, this);
38708 this.closeBtn.hide();
38710 this.createBody(this.config);
38711 if(this.config.hideWhenEmpty){
38713 this.on("paneladded", this.validateVisibility, this);
38714 this.on("panelremoved", this.validateVisibility, this);
38716 if(this.autoScroll){
38717 this.bodyEl.setStyle("overflow", "auto");
38719 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38721 //if(c.titlebar !== false){
38722 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38723 this.titleEl.hide();
38725 this.titleEl.show();
38726 if(this.config.title){
38727 this.titleTextEl.innerHTML = this.config.title;
38731 if(this.config.collapsed){
38732 this.collapse(true);
38734 if(this.config.hidden){
38738 if (this.unrendered_panels && this.unrendered_panels.length) {
38739 for (var i =0;i< this.unrendered_panels.length; i++) {
38740 this.add(this.unrendered_panels[i]);
38742 this.unrendered_panels = null;
38748 applyConfig : function(c)
38751 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38752 var dh = Roo.DomHelper;
38753 if(c.titlebar !== false){
38754 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38755 this.collapseBtn.on("click", this.collapse, this);
38756 this.collapseBtn.enableDisplayMode();
38758 if(c.showPin === true || this.showPin){
38759 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38760 this.stickBtn.enableDisplayMode();
38761 this.stickBtn.on("click", this.expand, this);
38762 this.stickBtn.hide();
38767 /** This region's collapsed element
38768 * @type Roo.Element */
38771 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38772 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38775 if(c.floatable !== false){
38776 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38777 this.collapsedEl.on("click", this.collapseClick, this);
38780 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38781 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38782 id: "message", unselectable: "on", style:{"float":"left"}});
38783 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38785 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38786 this.expandBtn.on("click", this.expand, this);
38790 if(this.collapseBtn){
38791 this.collapseBtn.setVisible(c.collapsible == true);
38794 this.cmargins = c.cmargins || this.cmargins ||
38795 (this.position == "west" || this.position == "east" ?
38796 {top: 0, left: 2, right:2, bottom: 0} :
38797 {top: 2, left: 0, right:0, bottom: 2});
38799 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38802 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38804 this.autoScroll = c.autoScroll || false;
38809 this.duration = c.duration || .30;
38810 this.slideDuration = c.slideDuration || .45;
38815 * Returns true if this region is currently visible.
38816 * @return {Boolean}
38818 isVisible : function(){
38819 return this.visible;
38823 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38824 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38826 //setCollapsedTitle : function(title){
38827 // title = title || " ";
38828 // if(this.collapsedTitleTextEl){
38829 // this.collapsedTitleTextEl.innerHTML = title;
38833 getBox : function(){
38835 // if(!this.collapsed){
38836 b = this.el.getBox(false, true);
38838 // b = this.collapsedEl.getBox(false, true);
38843 getMargins : function(){
38844 return this.margins;
38845 //return this.collapsed ? this.cmargins : this.margins;
38848 highlight : function(){
38849 this.el.addClass("x-layout-panel-dragover");
38852 unhighlight : function(){
38853 this.el.removeClass("x-layout-panel-dragover");
38856 updateBox : function(box)
38858 if (!this.bodyEl) {
38859 return; // not rendered yet..
38863 if(!this.collapsed){
38864 this.el.dom.style.left = box.x + "px";
38865 this.el.dom.style.top = box.y + "px";
38866 this.updateBody(box.width, box.height);
38868 this.collapsedEl.dom.style.left = box.x + "px";
38869 this.collapsedEl.dom.style.top = box.y + "px";
38870 this.collapsedEl.setSize(box.width, box.height);
38873 this.tabs.autoSizeTabs();
38877 updateBody : function(w, h)
38880 this.el.setWidth(w);
38881 w -= this.el.getBorderWidth("rl");
38882 if(this.config.adjustments){
38883 w += this.config.adjustments[0];
38886 if(h !== null && h > 0){
38887 this.el.setHeight(h);
38888 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38889 h -= this.el.getBorderWidth("tb");
38890 if(this.config.adjustments){
38891 h += this.config.adjustments[1];
38893 this.bodyEl.setHeight(h);
38895 h = this.tabs.syncHeight(h);
38898 if(this.panelSize){
38899 w = w !== null ? w : this.panelSize.width;
38900 h = h !== null ? h : this.panelSize.height;
38902 if(this.activePanel){
38903 var el = this.activePanel.getEl();
38904 w = w !== null ? w : el.getWidth();
38905 h = h !== null ? h : el.getHeight();
38906 this.panelSize = {width: w, height: h};
38907 this.activePanel.setSize(w, h);
38909 if(Roo.isIE && this.tabs){
38910 this.tabs.el.repaint();
38915 * Returns the container element for this region.
38916 * @return {Roo.Element}
38918 getEl : function(){
38923 * Hides this region.
38926 //if(!this.collapsed){
38927 this.el.dom.style.left = "-2000px";
38930 // this.collapsedEl.dom.style.left = "-2000px";
38931 // this.collapsedEl.hide();
38933 this.visible = false;
38934 this.fireEvent("visibilitychange", this, false);
38938 * Shows this region if it was previously hidden.
38941 //if(!this.collapsed){
38944 // this.collapsedEl.show();
38946 this.visible = true;
38947 this.fireEvent("visibilitychange", this, true);
38950 closeClicked : function(){
38951 if(this.activePanel){
38952 this.remove(this.activePanel);
38956 collapseClick : function(e){
38958 e.stopPropagation();
38961 e.stopPropagation();
38967 * Collapses this region.
38968 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38971 collapse : function(skipAnim, skipCheck = false){
38972 if(this.collapsed) {
38976 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38978 this.collapsed = true;
38980 this.split.el.hide();
38982 if(this.config.animate && skipAnim !== true){
38983 this.fireEvent("invalidated", this);
38984 this.animateCollapse();
38986 this.el.setLocation(-20000,-20000);
38988 this.collapsedEl.show();
38989 this.fireEvent("collapsed", this);
38990 this.fireEvent("invalidated", this);
38996 animateCollapse : function(){
39001 * Expands this region if it was previously collapsed.
39002 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39003 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39006 expand : function(e, skipAnim){
39008 e.stopPropagation();
39010 if(!this.collapsed || this.el.hasActiveFx()) {
39014 this.afterSlideIn();
39017 this.collapsed = false;
39018 if(this.config.animate && skipAnim !== true){
39019 this.animateExpand();
39023 this.split.el.show();
39025 this.collapsedEl.setLocation(-2000,-2000);
39026 this.collapsedEl.hide();
39027 this.fireEvent("invalidated", this);
39028 this.fireEvent("expanded", this);
39032 animateExpand : function(){
39036 initTabs : function()
39038 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39040 var ts = new Roo.bootstrap.panel.Tabs({
39041 el: this.bodyEl.dom,
39043 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39044 disableTooltips: this.config.disableTabTips,
39045 toolbar : this.config.toolbar
39048 if(this.config.hideTabs){
39049 ts.stripWrap.setDisplayed(false);
39052 ts.resizeTabs = this.config.resizeTabs === true;
39053 ts.minTabWidth = this.config.minTabWidth || 40;
39054 ts.maxTabWidth = this.config.maxTabWidth || 250;
39055 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39056 ts.monitorResize = false;
39057 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39058 ts.bodyEl.addClass('roo-layout-tabs-body');
39059 this.panels.each(this.initPanelAsTab, this);
39062 initPanelAsTab : function(panel){
39063 var ti = this.tabs.addTab(
39067 this.config.closeOnTab && panel.isClosable(),
39070 if(panel.tabTip !== undefined){
39071 ti.setTooltip(panel.tabTip);
39073 ti.on("activate", function(){
39074 this.setActivePanel(panel);
39077 if(this.config.closeOnTab){
39078 ti.on("beforeclose", function(t, e){
39080 this.remove(panel);
39084 panel.tabItem = ti;
39089 updatePanelTitle : function(panel, title)
39091 if(this.activePanel == panel){
39092 this.updateTitle(title);
39095 var ti = this.tabs.getTab(panel.getEl().id);
39097 if(panel.tabTip !== undefined){
39098 ti.setTooltip(panel.tabTip);
39103 updateTitle : function(title){
39104 if(this.titleTextEl && !this.config.title){
39105 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39109 setActivePanel : function(panel)
39111 panel = this.getPanel(panel);
39112 if(this.activePanel && this.activePanel != panel){
39113 if(this.activePanel.setActiveState(false) === false){
39117 this.activePanel = panel;
39118 panel.setActiveState(true);
39119 if(this.panelSize){
39120 panel.setSize(this.panelSize.width, this.panelSize.height);
39123 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39125 this.updateTitle(panel.getTitle());
39127 this.fireEvent("invalidated", this);
39129 this.fireEvent("panelactivated", this, panel);
39133 * Shows the specified panel.
39134 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39135 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39137 showPanel : function(panel)
39139 panel = this.getPanel(panel);
39142 var tab = this.tabs.getTab(panel.getEl().id);
39143 if(tab.isHidden()){
39144 this.tabs.unhideTab(tab.id);
39148 this.setActivePanel(panel);
39155 * Get the active panel for this region.
39156 * @return {Roo.ContentPanel} The active panel or null
39158 getActivePanel : function(){
39159 return this.activePanel;
39162 validateVisibility : function(){
39163 if(this.panels.getCount() < 1){
39164 this.updateTitle(" ");
39165 this.closeBtn.hide();
39168 if(!this.isVisible()){
39175 * Adds the passed ContentPanel(s) to this region.
39176 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39177 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39179 add : function(panel)
39181 if(arguments.length > 1){
39182 for(var i = 0, len = arguments.length; i < len; i++) {
39183 this.add(arguments[i]);
39188 // if we have not been rendered yet, then we can not really do much of this..
39189 if (!this.bodyEl) {
39190 this.unrendered_panels.push(panel);
39197 if(this.hasPanel(panel)){
39198 this.showPanel(panel);
39201 panel.setRegion(this);
39202 this.panels.add(panel);
39203 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39204 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39205 // and hide them... ???
39206 this.bodyEl.dom.appendChild(panel.getEl().dom);
39207 if(panel.background !== true){
39208 this.setActivePanel(panel);
39210 this.fireEvent("paneladded", this, panel);
39217 this.initPanelAsTab(panel);
39221 if(panel.background !== true){
39222 this.tabs.activate(panel.getEl().id);
39224 this.fireEvent("paneladded", this, panel);
39229 * Hides the tab for the specified panel.
39230 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39232 hidePanel : function(panel){
39233 if(this.tabs && (panel = this.getPanel(panel))){
39234 this.tabs.hideTab(panel.getEl().id);
39239 * Unhides the tab for a previously hidden panel.
39240 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39242 unhidePanel : function(panel){
39243 if(this.tabs && (panel = this.getPanel(panel))){
39244 this.tabs.unhideTab(panel.getEl().id);
39248 clearPanels : function(){
39249 while(this.panels.getCount() > 0){
39250 this.remove(this.panels.first());
39255 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39256 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39257 * @param {Boolean} preservePanel Overrides the config preservePanel option
39258 * @return {Roo.ContentPanel} The panel that was removed
39260 remove : function(panel, preservePanel)
39262 panel = this.getPanel(panel);
39267 this.fireEvent("beforeremove", this, panel, e);
39268 if(e.cancel === true){
39271 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39272 var panelId = panel.getId();
39273 this.panels.removeKey(panelId);
39275 document.body.appendChild(panel.getEl().dom);
39278 this.tabs.removeTab(panel.getEl().id);
39279 }else if (!preservePanel){
39280 this.bodyEl.dom.removeChild(panel.getEl().dom);
39282 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39283 var p = this.panels.first();
39284 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39285 tempEl.appendChild(p.getEl().dom);
39286 this.bodyEl.update("");
39287 this.bodyEl.dom.appendChild(p.getEl().dom);
39289 this.updateTitle(p.getTitle());
39291 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39292 this.setActivePanel(p);
39294 panel.setRegion(null);
39295 if(this.activePanel == panel){
39296 this.activePanel = null;
39298 if(this.config.autoDestroy !== false && preservePanel !== true){
39299 try{panel.destroy();}catch(e){}
39301 this.fireEvent("panelremoved", this, panel);
39306 * Returns the TabPanel component used by this region
39307 * @return {Roo.TabPanel}
39309 getTabs : function(){
39313 createTool : function(parentEl, className){
39314 var btn = Roo.DomHelper.append(parentEl, {
39316 cls: "x-layout-tools-button",
39319 cls: "roo-layout-tools-button-inner " + className,
39323 btn.addClassOnOver("roo-layout-tools-button-over");
39328 * Ext JS Library 1.1.1
39329 * Copyright(c) 2006-2007, Ext JS, LLC.
39331 * Originally Released Under LGPL - original licence link has changed is not relivant.
39334 * <script type="text/javascript">
39340 * @class Roo.SplitLayoutRegion
39341 * @extends Roo.LayoutRegion
39342 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39344 Roo.bootstrap.layout.Split = function(config){
39345 this.cursor = config.cursor;
39346 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39349 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39351 splitTip : "Drag to resize.",
39352 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39353 useSplitTips : false,
39355 applyConfig : function(config){
39356 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39359 onRender : function(ctr,pos) {
39361 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39362 if(!this.config.split){
39367 var splitEl = Roo.DomHelper.append(ctr.dom, {
39369 id: this.el.id + "-split",
39370 cls: "roo-layout-split roo-layout-split-"+this.position,
39373 /** The SplitBar for this region
39374 * @type Roo.SplitBar */
39375 // does not exist yet...
39376 Roo.log([this.position, this.orientation]);
39378 this.split = new Roo.bootstrap.SplitBar({
39379 dragElement : splitEl,
39380 resizingElement: this.el,
39381 orientation : this.orientation
39384 this.split.on("moved", this.onSplitMove, this);
39385 this.split.useShim = this.config.useShim === true;
39386 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39387 if(this.useSplitTips){
39388 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39390 //if(config.collapsible){
39391 // this.split.el.on("dblclick", this.collapse, this);
39394 if(typeof this.config.minSize != "undefined"){
39395 this.split.minSize = this.config.minSize;
39397 if(typeof this.config.maxSize != "undefined"){
39398 this.split.maxSize = this.config.maxSize;
39400 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39401 this.hideSplitter();
39406 getHMaxSize : function(){
39407 var cmax = this.config.maxSize || 10000;
39408 var center = this.mgr.getRegion("center");
39409 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39412 getVMaxSize : function(){
39413 var cmax = this.config.maxSize || 10000;
39414 var center = this.mgr.getRegion("center");
39415 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39418 onSplitMove : function(split, newSize){
39419 this.fireEvent("resized", this, newSize);
39423 * Returns the {@link Roo.SplitBar} for this region.
39424 * @return {Roo.SplitBar}
39426 getSplitBar : function(){
39431 this.hideSplitter();
39432 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39435 hideSplitter : function(){
39437 this.split.el.setLocation(-2000,-2000);
39438 this.split.el.hide();
39444 this.split.el.show();
39446 Roo.bootstrap.layout.Split.superclass.show.call(this);
39449 beforeSlide: function(){
39450 if(Roo.isGecko){// firefox overflow auto bug workaround
39451 this.bodyEl.clip();
39453 this.tabs.bodyEl.clip();
39455 if(this.activePanel){
39456 this.activePanel.getEl().clip();
39458 if(this.activePanel.beforeSlide){
39459 this.activePanel.beforeSlide();
39465 afterSlide : function(){
39466 if(Roo.isGecko){// firefox overflow auto bug workaround
39467 this.bodyEl.unclip();
39469 this.tabs.bodyEl.unclip();
39471 if(this.activePanel){
39472 this.activePanel.getEl().unclip();
39473 if(this.activePanel.afterSlide){
39474 this.activePanel.afterSlide();
39480 initAutoHide : function(){
39481 if(this.autoHide !== false){
39482 if(!this.autoHideHd){
39483 var st = new Roo.util.DelayedTask(this.slideIn, this);
39484 this.autoHideHd = {
39485 "mouseout": function(e){
39486 if(!e.within(this.el, true)){
39490 "mouseover" : function(e){
39496 this.el.on(this.autoHideHd);
39500 clearAutoHide : function(){
39501 if(this.autoHide !== false){
39502 this.el.un("mouseout", this.autoHideHd.mouseout);
39503 this.el.un("mouseover", this.autoHideHd.mouseover);
39507 clearMonitor : function(){
39508 Roo.get(document).un("click", this.slideInIf, this);
39511 // these names are backwards but not changed for compat
39512 slideOut : function(){
39513 if(this.isSlid || this.el.hasActiveFx()){
39516 this.isSlid = true;
39517 if(this.collapseBtn){
39518 this.collapseBtn.hide();
39520 this.closeBtnState = this.closeBtn.getStyle('display');
39521 this.closeBtn.hide();
39523 this.stickBtn.show();
39526 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39527 this.beforeSlide();
39528 this.el.setStyle("z-index", 10001);
39529 this.el.slideIn(this.getSlideAnchor(), {
39530 callback: function(){
39532 this.initAutoHide();
39533 Roo.get(document).on("click", this.slideInIf, this);
39534 this.fireEvent("slideshow", this);
39541 afterSlideIn : function(){
39542 this.clearAutoHide();
39543 this.isSlid = false;
39544 this.clearMonitor();
39545 this.el.setStyle("z-index", "");
39546 if(this.collapseBtn){
39547 this.collapseBtn.show();
39549 this.closeBtn.setStyle('display', this.closeBtnState);
39551 this.stickBtn.hide();
39553 this.fireEvent("slidehide", this);
39556 slideIn : function(cb){
39557 if(!this.isSlid || this.el.hasActiveFx()){
39561 this.isSlid = false;
39562 this.beforeSlide();
39563 this.el.slideOut(this.getSlideAnchor(), {
39564 callback: function(){
39565 this.el.setLeftTop(-10000, -10000);
39567 this.afterSlideIn();
39575 slideInIf : function(e){
39576 if(!e.within(this.el)){
39581 animateCollapse : function(){
39582 this.beforeSlide();
39583 this.el.setStyle("z-index", 20000);
39584 var anchor = this.getSlideAnchor();
39585 this.el.slideOut(anchor, {
39586 callback : function(){
39587 this.el.setStyle("z-index", "");
39588 this.collapsedEl.slideIn(anchor, {duration:.3});
39590 this.el.setLocation(-10000,-10000);
39592 this.fireEvent("collapsed", this);
39599 animateExpand : function(){
39600 this.beforeSlide();
39601 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39602 this.el.setStyle("z-index", 20000);
39603 this.collapsedEl.hide({
39606 this.el.slideIn(this.getSlideAnchor(), {
39607 callback : function(){
39608 this.el.setStyle("z-index", "");
39611 this.split.el.show();
39613 this.fireEvent("invalidated", this);
39614 this.fireEvent("expanded", this);
39642 getAnchor : function(){
39643 return this.anchors[this.position];
39646 getCollapseAnchor : function(){
39647 return this.canchors[this.position];
39650 getSlideAnchor : function(){
39651 return this.sanchors[this.position];
39654 getAlignAdj : function(){
39655 var cm = this.cmargins;
39656 switch(this.position){
39672 getExpandAdj : function(){
39673 var c = this.collapsedEl, cm = this.cmargins;
39674 switch(this.position){
39676 return [-(cm.right+c.getWidth()+cm.left), 0];
39679 return [cm.right+c.getWidth()+cm.left, 0];
39682 return [0, -(cm.top+cm.bottom+c.getHeight())];
39685 return [0, cm.top+cm.bottom+c.getHeight()];
39691 * Ext JS Library 1.1.1
39692 * Copyright(c) 2006-2007, Ext JS, LLC.
39694 * Originally Released Under LGPL - original licence link has changed is not relivant.
39697 * <script type="text/javascript">
39700 * These classes are private internal classes
39702 Roo.bootstrap.layout.Center = function(config){
39703 config.region = "center";
39704 Roo.bootstrap.layout.Region.call(this, config);
39705 this.visible = true;
39706 this.minWidth = config.minWidth || 20;
39707 this.minHeight = config.minHeight || 20;
39710 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39712 // center panel can't be hidden
39716 // center panel can't be hidden
39719 getMinWidth: function(){
39720 return this.minWidth;
39723 getMinHeight: function(){
39724 return this.minHeight;
39738 Roo.bootstrap.layout.North = function(config)
39740 config.region = 'north';
39741 config.cursor = 'n-resize';
39743 Roo.bootstrap.layout.Split.call(this, config);
39747 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39748 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39749 this.split.el.addClass("roo-layout-split-v");
39751 //var size = config.initialSize || config.height;
39752 //if(this.el && typeof size != "undefined"){
39753 // this.el.setHeight(size);
39756 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39758 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39761 onRender : function(ctr, pos)
39763 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39764 var size = this.config.initialSize || this.config.height;
39765 if(this.el && typeof size != "undefined"){
39766 this.el.setHeight(size);
39771 getBox : function(){
39772 if(this.collapsed){
39773 return this.collapsedEl.getBox();
39775 var box = this.el.getBox();
39777 box.height += this.split.el.getHeight();
39782 updateBox : function(box){
39783 if(this.split && !this.collapsed){
39784 box.height -= this.split.el.getHeight();
39785 this.split.el.setLeft(box.x);
39786 this.split.el.setTop(box.y+box.height);
39787 this.split.el.setWidth(box.width);
39789 if(this.collapsed){
39790 this.updateBody(box.width, null);
39792 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39800 Roo.bootstrap.layout.South = function(config){
39801 config.region = 'south';
39802 config.cursor = 's-resize';
39803 Roo.bootstrap.layout.Split.call(this, config);
39805 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39806 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39807 this.split.el.addClass("roo-layout-split-v");
39812 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39813 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39815 onRender : function(ctr, pos)
39817 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39818 var size = this.config.initialSize || this.config.height;
39819 if(this.el && typeof size != "undefined"){
39820 this.el.setHeight(size);
39825 getBox : function(){
39826 if(this.collapsed){
39827 return this.collapsedEl.getBox();
39829 var box = this.el.getBox();
39831 var sh = this.split.el.getHeight();
39838 updateBox : function(box){
39839 if(this.split && !this.collapsed){
39840 var sh = this.split.el.getHeight();
39843 this.split.el.setLeft(box.x);
39844 this.split.el.setTop(box.y-sh);
39845 this.split.el.setWidth(box.width);
39847 if(this.collapsed){
39848 this.updateBody(box.width, null);
39850 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39854 Roo.bootstrap.layout.East = function(config){
39855 config.region = "east";
39856 config.cursor = "e-resize";
39857 Roo.bootstrap.layout.Split.call(this, config);
39859 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39860 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39861 this.split.el.addClass("roo-layout-split-h");
39865 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39866 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39868 onRender : function(ctr, pos)
39870 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39871 var size = this.config.initialSize || this.config.width;
39872 if(this.el && typeof size != "undefined"){
39873 this.el.setWidth(size);
39878 getBox : function(){
39879 if(this.collapsed){
39880 return this.collapsedEl.getBox();
39882 var box = this.el.getBox();
39884 var sw = this.split.el.getWidth();
39891 updateBox : function(box){
39892 if(this.split && !this.collapsed){
39893 var sw = this.split.el.getWidth();
39895 this.split.el.setLeft(box.x);
39896 this.split.el.setTop(box.y);
39897 this.split.el.setHeight(box.height);
39900 if(this.collapsed){
39901 this.updateBody(null, box.height);
39903 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39907 Roo.bootstrap.layout.West = function(config){
39908 config.region = "west";
39909 config.cursor = "w-resize";
39911 Roo.bootstrap.layout.Split.call(this, config);
39913 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39914 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39915 this.split.el.addClass("roo-layout-split-h");
39919 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39920 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39922 onRender: function(ctr, pos)
39924 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39925 var size = this.config.initialSize || this.config.width;
39926 if(typeof size != "undefined"){
39927 this.el.setWidth(size);
39931 getBox : function(){
39932 if(this.collapsed){
39933 return this.collapsedEl.getBox();
39935 var box = this.el.getBox();
39936 if (box.width == 0) {
39937 box.width = this.config.width; // kludge?
39940 box.width += this.split.el.getWidth();
39945 updateBox : function(box){
39946 if(this.split && !this.collapsed){
39947 var sw = this.split.el.getWidth();
39949 this.split.el.setLeft(box.x+box.width);
39950 this.split.el.setTop(box.y);
39951 this.split.el.setHeight(box.height);
39953 if(this.collapsed){
39954 this.updateBody(null, box.height);
39956 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39958 });Roo.namespace("Roo.bootstrap.panel");/*
39960 * Ext JS Library 1.1.1
39961 * Copyright(c) 2006-2007, Ext JS, LLC.
39963 * Originally Released Under LGPL - original licence link has changed is not relivant.
39966 * <script type="text/javascript">
39969 * @class Roo.ContentPanel
39970 * @extends Roo.util.Observable
39971 * A basic ContentPanel element.
39972 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39973 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39974 * @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
39975 * @cfg {Boolean} closable True if the panel can be closed/removed
39976 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39977 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39978 * @cfg {Toolbar} toolbar A toolbar for this panel
39979 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39980 * @cfg {String} title The title for this panel
39981 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39982 * @cfg {String} url Calls {@link #setUrl} with this value
39983 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39984 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39985 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39986 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39987 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39988 * @cfg {Boolean} badges render the badges
39989 * @cfg {String} cls extra classes to use
39990 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39993 * Create a new ContentPanel.
39994 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39995 * @param {String/Object} config A string to set only the title or a config object
39996 * @param {String} content (optional) Set the HTML content for this panel
39997 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39999 Roo.bootstrap.panel.Content = function( config){
40001 this.tpl = config.tpl || false;
40003 var el = config.el;
40004 var content = config.content;
40006 if(config.autoCreate){ // xtype is available if this is called from factory
40009 this.el = Roo.get(el);
40010 if(!this.el && config && config.autoCreate){
40011 if(typeof config.autoCreate == "object"){
40012 if(!config.autoCreate.id){
40013 config.autoCreate.id = config.id||el;
40015 this.el = Roo.DomHelper.append(document.body,
40016 config.autoCreate, true);
40020 cls: (config.cls || '') +
40021 (config.background ? ' bg-' + config.background : '') +
40022 " roo-layout-inactive-content",
40025 if (config.iframe) {
40029 style : 'border: 0px',
40030 src : 'about:blank'
40036 elcfg.html = config.html;
40040 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40041 if (config.iframe) {
40042 this.iframeEl = this.el.select('iframe',true).first();
40047 this.closable = false;
40048 this.loaded = false;
40049 this.active = false;
40052 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40054 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40056 this.wrapEl = this.el; //this.el.wrap();
40058 if (config.toolbar.items) {
40059 ti = config.toolbar.items ;
40060 delete config.toolbar.items ;
40064 this.toolbar.render(this.wrapEl, 'before');
40065 for(var i =0;i < ti.length;i++) {
40066 // Roo.log(['add child', items[i]]);
40067 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40069 this.toolbar.items = nitems;
40070 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40071 delete config.toolbar;
40075 // xtype created footer. - not sure if will work as we normally have to render first..
40076 if (this.footer && !this.footer.el && this.footer.xtype) {
40077 if (!this.wrapEl) {
40078 this.wrapEl = this.el.wrap();
40081 this.footer.container = this.wrapEl.createChild();
40083 this.footer = Roo.factory(this.footer, Roo);
40088 if(typeof config == "string"){
40089 this.title = config;
40091 Roo.apply(this, config);
40095 this.resizeEl = Roo.get(this.resizeEl, true);
40097 this.resizeEl = this.el;
40099 // handle view.xtype
40107 * Fires when this panel is activated.
40108 * @param {Roo.ContentPanel} this
40112 * @event deactivate
40113 * Fires when this panel is activated.
40114 * @param {Roo.ContentPanel} this
40116 "deactivate" : true,
40120 * Fires when this panel is resized if fitToFrame is true.
40121 * @param {Roo.ContentPanel} this
40122 * @param {Number} width The width after any component adjustments
40123 * @param {Number} height The height after any component adjustments
40129 * Fires when this tab is created
40130 * @param {Roo.ContentPanel} this
40141 if(this.autoScroll && !this.iframe){
40142 this.resizeEl.setStyle("overflow", "auto");
40144 // fix randome scrolling
40145 //this.el.on('scroll', function() {
40146 // Roo.log('fix random scolling');
40147 // this.scrollTo('top',0);
40150 content = content || this.content;
40152 this.setContent(content);
40154 if(config && config.url){
40155 this.setUrl(this.url, this.params, this.loadOnce);
40160 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40162 if (this.view && typeof(this.view.xtype) != 'undefined') {
40163 this.view.el = this.el.appendChild(document.createElement("div"));
40164 this.view = Roo.factory(this.view);
40165 this.view.render && this.view.render(false, '');
40169 this.fireEvent('render', this);
40172 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40182 setRegion : function(region){
40183 this.region = region;
40184 this.setActiveClass(region && !this.background);
40188 setActiveClass: function(state)
40191 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40192 this.el.setStyle('position','relative');
40194 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40195 this.el.setStyle('position', 'absolute');
40200 * Returns the toolbar for this Panel if one was configured.
40201 * @return {Roo.Toolbar}
40203 getToolbar : function(){
40204 return this.toolbar;
40207 setActiveState : function(active)
40209 this.active = active;
40210 this.setActiveClass(active);
40212 if(this.fireEvent("deactivate", this) === false){
40217 this.fireEvent("activate", this);
40221 * Updates this panel's element (not for iframe)
40222 * @param {String} content The new content
40223 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40225 setContent : function(content, loadScripts){
40230 this.el.update(content, loadScripts);
40233 ignoreResize : function(w, h){
40234 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40237 this.lastSize = {width: w, height: h};
40242 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40243 * @return {Roo.UpdateManager} The UpdateManager
40245 getUpdateManager : function(){
40249 return this.el.getUpdateManager();
40252 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40253 * Does not work with IFRAME contents
40254 * @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:
40257 url: "your-url.php",
40258 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40259 callback: yourFunction,
40260 scope: yourObject, //(optional scope)
40263 text: "Loading...",
40269 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40270 * 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.
40271 * @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}
40272 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40273 * @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.
40274 * @return {Roo.ContentPanel} this
40282 var um = this.el.getUpdateManager();
40283 um.update.apply(um, arguments);
40289 * 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.
40290 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40291 * @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)
40292 * @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)
40293 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40295 setUrl : function(url, params, loadOnce){
40297 this.iframeEl.dom.src = url;
40301 if(this.refreshDelegate){
40302 this.removeListener("activate", this.refreshDelegate);
40304 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40305 this.on("activate", this.refreshDelegate);
40306 return this.el.getUpdateManager();
40309 _handleRefresh : function(url, params, loadOnce){
40310 if(!loadOnce || !this.loaded){
40311 var updater = this.el.getUpdateManager();
40312 updater.update(url, params, this._setLoaded.createDelegate(this));
40316 _setLoaded : function(){
40317 this.loaded = true;
40321 * Returns this panel's id
40324 getId : function(){
40329 * Returns this panel's element - used by regiosn to add.
40330 * @return {Roo.Element}
40332 getEl : function(){
40333 return this.wrapEl || this.el;
40338 adjustForComponents : function(width, height)
40340 //Roo.log('adjustForComponents ');
40341 if(this.resizeEl != this.el){
40342 width -= this.el.getFrameWidth('lr');
40343 height -= this.el.getFrameWidth('tb');
40346 var te = this.toolbar.getEl();
40347 te.setWidth(width);
40348 height -= te.getHeight();
40351 var te = this.footer.getEl();
40352 te.setWidth(width);
40353 height -= te.getHeight();
40357 if(this.adjustments){
40358 width += this.adjustments[0];
40359 height += this.adjustments[1];
40361 return {"width": width, "height": height};
40364 setSize : function(width, height){
40365 if(this.fitToFrame && !this.ignoreResize(width, height)){
40366 if(this.fitContainer && this.resizeEl != this.el){
40367 this.el.setSize(width, height);
40369 var size = this.adjustForComponents(width, height);
40371 this.iframeEl.setSize(width,height);
40374 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40375 this.fireEvent('resize', this, size.width, size.height);
40382 * Returns this panel's title
40385 getTitle : function(){
40387 if (typeof(this.title) != 'object') {
40392 for (var k in this.title) {
40393 if (!this.title.hasOwnProperty(k)) {
40397 if (k.indexOf('-') >= 0) {
40398 var s = k.split('-');
40399 for (var i = 0; i<s.length; i++) {
40400 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40403 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40410 * Set this panel's title
40411 * @param {String} title
40413 setTitle : function(title){
40414 this.title = title;
40416 this.region.updatePanelTitle(this, title);
40421 * Returns true is this panel was configured to be closable
40422 * @return {Boolean}
40424 isClosable : function(){
40425 return this.closable;
40428 beforeSlide : function(){
40430 this.resizeEl.clip();
40433 afterSlide : function(){
40435 this.resizeEl.unclip();
40439 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40440 * Will fail silently if the {@link #setUrl} method has not been called.
40441 * This does not activate the panel, just updates its content.
40443 refresh : function(){
40444 if(this.refreshDelegate){
40445 this.loaded = false;
40446 this.refreshDelegate();
40451 * Destroys this panel
40453 destroy : function(){
40454 this.el.removeAllListeners();
40455 var tempEl = document.createElement("span");
40456 tempEl.appendChild(this.el.dom);
40457 tempEl.innerHTML = "";
40463 * form - if the content panel contains a form - this is a reference to it.
40464 * @type {Roo.form.Form}
40468 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40469 * This contains a reference to it.
40475 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40485 * @param {Object} cfg Xtype definition of item to add.
40489 getChildContainer: function () {
40490 return this.getEl();
40495 var ret = new Roo.factory(cfg);
40500 if (cfg.xtype.match(/^Form$/)) {
40503 //if (this.footer) {
40504 // el = this.footer.container.insertSibling(false, 'before');
40506 el = this.el.createChild();
40509 this.form = new Roo.form.Form(cfg);
40512 if ( this.form.allItems.length) {
40513 this.form.render(el.dom);
40517 // should only have one of theses..
40518 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40519 // views.. should not be just added - used named prop 'view''
40521 cfg.el = this.el.appendChild(document.createElement("div"));
40524 var ret = new Roo.factory(cfg);
40526 ret.render && ret.render(false, ''); // render blank..
40536 * @class Roo.bootstrap.panel.Grid
40537 * @extends Roo.bootstrap.panel.Content
40539 * Create a new GridPanel.
40540 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40541 * @param {Object} config A the config object
40547 Roo.bootstrap.panel.Grid = function(config)
40551 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40552 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40554 config.el = this.wrapper;
40555 //this.el = this.wrapper;
40557 if (config.container) {
40558 // ctor'ed from a Border/panel.grid
40561 this.wrapper.setStyle("overflow", "hidden");
40562 this.wrapper.addClass('roo-grid-container');
40567 if(config.toolbar){
40568 var tool_el = this.wrapper.createChild();
40569 this.toolbar = Roo.factory(config.toolbar);
40571 if (config.toolbar.items) {
40572 ti = config.toolbar.items ;
40573 delete config.toolbar.items ;
40577 this.toolbar.render(tool_el);
40578 for(var i =0;i < ti.length;i++) {
40579 // Roo.log(['add child', items[i]]);
40580 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40582 this.toolbar.items = nitems;
40584 delete config.toolbar;
40587 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40588 config.grid.scrollBody = true;;
40589 config.grid.monitorWindowResize = false; // turn off autosizing
40590 config.grid.autoHeight = false;
40591 config.grid.autoWidth = false;
40593 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40595 if (config.background) {
40596 // render grid on panel activation (if panel background)
40597 this.on('activate', function(gp) {
40598 if (!gp.grid.rendered) {
40599 gp.grid.render(this.wrapper);
40600 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40605 this.grid.render(this.wrapper);
40606 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40609 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40610 // ??? needed ??? config.el = this.wrapper;
40615 // xtype created footer. - not sure if will work as we normally have to render first..
40616 if (this.footer && !this.footer.el && this.footer.xtype) {
40618 var ctr = this.grid.getView().getFooterPanel(true);
40619 this.footer.dataSource = this.grid.dataSource;
40620 this.footer = Roo.factory(this.footer, Roo);
40621 this.footer.render(ctr);
40631 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40632 getId : function(){
40633 return this.grid.id;
40637 * Returns the grid for this panel
40638 * @return {Roo.bootstrap.Table}
40640 getGrid : function(){
40644 setSize : function(width, height){
40645 if(!this.ignoreResize(width, height)){
40646 var grid = this.grid;
40647 var size = this.adjustForComponents(width, height);
40648 // tfoot is not a footer?
40651 var gridel = grid.getGridEl();
40652 gridel.setSize(size.width, size.height);
40654 var tbd = grid.getGridEl().select('tbody', true).first();
40655 var thd = grid.getGridEl().select('thead',true).first();
40656 var tbf= grid.getGridEl().select('tfoot', true).first();
40659 size.height -= tbf.getHeight();
40662 size.height -= thd.getHeight();
40665 tbd.setSize(size.width, size.height );
40666 // this is for the account management tab -seems to work there.
40667 var thd = grid.getGridEl().select('thead',true).first();
40669 // tbd.setSize(size.width, size.height - thd.getHeight());
40678 beforeSlide : function(){
40679 this.grid.getView().scroller.clip();
40682 afterSlide : function(){
40683 this.grid.getView().scroller.unclip();
40686 destroy : function(){
40687 this.grid.destroy();
40689 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40694 * @class Roo.bootstrap.panel.Nest
40695 * @extends Roo.bootstrap.panel.Content
40697 * Create a new Panel, that can contain a layout.Border.
40700 * @param {Roo.BorderLayout} layout The layout for this panel
40701 * @param {String/Object} config A string to set only the title or a config object
40703 Roo.bootstrap.panel.Nest = function(config)
40705 // construct with only one argument..
40706 /* FIXME - implement nicer consturctors
40707 if (layout.layout) {
40709 layout = config.layout;
40710 delete config.layout;
40712 if (layout.xtype && !layout.getEl) {
40713 // then layout needs constructing..
40714 layout = Roo.factory(layout, Roo);
40718 config.el = config.layout.getEl();
40720 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40722 config.layout.monitorWindowResize = false; // turn off autosizing
40723 this.layout = config.layout;
40724 this.layout.getEl().addClass("roo-layout-nested-layout");
40725 this.layout.parent = this;
40732 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40734 setSize : function(width, height){
40735 if(!this.ignoreResize(width, height)){
40736 var size = this.adjustForComponents(width, height);
40737 var el = this.layout.getEl();
40738 if (size.height < 1) {
40739 el.setWidth(size.width);
40741 el.setSize(size.width, size.height);
40743 var touch = el.dom.offsetWidth;
40744 this.layout.layout();
40745 // ie requires a double layout on the first pass
40746 if(Roo.isIE && !this.initialized){
40747 this.initialized = true;
40748 this.layout.layout();
40753 // activate all subpanels if not currently active..
40755 setActiveState : function(active){
40756 this.active = active;
40757 this.setActiveClass(active);
40760 this.fireEvent("deactivate", this);
40764 this.fireEvent("activate", this);
40765 // not sure if this should happen before or after..
40766 if (!this.layout) {
40767 return; // should not happen..
40770 for (var r in this.layout.regions) {
40771 reg = this.layout.getRegion(r);
40772 if (reg.getActivePanel()) {
40773 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40774 reg.setActivePanel(reg.getActivePanel());
40777 if (!reg.panels.length) {
40780 reg.showPanel(reg.getPanel(0));
40789 * Returns the nested BorderLayout for this panel
40790 * @return {Roo.BorderLayout}
40792 getLayout : function(){
40793 return this.layout;
40797 * Adds a xtype elements to the layout of the nested panel
40801 xtype : 'ContentPanel',
40808 xtype : 'NestedLayoutPanel',
40814 items : [ ... list of content panels or nested layout panels.. ]
40818 * @param {Object} cfg Xtype definition of item to add.
40820 addxtype : function(cfg) {
40821 return this.layout.addxtype(cfg);
40826 * Ext JS Library 1.1.1
40827 * Copyright(c) 2006-2007, Ext JS, LLC.
40829 * Originally Released Under LGPL - original licence link has changed is not relivant.
40832 * <script type="text/javascript">
40835 * @class Roo.TabPanel
40836 * @extends Roo.util.Observable
40837 * A lightweight tab container.
40841 // basic tabs 1, built from existing content
40842 var tabs = new Roo.TabPanel("tabs1");
40843 tabs.addTab("script", "View Script");
40844 tabs.addTab("markup", "View Markup");
40845 tabs.activate("script");
40847 // more advanced tabs, built from javascript
40848 var jtabs = new Roo.TabPanel("jtabs");
40849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40851 // set up the UpdateManager
40852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40853 var updater = tab2.getUpdateManager();
40854 updater.setDefaultUrl("ajax1.htm");
40855 tab2.on('activate', updater.refresh, updater, true);
40857 // Use setUrl for Ajax loading
40858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40859 tab3.setUrl("ajax2.htm", null, true);
40862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40865 jtabs.activate("jtabs-1");
40868 * Create a new TabPanel.
40869 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40870 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40872 Roo.bootstrap.panel.Tabs = function(config){
40874 * The container element for this TabPanel.
40875 * @type Roo.Element
40877 this.el = Roo.get(config.el);
40880 if(typeof config == "boolean"){
40881 this.tabPosition = config ? "bottom" : "top";
40883 Roo.apply(this, config);
40887 if(this.tabPosition == "bottom"){
40888 // if tabs are at the bottom = create the body first.
40889 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40890 this.el.addClass("roo-tabs-bottom");
40892 // next create the tabs holders
40894 if (this.tabPosition == "west"){
40896 var reg = this.region; // fake it..
40898 if (!reg.mgr.parent) {
40901 reg = reg.mgr.parent.region;
40903 Roo.log("got nest?");
40905 if (reg.mgr.getRegion('west')) {
40906 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40907 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40908 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40909 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40910 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40918 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40919 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40920 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40921 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40926 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40929 // finally - if tabs are at the top, then create the body last..
40930 if(this.tabPosition != "bottom"){
40931 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40932 * @type Roo.Element
40934 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40935 this.el.addClass("roo-tabs-top");
40939 this.bodyEl.setStyle("position", "relative");
40941 this.active = null;
40942 this.activateDelegate = this.activate.createDelegate(this);
40947 * Fires when the active tab changes
40948 * @param {Roo.TabPanel} this
40949 * @param {Roo.TabPanelItem} activePanel The new active tab
40953 * @event beforetabchange
40954 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40955 * @param {Roo.TabPanel} this
40956 * @param {Object} e Set cancel to true on this object to cancel the tab change
40957 * @param {Roo.TabPanelItem} tab The tab being changed to
40959 "beforetabchange" : true
40962 Roo.EventManager.onWindowResize(this.onResize, this);
40963 this.cpad = this.el.getPadding("lr");
40964 this.hiddenCount = 0;
40967 // toolbar on the tabbar support...
40968 if (this.toolbar) {
40969 alert("no toolbar support yet");
40970 this.toolbar = false;
40972 var tcfg = this.toolbar;
40973 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40974 this.toolbar = new Roo.Toolbar(tcfg);
40975 if (Roo.isSafari) {
40976 var tbl = tcfg.container.child('table', true);
40977 tbl.setAttribute('width', '100%');
40985 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40988 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40990 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40992 tabPosition : "top",
40994 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40996 currentTabWidth : 0,
40998 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41002 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41006 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41008 preferredTabWidth : 175,
41010 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41012 resizeTabs : false,
41014 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41016 monitorResize : true,
41018 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41020 toolbar : false, // set by caller..
41022 region : false, /// set by caller
41024 disableTooltips : true, // not used yet...
41027 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41028 * @param {String} id The id of the div to use <b>or create</b>
41029 * @param {String} text The text for the tab
41030 * @param {String} content (optional) Content to put in the TabPanelItem body
41031 * @param {Boolean} closable (optional) True to create a close icon on the tab
41032 * @return {Roo.TabPanelItem} The created TabPanelItem
41034 addTab : function(id, text, content, closable, tpl)
41036 var item = new Roo.bootstrap.panel.TabItem({
41040 closable : closable,
41043 this.addTabItem(item);
41045 item.setContent(content);
41051 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41052 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41053 * @return {Roo.TabPanelItem}
41055 getTab : function(id){
41056 return this.items[id];
41060 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41061 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41063 hideTab : function(id){
41064 var t = this.items[id];
41067 this.hiddenCount++;
41068 this.autoSizeTabs();
41073 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41074 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41076 unhideTab : function(id){
41077 var t = this.items[id];
41079 t.setHidden(false);
41080 this.hiddenCount--;
41081 this.autoSizeTabs();
41086 * Adds an existing {@link Roo.TabPanelItem}.
41087 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41089 addTabItem : function(item)
41091 this.items[item.id] = item;
41092 this.items.push(item);
41093 this.autoSizeTabs();
41094 // if(this.resizeTabs){
41095 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41096 // this.autoSizeTabs();
41098 // item.autoSize();
41103 * Removes a {@link Roo.TabPanelItem}.
41104 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41106 removeTab : function(id){
41107 var items = this.items;
41108 var tab = items[id];
41109 if(!tab) { return; }
41110 var index = items.indexOf(tab);
41111 if(this.active == tab && items.length > 1){
41112 var newTab = this.getNextAvailable(index);
41117 this.stripEl.dom.removeChild(tab.pnode.dom);
41118 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41119 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41121 items.splice(index, 1);
41122 delete this.items[tab.id];
41123 tab.fireEvent("close", tab);
41124 tab.purgeListeners();
41125 this.autoSizeTabs();
41128 getNextAvailable : function(start){
41129 var items = this.items;
41131 // look for a next tab that will slide over to
41132 // replace the one being removed
41133 while(index < items.length){
41134 var item = items[++index];
41135 if(item && !item.isHidden()){
41139 // if one isn't found select the previous tab (on the left)
41142 var item = items[--index];
41143 if(item && !item.isHidden()){
41151 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41152 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41154 disableTab : function(id){
41155 var tab = this.items[id];
41156 if(tab && this.active != tab){
41162 * Enables a {@link Roo.TabPanelItem} that is disabled.
41163 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41165 enableTab : function(id){
41166 var tab = this.items[id];
41171 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41172 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41173 * @return {Roo.TabPanelItem} The TabPanelItem.
41175 activate : function(id)
41177 //Roo.log('activite:' + id);
41179 var tab = this.items[id];
41183 if(tab == this.active || tab.disabled){
41187 this.fireEvent("beforetabchange", this, e, tab);
41188 if(e.cancel !== true && !tab.disabled){
41190 this.active.hide();
41192 this.active = this.items[id];
41193 this.active.show();
41194 this.fireEvent("tabchange", this, this.active);
41200 * Gets the active {@link Roo.TabPanelItem}.
41201 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41203 getActiveTab : function(){
41204 return this.active;
41208 * Updates the tab body element to fit the height of the container element
41209 * for overflow scrolling
41210 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41212 syncHeight : function(targetHeight){
41213 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41214 var bm = this.bodyEl.getMargins();
41215 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41216 this.bodyEl.setHeight(newHeight);
41220 onResize : function(){
41221 if(this.monitorResize){
41222 this.autoSizeTabs();
41227 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41229 beginUpdate : function(){
41230 this.updating = true;
41234 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41236 endUpdate : function(){
41237 this.updating = false;
41238 this.autoSizeTabs();
41242 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41244 autoSizeTabs : function()
41246 var count = this.items.length;
41247 var vcount = count - this.hiddenCount;
41250 this.stripEl.hide();
41252 this.stripEl.show();
41255 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41260 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41261 var availWidth = Math.floor(w / vcount);
41262 var b = this.stripBody;
41263 if(b.getWidth() > w){
41264 var tabs = this.items;
41265 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41266 if(availWidth < this.minTabWidth){
41267 /*if(!this.sleft){ // incomplete scrolling code
41268 this.createScrollButtons();
41271 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41274 if(this.currentTabWidth < this.preferredTabWidth){
41275 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41281 * Returns the number of tabs in this TabPanel.
41284 getCount : function(){
41285 return this.items.length;
41289 * Resizes all the tabs to the passed width
41290 * @param {Number} The new width
41292 setTabWidth : function(width){
41293 this.currentTabWidth = width;
41294 for(var i = 0, len = this.items.length; i < len; i++) {
41295 if(!this.items[i].isHidden()) {
41296 this.items[i].setWidth(width);
41302 * Destroys this TabPanel
41303 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41305 destroy : function(removeEl){
41306 Roo.EventManager.removeResizeListener(this.onResize, this);
41307 for(var i = 0, len = this.items.length; i < len; i++){
41308 this.items[i].purgeListeners();
41310 if(removeEl === true){
41311 this.el.update("");
41316 createStrip : function(container)
41318 var strip = document.createElement("nav");
41319 strip.className = Roo.bootstrap.version == 4 ?
41320 "navbar-light bg-light" :
41321 "navbar navbar-default"; //"x-tabs-wrap";
41322 container.appendChild(strip);
41326 createStripList : function(strip)
41328 // div wrapper for retard IE
41329 // returns the "tr" element.
41330 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41331 //'<div class="x-tabs-strip-wrap">'+
41332 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41333 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41334 return strip.firstChild; //.firstChild.firstChild.firstChild;
41336 createBody : function(container)
41338 var body = document.createElement("div");
41339 Roo.id(body, "tab-body");
41340 //Roo.fly(body).addClass("x-tabs-body");
41341 Roo.fly(body).addClass("tab-content");
41342 container.appendChild(body);
41345 createItemBody :function(bodyEl, id){
41346 var body = Roo.getDom(id);
41348 body = document.createElement("div");
41351 //Roo.fly(body).addClass("x-tabs-item-body");
41352 Roo.fly(body).addClass("tab-pane");
41353 bodyEl.insertBefore(body, bodyEl.firstChild);
41357 createStripElements : function(stripEl, text, closable, tpl)
41359 var td = document.createElement("li"); // was td..
41360 td.className = 'nav-item';
41362 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41365 stripEl.appendChild(td);
41367 td.className = "x-tabs-closable";
41368 if(!this.closeTpl){
41369 this.closeTpl = new Roo.Template(
41370 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41371 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41372 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41375 var el = this.closeTpl.overwrite(td, {"text": text});
41376 var close = el.getElementsByTagName("div")[0];
41377 var inner = el.getElementsByTagName("em")[0];
41378 return {"el": el, "close": close, "inner": inner};
41381 // not sure what this is..
41382 // if(!this.tabTpl){
41383 //this.tabTpl = new Roo.Template(
41384 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41385 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41387 // this.tabTpl = new Roo.Template(
41388 // '<a href="#">' +
41389 // '<span unselectable="on"' +
41390 // (this.disableTooltips ? '' : ' title="{text}"') +
41391 // ' >{text}</span></a>'
41397 var template = tpl || this.tabTpl || false;
41400 template = new Roo.Template(
41401 Roo.bootstrap.version == 4 ?
41403 '<a class="nav-link" href="#" unselectable="on"' +
41404 (this.disableTooltips ? '' : ' title="{text}"') +
41407 '<a class="nav-link" href="#">' +
41408 '<span unselectable="on"' +
41409 (this.disableTooltips ? '' : ' title="{text}"') +
41410 ' >{text}</span></a>'
41415 switch (typeof(template)) {
41419 template = new Roo.Template(template);
41425 var el = template.overwrite(td, {"text": text});
41427 var inner = el.getElementsByTagName("span")[0];
41429 return {"el": el, "inner": inner};
41437 * @class Roo.TabPanelItem
41438 * @extends Roo.util.Observable
41439 * Represents an individual item (tab plus body) in a TabPanel.
41440 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41441 * @param {String} id The id of this TabPanelItem
41442 * @param {String} text The text for the tab of this TabPanelItem
41443 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41445 Roo.bootstrap.panel.TabItem = function(config){
41447 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41448 * @type Roo.TabPanel
41450 this.tabPanel = config.panel;
41452 * The id for this TabPanelItem
41455 this.id = config.id;
41457 this.disabled = false;
41459 this.text = config.text;
41461 this.loaded = false;
41462 this.closable = config.closable;
41465 * The body element for this TabPanelItem.
41466 * @type Roo.Element
41468 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41469 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41470 this.bodyEl.setStyle("display", "block");
41471 this.bodyEl.setStyle("zoom", "1");
41472 //this.hideAction();
41474 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41476 this.el = Roo.get(els.el);
41477 this.inner = Roo.get(els.inner, true);
41478 this.textEl = Roo.bootstrap.version == 4 ?
41479 this.el : Roo.get(this.el.dom.firstChild, true);
41481 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41482 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41485 // this.el.on("mousedown", this.onTabMouseDown, this);
41486 this.el.on("click", this.onTabClick, this);
41488 if(config.closable){
41489 var c = Roo.get(els.close, true);
41490 c.dom.title = this.closeText;
41491 c.addClassOnOver("close-over");
41492 c.on("click", this.closeClick, this);
41498 * Fires when this tab becomes the active tab.
41499 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41500 * @param {Roo.TabPanelItem} this
41504 * @event beforeclose
41505 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41506 * @param {Roo.TabPanelItem} this
41507 * @param {Object} e Set cancel to true on this object to cancel the close.
41509 "beforeclose": true,
41512 * Fires when this tab is closed.
41513 * @param {Roo.TabPanelItem} this
41517 * @event deactivate
41518 * Fires when this tab is no longer the active tab.
41519 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41520 * @param {Roo.TabPanelItem} this
41522 "deactivate" : true
41524 this.hidden = false;
41526 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41529 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41531 purgeListeners : function(){
41532 Roo.util.Observable.prototype.purgeListeners.call(this);
41533 this.el.removeAllListeners();
41536 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41539 this.status_node.addClass("active");
41542 this.tabPanel.stripWrap.repaint();
41544 this.fireEvent("activate", this.tabPanel, this);
41548 * Returns true if this tab is the active tab.
41549 * @return {Boolean}
41551 isActive : function(){
41552 return this.tabPanel.getActiveTab() == this;
41556 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41559 this.status_node.removeClass("active");
41561 this.fireEvent("deactivate", this.tabPanel, this);
41564 hideAction : function(){
41565 this.bodyEl.hide();
41566 this.bodyEl.setStyle("position", "absolute");
41567 this.bodyEl.setLeft("-20000px");
41568 this.bodyEl.setTop("-20000px");
41571 showAction : function(){
41572 this.bodyEl.setStyle("position", "relative");
41573 this.bodyEl.setTop("");
41574 this.bodyEl.setLeft("");
41575 this.bodyEl.show();
41579 * Set the tooltip for the tab.
41580 * @param {String} tooltip The tab's tooltip
41582 setTooltip : function(text){
41583 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41584 this.textEl.dom.qtip = text;
41585 this.textEl.dom.removeAttribute('title');
41587 this.textEl.dom.title = text;
41591 onTabClick : function(e){
41592 e.preventDefault();
41593 this.tabPanel.activate(this.id);
41596 onTabMouseDown : function(e){
41597 e.preventDefault();
41598 this.tabPanel.activate(this.id);
41601 getWidth : function(){
41602 return this.inner.getWidth();
41605 setWidth : function(width){
41606 var iwidth = width - this.linode.getPadding("lr");
41607 this.inner.setWidth(iwidth);
41608 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41609 this.linode.setWidth(width);
41613 * Show or hide the tab
41614 * @param {Boolean} hidden True to hide or false to show.
41616 setHidden : function(hidden){
41617 this.hidden = hidden;
41618 this.linode.setStyle("display", hidden ? "none" : "");
41622 * Returns true if this tab is "hidden"
41623 * @return {Boolean}
41625 isHidden : function(){
41626 return this.hidden;
41630 * Returns the text for this tab
41633 getText : function(){
41637 autoSize : function(){
41638 //this.el.beginMeasure();
41639 this.textEl.setWidth(1);
41641 * #2804 [new] Tabs in Roojs
41642 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41644 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41645 //this.el.endMeasure();
41649 * Sets the text for the tab (Note: this also sets the tooltip text)
41650 * @param {String} text The tab's text and tooltip
41652 setText : function(text){
41654 this.textEl.update(text);
41655 this.setTooltip(text);
41656 //if(!this.tabPanel.resizeTabs){
41657 // this.autoSize();
41661 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41663 activate : function(){
41664 this.tabPanel.activate(this.id);
41668 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41670 disable : function(){
41671 if(this.tabPanel.active != this){
41672 this.disabled = true;
41673 this.status_node.addClass("disabled");
41678 * Enables this TabPanelItem if it was previously disabled.
41680 enable : function(){
41681 this.disabled = false;
41682 this.status_node.removeClass("disabled");
41686 * Sets the content for this TabPanelItem.
41687 * @param {String} content The content
41688 * @param {Boolean} loadScripts true to look for and load scripts
41690 setContent : function(content, loadScripts){
41691 this.bodyEl.update(content, loadScripts);
41695 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41696 * @return {Roo.UpdateManager} The UpdateManager
41698 getUpdateManager : function(){
41699 return this.bodyEl.getUpdateManager();
41703 * Set a URL to be used to load the content for this TabPanelItem.
41704 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41705 * @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)
41706 * @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)
41707 * @return {Roo.UpdateManager} The UpdateManager
41709 setUrl : function(url, params, loadOnce){
41710 if(this.refreshDelegate){
41711 this.un('activate', this.refreshDelegate);
41713 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41714 this.on("activate", this.refreshDelegate);
41715 return this.bodyEl.getUpdateManager();
41719 _handleRefresh : function(url, params, loadOnce){
41720 if(!loadOnce || !this.loaded){
41721 var updater = this.bodyEl.getUpdateManager();
41722 updater.update(url, params, this._setLoaded.createDelegate(this));
41727 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41728 * Will fail silently if the setUrl method has not been called.
41729 * This does not activate the panel, just updates its content.
41731 refresh : function(){
41732 if(this.refreshDelegate){
41733 this.loaded = false;
41734 this.refreshDelegate();
41739 _setLoaded : function(){
41740 this.loaded = true;
41744 closeClick : function(e){
41747 this.fireEvent("beforeclose", this, o);
41748 if(o.cancel !== true){
41749 this.tabPanel.removeTab(this.id);
41753 * The text displayed in the tooltip for the close icon.
41756 closeText : "Close this tab"
41759 * This script refer to:
41760 * Title: International Telephone Input
41761 * Author: Jack O'Connor
41762 * Code version: v12.1.12
41763 * Availability: https://github.com/jackocnr/intl-tel-input.git
41766 Roo.bootstrap.PhoneInputData = function() {
41769 "Afghanistan (افغانستان)",
41774 "Albania (Shqipëri)",
41779 "Algeria (الجزائر)",
41804 "Antigua and Barbuda",
41814 "Armenia (Հայաստան)",
41830 "Austria (Österreich)",
41835 "Azerbaijan (Azərbaycan)",
41845 "Bahrain (البحرين)",
41850 "Bangladesh (বাংলাদেশ)",
41860 "Belarus (Беларусь)",
41865 "Belgium (België)",
41895 "Bosnia and Herzegovina (Босна и Херцеговина)",
41910 "British Indian Ocean Territory",
41915 "British Virgin Islands",
41925 "Bulgaria (България)",
41935 "Burundi (Uburundi)",
41940 "Cambodia (កម្ពុជា)",
41945 "Cameroon (Cameroun)",
41954 ["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"]
41957 "Cape Verde (Kabu Verdi)",
41962 "Caribbean Netherlands",
41973 "Central African Republic (République centrafricaine)",
41993 "Christmas Island",
41999 "Cocos (Keeling) Islands",
42010 "Comoros (جزر القمر)",
42015 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42020 "Congo (Republic) (Congo-Brazzaville)",
42040 "Croatia (Hrvatska)",
42061 "Czech Republic (Česká republika)",
42066 "Denmark (Danmark)",
42081 "Dominican Republic (República Dominicana)",
42085 ["809", "829", "849"]
42103 "Equatorial Guinea (Guinea Ecuatorial)",
42123 "Falkland Islands (Islas Malvinas)",
42128 "Faroe Islands (Føroyar)",
42149 "French Guiana (Guyane française)",
42154 "French Polynesia (Polynésie française)",
42169 "Georgia (საქართველო)",
42174 "Germany (Deutschland)",
42194 "Greenland (Kalaallit Nunaat)",
42231 "Guinea-Bissau (Guiné Bissau)",
42256 "Hungary (Magyarország)",
42261 "Iceland (Ísland)",
42281 "Iraq (العراق)",
42297 "Israel (ישראל)",
42324 "Jordan (الأردن)",
42329 "Kazakhstan (Казахстан)",
42350 "Kuwait (الكويت)",
42355 "Kyrgyzstan (Кыргызстан)",
42365 "Latvia (Latvija)",
42370 "Lebanon (لبنان)",
42385 "Libya (ليبيا)",
42395 "Lithuania (Lietuva)",
42410 "Macedonia (FYROM) (Македонија)",
42415 "Madagascar (Madagasikara)",
42445 "Marshall Islands",
42455 "Mauritania (موريتانيا)",
42460 "Mauritius (Moris)",
42481 "Moldova (Republica Moldova)",
42491 "Mongolia (Монгол)",
42496 "Montenegro (Crna Gora)",
42506 "Morocco (المغرب)",
42512 "Mozambique (Moçambique)",
42517 "Myanmar (Burma) (မြန်မာ)",
42522 "Namibia (Namibië)",
42537 "Netherlands (Nederland)",
42542 "New Caledonia (Nouvelle-Calédonie)",
42577 "North Korea (조선 민주주의 인민 공화국)",
42582 "Northern Mariana Islands",
42598 "Pakistan (پاکستان)",
42608 "Palestine (فلسطين)",
42618 "Papua New Guinea",
42660 "Réunion (La Réunion)",
42666 "Romania (România)",
42682 "Saint Barthélemy",
42693 "Saint Kitts and Nevis",
42703 "Saint Martin (Saint-Martin (partie française))",
42709 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42714 "Saint Vincent and the Grenadines",
42729 "São Tomé and Príncipe (São Tomé e Príncipe)",
42734 "Saudi Arabia (المملكة العربية السعودية)",
42739 "Senegal (Sénégal)",
42769 "Slovakia (Slovensko)",
42774 "Slovenia (Slovenija)",
42784 "Somalia (Soomaaliya)",
42794 "South Korea (대한민국)",
42799 "South Sudan (جنوب السودان)",
42809 "Sri Lanka (ශ්රී ලංකාව)",
42814 "Sudan (السودان)",
42824 "Svalbard and Jan Mayen",
42835 "Sweden (Sverige)",
42840 "Switzerland (Schweiz)",
42845 "Syria (سوريا)",
42890 "Trinidad and Tobago",
42895 "Tunisia (تونس)",
42900 "Turkey (Türkiye)",
42910 "Turks and Caicos Islands",
42920 "U.S. Virgin Islands",
42930 "Ukraine (Україна)",
42935 "United Arab Emirates (الإمارات العربية المتحدة)",
42957 "Uzbekistan (Oʻzbekiston)",
42967 "Vatican City (Città del Vaticano)",
42978 "Vietnam (Việt Nam)",
42983 "Wallis and Futuna (Wallis-et-Futuna)",
42988 "Western Sahara (الصحراء الغربية)",
42994 "Yemen (اليمن)",
43018 * This script refer to:
43019 * Title: International Telephone Input
43020 * Author: Jack O'Connor
43021 * Code version: v12.1.12
43022 * Availability: https://github.com/jackocnr/intl-tel-input.git
43026 * @class Roo.bootstrap.PhoneInput
43027 * @extends Roo.bootstrap.TriggerField
43028 * An input with International dial-code selection
43030 * @cfg {String} defaultDialCode default '+852'
43031 * @cfg {Array} preferedCountries default []
43034 * Create a new PhoneInput.
43035 * @param {Object} config Configuration options
43038 Roo.bootstrap.PhoneInput = function(config) {
43039 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43042 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43044 listWidth: undefined,
43046 selectedClass: 'active',
43048 invalidClass : "has-warning",
43050 validClass: 'has-success',
43052 allowed: '0123456789',
43057 * @cfg {String} defaultDialCode The default dial code when initializing the input
43059 defaultDialCode: '+852',
43062 * @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
43064 preferedCountries: false,
43066 getAutoCreate : function()
43068 var data = Roo.bootstrap.PhoneInputData();
43069 var align = this.labelAlign || this.parentLabelAlign();
43072 this.allCountries = [];
43073 this.dialCodeMapping = [];
43075 for (var i = 0; i < data.length; i++) {
43077 this.allCountries[i] = {
43081 priority: c[3] || 0,
43082 areaCodes: c[4] || null
43084 this.dialCodeMapping[c[2]] = {
43087 priority: c[3] || 0,
43088 areaCodes: c[4] || null
43100 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43101 maxlength: this.max_length,
43102 cls : 'form-control tel-input',
43103 autocomplete: 'new-password'
43106 var hiddenInput = {
43109 cls: 'hidden-tel-input'
43113 hiddenInput.name = this.name;
43116 if (this.disabled) {
43117 input.disabled = true;
43120 var flag_container = {
43137 cls: this.hasFeedback ? 'has-feedback' : '',
43143 cls: 'dial-code-holder',
43150 cls: 'roo-select2-container input-group',
43157 if (this.fieldLabel.length) {
43160 tooltip: 'This field is required'
43166 cls: 'control-label',
43172 html: this.fieldLabel
43175 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43181 if(this.indicatorpos == 'right') {
43182 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43189 if(align == 'left') {
43197 if(this.labelWidth > 12){
43198 label.style = "width: " + this.labelWidth + 'px';
43200 if(this.labelWidth < 13 && this.labelmd == 0){
43201 this.labelmd = this.labelWidth;
43203 if(this.labellg > 0){
43204 label.cls += ' col-lg-' + this.labellg;
43205 input.cls += ' col-lg-' + (12 - this.labellg);
43207 if(this.labelmd > 0){
43208 label.cls += ' col-md-' + this.labelmd;
43209 container.cls += ' col-md-' + (12 - this.labelmd);
43211 if(this.labelsm > 0){
43212 label.cls += ' col-sm-' + this.labelsm;
43213 container.cls += ' col-sm-' + (12 - this.labelsm);
43215 if(this.labelxs > 0){
43216 label.cls += ' col-xs-' + this.labelxs;
43217 container.cls += ' col-xs-' + (12 - this.labelxs);
43227 var settings = this;
43229 ['xs','sm','md','lg'].map(function(size){
43230 if (settings[size]) {
43231 cfg.cls += ' col-' + size + '-' + settings[size];
43235 this.store = new Roo.data.Store({
43236 proxy : new Roo.data.MemoryProxy({}),
43237 reader : new Roo.data.JsonReader({
43248 'name' : 'dialCode',
43252 'name' : 'priority',
43256 'name' : 'areaCodes',
43263 if(!this.preferedCountries) {
43264 this.preferedCountries = [
43271 var p = this.preferedCountries.reverse();
43274 for (var i = 0; i < p.length; i++) {
43275 for (var j = 0; j < this.allCountries.length; j++) {
43276 if(this.allCountries[j].iso2 == p[i]) {
43277 var t = this.allCountries[j];
43278 this.allCountries.splice(j,1);
43279 this.allCountries.unshift(t);
43285 this.store.proxy.data = {
43287 data: this.allCountries
43293 initEvents : function()
43296 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43298 this.indicator = this.indicatorEl();
43299 this.flag = this.flagEl();
43300 this.dialCodeHolder = this.dialCodeHolderEl();
43302 this.trigger = this.el.select('div.flag-box',true).first();
43303 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43308 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43309 _this.list.setWidth(lw);
43312 this.list.on('mouseover', this.onViewOver, this);
43313 this.list.on('mousemove', this.onViewMove, this);
43314 this.inputEl().on("keyup", this.onKeyUp, this);
43315 this.inputEl().on("keypress", this.onKeyPress, this);
43317 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43319 this.view = new Roo.View(this.list, this.tpl, {
43320 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43323 this.view.on('click', this.onViewClick, this);
43324 this.setValue(this.defaultDialCode);
43327 onTriggerClick : function(e)
43329 Roo.log('trigger click');
43334 if(this.isExpanded()){
43336 this.hasFocus = false;
43338 this.store.load({});
43339 this.hasFocus = true;
43344 isExpanded : function()
43346 return this.list.isVisible();
43349 collapse : function()
43351 if(!this.isExpanded()){
43355 Roo.get(document).un('mousedown', this.collapseIf, this);
43356 Roo.get(document).un('mousewheel', this.collapseIf, this);
43357 this.fireEvent('collapse', this);
43361 expand : function()
43365 if(this.isExpanded() || !this.hasFocus){
43369 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43370 this.list.setWidth(lw);
43373 this.restrictHeight();
43375 Roo.get(document).on('mousedown', this.collapseIf, this);
43376 Roo.get(document).on('mousewheel', this.collapseIf, this);
43378 this.fireEvent('expand', this);
43381 restrictHeight : function()
43383 this.list.alignTo(this.inputEl(), this.listAlign);
43384 this.list.alignTo(this.inputEl(), this.listAlign);
43387 onViewOver : function(e, t)
43389 if(this.inKeyMode){
43392 var item = this.view.findItemFromChild(t);
43395 var index = this.view.indexOf(item);
43396 this.select(index, false);
43401 onViewClick : function(view, doFocus, el, e)
43403 var index = this.view.getSelectedIndexes()[0];
43405 var r = this.store.getAt(index);
43408 this.onSelect(r, index);
43410 if(doFocus !== false && !this.blockFocus){
43411 this.inputEl().focus();
43415 onViewMove : function(e, t)
43417 this.inKeyMode = false;
43420 select : function(index, scrollIntoView)
43422 this.selectedIndex = index;
43423 this.view.select(index);
43424 if(scrollIntoView !== false){
43425 var el = this.view.getNode(index);
43427 this.list.scrollChildIntoView(el, false);
43432 createList : function()
43434 this.list = Roo.get(document.body).createChild({
43436 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43437 style: 'display:none'
43440 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43443 collapseIf : function(e)
43445 var in_combo = e.within(this.el);
43446 var in_list = e.within(this.list);
43447 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43449 if (in_combo || in_list || is_list) {
43455 onSelect : function(record, index)
43457 if(this.fireEvent('beforeselect', this, record, index) !== false){
43459 this.setFlagClass(record.data.iso2);
43460 this.setDialCode(record.data.dialCode);
43461 this.hasFocus = false;
43463 this.fireEvent('select', this, record, index);
43467 flagEl : function()
43469 var flag = this.el.select('div.flag',true).first();
43476 dialCodeHolderEl : function()
43478 var d = this.el.select('input.dial-code-holder',true).first();
43485 setDialCode : function(v)
43487 this.dialCodeHolder.dom.value = '+'+v;
43490 setFlagClass : function(n)
43492 this.flag.dom.className = 'flag '+n;
43495 getValue : function()
43497 var v = this.inputEl().getValue();
43498 if(this.dialCodeHolder) {
43499 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43504 setValue : function(v)
43506 var d = this.getDialCode(v);
43508 //invalid dial code
43509 if(v.length == 0 || !d || d.length == 0) {
43511 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43512 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43518 this.setFlagClass(this.dialCodeMapping[d].iso2);
43519 this.setDialCode(d);
43520 this.inputEl().dom.value = v.replace('+'+d,'');
43521 this.hiddenEl().dom.value = this.getValue();
43526 getDialCode : function(v)
43530 if (v.length == 0) {
43531 return this.dialCodeHolder.dom.value;
43535 if (v.charAt(0) != "+") {
43538 var numericChars = "";
43539 for (var i = 1; i < v.length; i++) {
43540 var c = v.charAt(i);
43543 if (this.dialCodeMapping[numericChars]) {
43544 dialCode = v.substr(1, i);
43546 if (numericChars.length == 4) {
43556 this.setValue(this.defaultDialCode);
43560 hiddenEl : function()
43562 return this.el.select('input.hidden-tel-input',true).first();
43565 // after setting val
43566 onKeyUp : function(e){
43567 this.setValue(this.getValue());
43570 onKeyPress : function(e){
43571 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43578 * @class Roo.bootstrap.MoneyField
43579 * @extends Roo.bootstrap.ComboBox
43580 * Bootstrap MoneyField class
43583 * Create a new MoneyField.
43584 * @param {Object} config Configuration options
43587 Roo.bootstrap.MoneyField = function(config) {
43589 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43593 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43596 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43598 allowDecimals : true,
43600 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43602 decimalSeparator : ".",
43604 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43606 decimalPrecision : 0,
43608 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43610 allowNegative : true,
43612 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43616 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43618 minValue : Number.NEGATIVE_INFINITY,
43620 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43622 maxValue : Number.MAX_VALUE,
43624 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43626 minText : "The minimum value for this field is {0}",
43628 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43630 maxText : "The maximum value for this field is {0}",
43632 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43633 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43635 nanText : "{0} is not a valid number",
43637 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43641 * @cfg {String} defaults currency of the MoneyField
43642 * value should be in lkey
43644 defaultCurrency : false,
43646 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43648 thousandsDelimiter : false,
43650 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43661 getAutoCreate : function()
43663 var align = this.labelAlign || this.parentLabelAlign();
43675 cls : 'form-control roo-money-amount-input',
43676 autocomplete: 'new-password'
43679 var hiddenInput = {
43683 cls: 'hidden-number-input'
43686 if(this.max_length) {
43687 input.maxlength = this.max_length;
43691 hiddenInput.name = this.name;
43694 if (this.disabled) {
43695 input.disabled = true;
43698 var clg = 12 - this.inputlg;
43699 var cmd = 12 - this.inputmd;
43700 var csm = 12 - this.inputsm;
43701 var cxs = 12 - this.inputxs;
43705 cls : 'row roo-money-field',
43709 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43713 cls: 'roo-select2-container input-group',
43717 cls : 'form-control roo-money-currency-input',
43718 autocomplete: 'new-password',
43720 name : this.currencyName
43724 cls : 'input-group-addon',
43738 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43742 cls: this.hasFeedback ? 'has-feedback' : '',
43753 if (this.fieldLabel.length) {
43756 tooltip: 'This field is required'
43762 cls: 'control-label',
43768 html: this.fieldLabel
43771 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43777 if(this.indicatorpos == 'right') {
43778 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43785 if(align == 'left') {
43793 if(this.labelWidth > 12){
43794 label.style = "width: " + this.labelWidth + 'px';
43796 if(this.labelWidth < 13 && this.labelmd == 0){
43797 this.labelmd = this.labelWidth;
43799 if(this.labellg > 0){
43800 label.cls += ' col-lg-' + this.labellg;
43801 input.cls += ' col-lg-' + (12 - this.labellg);
43803 if(this.labelmd > 0){
43804 label.cls += ' col-md-' + this.labelmd;
43805 container.cls += ' col-md-' + (12 - this.labelmd);
43807 if(this.labelsm > 0){
43808 label.cls += ' col-sm-' + this.labelsm;
43809 container.cls += ' col-sm-' + (12 - this.labelsm);
43811 if(this.labelxs > 0){
43812 label.cls += ' col-xs-' + this.labelxs;
43813 container.cls += ' col-xs-' + (12 - this.labelxs);
43824 var settings = this;
43826 ['xs','sm','md','lg'].map(function(size){
43827 if (settings[size]) {
43828 cfg.cls += ' col-' + size + '-' + settings[size];
43835 initEvents : function()
43837 this.indicator = this.indicatorEl();
43839 this.initCurrencyEvent();
43841 this.initNumberEvent();
43844 initCurrencyEvent : function()
43847 throw "can not find store for combo";
43850 this.store = Roo.factory(this.store, Roo.data);
43851 this.store.parent = this;
43855 this.triggerEl = this.el.select('.input-group-addon', true).first();
43857 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43862 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43863 _this.list.setWidth(lw);
43866 this.list.on('mouseover', this.onViewOver, this);
43867 this.list.on('mousemove', this.onViewMove, this);
43868 this.list.on('scroll', this.onViewScroll, this);
43871 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43874 this.view = new Roo.View(this.list, this.tpl, {
43875 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43878 this.view.on('click', this.onViewClick, this);
43880 this.store.on('beforeload', this.onBeforeLoad, this);
43881 this.store.on('load', this.onLoad, this);
43882 this.store.on('loadexception', this.onLoadException, this);
43884 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43885 "up" : function(e){
43886 this.inKeyMode = true;
43890 "down" : function(e){
43891 if(!this.isExpanded()){
43892 this.onTriggerClick();
43894 this.inKeyMode = true;
43899 "enter" : function(e){
43902 if(this.fireEvent("specialkey", this, e)){
43903 this.onViewClick(false);
43909 "esc" : function(e){
43913 "tab" : function(e){
43916 if(this.fireEvent("specialkey", this, e)){
43917 this.onViewClick(false);
43925 doRelay : function(foo, bar, hname){
43926 if(hname == 'down' || this.scope.isExpanded()){
43927 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43935 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43939 initNumberEvent : function(e)
43941 this.inputEl().on("keydown" , this.fireKey, this);
43942 this.inputEl().on("focus", this.onFocus, this);
43943 this.inputEl().on("blur", this.onBlur, this);
43945 this.inputEl().relayEvent('keyup', this);
43947 if(this.indicator){
43948 this.indicator.addClass('invisible');
43951 this.originalValue = this.getValue();
43953 if(this.validationEvent == 'keyup'){
43954 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43955 this.inputEl().on('keyup', this.filterValidation, this);
43957 else if(this.validationEvent !== false){
43958 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43961 if(this.selectOnFocus){
43962 this.on("focus", this.preFocus, this);
43965 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43966 this.inputEl().on("keypress", this.filterKeys, this);
43968 this.inputEl().relayEvent('keypress', this);
43971 var allowed = "0123456789";
43973 if(this.allowDecimals){
43974 allowed += this.decimalSeparator;
43977 if(this.allowNegative){
43981 if(this.thousandsDelimiter) {
43985 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43987 var keyPress = function(e){
43989 var k = e.getKey();
43991 var c = e.getCharCode();
43994 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43995 allowed.indexOf(String.fromCharCode(c)) === -1
44001 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44005 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44010 this.inputEl().on("keypress", keyPress, this);
44014 onTriggerClick : function(e)
44021 this.loadNext = false;
44023 if(this.isExpanded()){
44028 this.hasFocus = true;
44030 if(this.triggerAction == 'all') {
44031 this.doQuery(this.allQuery, true);
44035 this.doQuery(this.getRawValue());
44038 getCurrency : function()
44040 var v = this.currencyEl().getValue();
44045 restrictHeight : function()
44047 this.list.alignTo(this.currencyEl(), this.listAlign);
44048 this.list.alignTo(this.currencyEl(), this.listAlign);
44051 onViewClick : function(view, doFocus, el, e)
44053 var index = this.view.getSelectedIndexes()[0];
44055 var r = this.store.getAt(index);
44058 this.onSelect(r, index);
44062 onSelect : function(record, index){
44064 if(this.fireEvent('beforeselect', this, record, index) !== false){
44066 this.setFromCurrencyData(index > -1 ? record.data : false);
44070 this.fireEvent('select', this, record, index);
44074 setFromCurrencyData : function(o)
44078 this.lastCurrency = o;
44080 if (this.currencyField) {
44081 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44083 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44086 this.lastSelectionText = currency;
44088 //setting default currency
44089 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44090 this.setCurrency(this.defaultCurrency);
44094 this.setCurrency(currency);
44097 setFromData : function(o)
44101 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44103 this.setFromCurrencyData(c);
44108 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44110 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44113 this.setValue(value);
44117 setCurrency : function(v)
44119 this.currencyValue = v;
44122 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44127 setValue : function(v)
44129 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44135 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44137 this.inputEl().dom.value = (v == '') ? '' :
44138 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44140 if(!this.allowZero && v === '0') {
44141 this.hiddenEl().dom.value = '';
44142 this.inputEl().dom.value = '';
44149 getRawValue : function()
44151 var v = this.inputEl().getValue();
44156 getValue : function()
44158 return this.fixPrecision(this.parseValue(this.getRawValue()));
44161 parseValue : function(value)
44163 if(this.thousandsDelimiter) {
44165 r = new RegExp(",", "g");
44166 value = value.replace(r, "");
44169 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44170 return isNaN(value) ? '' : value;
44174 fixPrecision : function(value)
44176 if(this.thousandsDelimiter) {
44178 r = new RegExp(",", "g");
44179 value = value.replace(r, "");
44182 var nan = isNaN(value);
44184 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44185 return nan ? '' : value;
44187 return parseFloat(value).toFixed(this.decimalPrecision);
44190 decimalPrecisionFcn : function(v)
44192 return Math.floor(v);
44195 validateValue : function(value)
44197 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44201 var num = this.parseValue(value);
44204 this.markInvalid(String.format(this.nanText, value));
44208 if(num < this.minValue){
44209 this.markInvalid(String.format(this.minText, this.minValue));
44213 if(num > this.maxValue){
44214 this.markInvalid(String.format(this.maxText, this.maxValue));
44221 validate : function()
44223 if(this.disabled || this.allowBlank){
44228 var currency = this.getCurrency();
44230 if(this.validateValue(this.getRawValue()) && currency.length){
44235 this.markInvalid();
44239 getName: function()
44244 beforeBlur : function()
44250 var v = this.parseValue(this.getRawValue());
44257 onBlur : function()
44261 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44262 //this.el.removeClass(this.focusClass);
44265 this.hasFocus = false;
44267 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44271 var v = this.getValue();
44273 if(String(v) !== String(this.startValue)){
44274 this.fireEvent('change', this, v, this.startValue);
44277 this.fireEvent("blur", this);
44280 inputEl : function()
44282 return this.el.select('.roo-money-amount-input', true).first();
44285 currencyEl : function()
44287 return this.el.select('.roo-money-currency-input', true).first();
44290 hiddenEl : function()
44292 return this.el.select('input.hidden-number-input',true).first();
44296 * @class Roo.bootstrap.BezierSignature
44297 * @extends Roo.bootstrap.Component
44298 * Bootstrap BezierSignature class
44299 * This script refer to:
44300 * Title: Signature Pad
44302 * Availability: https://github.com/szimek/signature_pad
44305 * Create a new BezierSignature
44306 * @param {Object} config The config object
44309 Roo.bootstrap.BezierSignature = function(config){
44310 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44316 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44323 mouse_btn_down: true,
44326 * @cfg {int} canvas height
44328 canvas_height: '200px',
44331 * @cfg {float|function} Radius of a single dot.
44336 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44341 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44346 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44351 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44356 * @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.
44358 bg_color: 'rgba(0, 0, 0, 0)',
44361 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44363 dot_color: 'black',
44366 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44368 velocity_filter_weight: 0.7,
44371 * @cfg {function} Callback when stroke begin.
44376 * @cfg {function} Callback when stroke end.
44380 getAutoCreate : function()
44382 var cls = 'roo-signature column';
44385 cls += ' ' + this.cls;
44395 for(var i = 0; i < col_sizes.length; i++) {
44396 if(this[col_sizes[i]]) {
44397 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44407 cls: 'roo-signature-body',
44411 cls: 'roo-signature-body-canvas',
44412 height: this.canvas_height,
44413 width: this.canvas_width
44420 style: 'display: none'
44428 initEvents: function()
44430 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44432 var canvas = this.canvasEl();
44434 // mouse && touch event swapping...
44435 canvas.dom.style.touchAction = 'none';
44436 canvas.dom.style.msTouchAction = 'none';
44438 this.mouse_btn_down = false;
44439 canvas.on('mousedown', this._handleMouseDown, this);
44440 canvas.on('mousemove', this._handleMouseMove, this);
44441 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44443 if (window.PointerEvent) {
44444 canvas.on('pointerdown', this._handleMouseDown, this);
44445 canvas.on('pointermove', this._handleMouseMove, this);
44446 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44449 if ('ontouchstart' in window) {
44450 canvas.on('touchstart', this._handleTouchStart, this);
44451 canvas.on('touchmove', this._handleTouchMove, this);
44452 canvas.on('touchend', this._handleTouchEnd, this);
44455 Roo.EventManager.onWindowResize(this.resize, this, true);
44457 // file input event
44458 this.fileEl().on('change', this.uploadImage, this);
44465 resize: function(){
44467 var canvas = this.canvasEl().dom;
44468 var ctx = this.canvasElCtx();
44469 var img_data = false;
44471 if(canvas.width > 0) {
44472 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44474 // setting canvas width will clean img data
44477 var style = window.getComputedStyle ?
44478 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44480 var padding_left = parseInt(style.paddingLeft) || 0;
44481 var padding_right = parseInt(style.paddingRight) || 0;
44483 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44486 ctx.putImageData(img_data, 0, 0);
44490 _handleMouseDown: function(e)
44492 if (e.browserEvent.which === 1) {
44493 this.mouse_btn_down = true;
44494 this.strokeBegin(e);
44498 _handleMouseMove: function (e)
44500 if (this.mouse_btn_down) {
44501 this.strokeMoveUpdate(e);
44505 _handleMouseUp: function (e)
44507 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44508 this.mouse_btn_down = false;
44513 _handleTouchStart: function (e) {
44515 e.preventDefault();
44516 if (e.browserEvent.targetTouches.length === 1) {
44517 // var touch = e.browserEvent.changedTouches[0];
44518 // this.strokeBegin(touch);
44520 this.strokeBegin(e); // assume e catching the correct xy...
44524 _handleTouchMove: function (e) {
44525 e.preventDefault();
44526 // var touch = event.targetTouches[0];
44527 // _this._strokeMoveUpdate(touch);
44528 this.strokeMoveUpdate(e);
44531 _handleTouchEnd: function (e) {
44532 var wasCanvasTouched = e.target === this.canvasEl().dom;
44533 if (wasCanvasTouched) {
44534 e.preventDefault();
44535 // var touch = event.changedTouches[0];
44536 // _this._strokeEnd(touch);
44541 reset: function () {
44542 this._lastPoints = [];
44543 this._lastVelocity = 0;
44544 this._lastWidth = (this.min_width + this.max_width) / 2;
44545 this.canvasElCtx().fillStyle = this.dot_color;
44548 strokeMoveUpdate: function(e)
44550 this.strokeUpdate(e);
44552 if (this.throttle) {
44553 this.throttleStroke(this.strokeUpdate, this.throttle);
44556 this.strokeUpdate(e);
44560 strokeBegin: function(e)
44562 var newPointGroup = {
44563 color: this.dot_color,
44567 if (typeof this.onBegin === 'function') {
44571 this.curve_data.push(newPointGroup);
44573 this.strokeUpdate(e);
44576 strokeUpdate: function(e)
44578 var rect = this.canvasEl().dom.getBoundingClientRect();
44579 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44580 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44581 var lastPoints = lastPointGroup.points;
44582 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44583 var isLastPointTooClose = lastPoint
44584 ? point.distanceTo(lastPoint) <= this.min_distance
44586 var color = lastPointGroup.color;
44587 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44588 var curve = this.addPoint(point);
44590 this.drawDot({color: color, point: point});
44593 this.drawCurve({color: color, curve: curve});
44603 strokeEnd: function(e)
44605 this.strokeUpdate(e);
44606 if (typeof this.onEnd === 'function') {
44611 addPoint: function (point) {
44612 var _lastPoints = this._lastPoints;
44613 _lastPoints.push(point);
44614 if (_lastPoints.length > 2) {
44615 if (_lastPoints.length === 3) {
44616 _lastPoints.unshift(_lastPoints[0]);
44618 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44619 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44620 _lastPoints.shift();
44626 calculateCurveWidths: function (startPoint, endPoint) {
44627 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44628 (1 - this.velocity_filter_weight) * this._lastVelocity;
44630 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44633 start: this._lastWidth
44636 this._lastVelocity = velocity;
44637 this._lastWidth = newWidth;
44641 drawDot: function (_a) {
44642 var color = _a.color, point = _a.point;
44643 var ctx = this.canvasElCtx();
44644 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44646 this.drawCurveSegment(point.x, point.y, width);
44648 ctx.fillStyle = color;
44652 drawCurve: function (_a) {
44653 var color = _a.color, curve = _a.curve;
44654 var ctx = this.canvasElCtx();
44655 var widthDelta = curve.endWidth - curve.startWidth;
44656 var drawSteps = Math.floor(curve.length()) * 2;
44658 ctx.fillStyle = color;
44659 for (var i = 0; i < drawSteps; i += 1) {
44660 var t = i / drawSteps;
44666 var x = uuu * curve.startPoint.x;
44667 x += 3 * uu * t * curve.control1.x;
44668 x += 3 * u * tt * curve.control2.x;
44669 x += ttt * curve.endPoint.x;
44670 var y = uuu * curve.startPoint.y;
44671 y += 3 * uu * t * curve.control1.y;
44672 y += 3 * u * tt * curve.control2.y;
44673 y += ttt * curve.endPoint.y;
44674 var width = curve.startWidth + ttt * widthDelta;
44675 this.drawCurveSegment(x, y, width);
44681 drawCurveSegment: function (x, y, width) {
44682 var ctx = this.canvasElCtx();
44684 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44685 this.is_empty = false;
44690 var ctx = this.canvasElCtx();
44691 var canvas = this.canvasEl().dom;
44692 ctx.fillStyle = this.bg_color;
44693 ctx.clearRect(0, 0, canvas.width, canvas.height);
44694 ctx.fillRect(0, 0, canvas.width, canvas.height);
44695 this.curve_data = [];
44697 this.is_empty = true;
44702 return this.el.select('input',true).first();
44705 canvasEl: function()
44707 return this.el.select('canvas',true).first();
44710 canvasElCtx: function()
44712 return this.el.select('canvas',true).first().dom.getContext('2d');
44715 getImage: function(type)
44717 if(this.is_empty) {
44722 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44725 drawFromImage: function(img_src)
44727 var img = new Image();
44729 img.onload = function(){
44730 this.canvasElCtx().drawImage(img, 0, 0);
44735 this.is_empty = false;
44738 selectImage: function()
44740 this.fileEl().dom.click();
44743 uploadImage: function(e)
44745 var reader = new FileReader();
44747 reader.onload = function(e){
44748 var img = new Image();
44749 img.onload = function(){
44751 this.canvasElCtx().drawImage(img, 0, 0);
44753 img.src = e.target.result;
44756 reader.readAsDataURL(e.target.files[0]);
44759 // Bezier Point Constructor
44760 Point: (function () {
44761 function Point(x, y, time) {
44764 this.time = time || Date.now();
44766 Point.prototype.distanceTo = function (start) {
44767 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44769 Point.prototype.equals = function (other) {
44770 return this.x === other.x && this.y === other.y && this.time === other.time;
44772 Point.prototype.velocityFrom = function (start) {
44773 return this.time !== start.time
44774 ? this.distanceTo(start) / (this.time - start.time)
44781 // Bezier Constructor
44782 Bezier: (function () {
44783 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44784 this.startPoint = startPoint;
44785 this.control2 = control2;
44786 this.control1 = control1;
44787 this.endPoint = endPoint;
44788 this.startWidth = startWidth;
44789 this.endWidth = endWidth;
44791 Bezier.fromPoints = function (points, widths, scope) {
44792 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44793 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44794 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44796 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44797 var dx1 = s1.x - s2.x;
44798 var dy1 = s1.y - s2.y;
44799 var dx2 = s2.x - s3.x;
44800 var dy2 = s2.y - s3.y;
44801 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44802 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44803 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44804 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44805 var dxm = m1.x - m2.x;
44806 var dym = m1.y - m2.y;
44807 var k = l2 / (l1 + l2);
44808 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44809 var tx = s2.x - cm.x;
44810 var ty = s2.y - cm.y;
44812 c1: new scope.Point(m1.x + tx, m1.y + ty),
44813 c2: new scope.Point(m2.x + tx, m2.y + ty)
44816 Bezier.prototype.length = function () {
44821 for (var i = 0; i <= steps; i += 1) {
44823 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44824 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44826 var xdiff = cx - px;
44827 var ydiff = cy - py;
44828 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44835 Bezier.prototype.point = function (t, start, c1, c2, end) {
44836 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44837 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44838 + (3.0 * c2 * (1.0 - t) * t * t)
44839 + (end * t * t * t);
44844 throttleStroke: function(fn, wait) {
44845 if (wait === void 0) { wait = 250; }
44847 var timeout = null;
44851 var later = function () {
44852 previous = Date.now();
44854 result = fn.apply(storedContext, storedArgs);
44856 storedContext = null;
44860 return function wrapper() {
44862 for (var _i = 0; _i < arguments.length; _i++) {
44863 args[_i] = arguments[_i];
44865 var now = Date.now();
44866 var remaining = wait - (now - previous);
44867 storedContext = this;
44869 if (remaining <= 0 || remaining > wait) {
44871 clearTimeout(timeout);
44875 result = fn.apply(storedContext, storedArgs);
44877 storedContext = null;
44881 else if (!timeout) {
44882 timeout = window.setTimeout(later, remaining);