2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 //if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 //if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8457 onDblClick : function(e,el)
8459 var cell = Roo.get(el);
8461 if(!cell || (!this.cellSelection && !this.rowSelection)){
8465 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8466 cell = cell.findParent('td', false, true);
8469 if(!cell || typeof(cell) == 'undefined'){
8473 var row = cell.findParent('tr', false, true);
8475 if(!row || typeof(row) == 'undefined'){
8479 var cellIndex = cell.dom.cellIndex;
8480 var rowIndex = this.getRowIndex(row);
8482 if(this.cellSelection){
8483 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8486 if(this.rowSelection){
8487 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8491 sort : function(e,el)
8493 var col = Roo.get(el);
8495 if(!col.hasClass('sortable')){
8499 var sort = col.attr('sort');
8502 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8506 this.store.sortInfo = {field : sort, direction : dir};
8509 Roo.log("calling footer first");
8510 this.footer.onClick('first');
8513 this.store.load({ params : { start : 0 } });
8517 renderHeader : function()
8525 this.totalWidth = 0;
8527 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529 var config = cm.config[i];
8533 cls : 'x-hcol-' + i,
8536 html: cm.getColumnHeader(i)
8539 var tooltip = cm.getColumnTooltip(i);
8541 c.tooltip = tooltip;
8547 if(typeof(config.sortable) != 'undefined' && config.sortable){
8549 c.html = '<i class="fa"></i>' + c.html;
8552 // could use BS4 hidden-..-down
8554 if(typeof(config.lgHeader) != 'undefined'){
8555 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8558 if(typeof(config.mdHeader) != 'undefined'){
8559 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8562 if(typeof(config.smHeader) != 'undefined'){
8563 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8566 if(typeof(config.xsHeader) != 'undefined'){
8567 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8574 if(typeof(config.tooltip) != 'undefined'){
8575 c.tooltip = config.tooltip;
8578 if(typeof(config.colspan) != 'undefined'){
8579 c.colspan = config.colspan;
8582 if(typeof(config.hidden) != 'undefined' && config.hidden){
8583 c.style += ' display:none;';
8586 if(typeof(config.dataIndex) != 'undefined'){
8587 c.sort = config.dataIndex;
8592 if(typeof(config.align) != 'undefined' && config.align.length){
8593 c.style += ' text-align:' + config.align + ';';
8596 if(typeof(config.width) != 'undefined'){
8597 c.style += ' width:' + config.width + 'px;';
8598 this.totalWidth += config.width;
8600 this.totalWidth += 100; // assume minimum of 100 per column?
8603 if(typeof(config.cls) != 'undefined'){
8604 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8607 ['xs','sm','md','lg'].map(function(size){
8609 if(typeof(config[size]) == 'undefined'){
8613 if (!config[size]) { // 0 = hidden
8614 // BS 4 '0' is treated as hide that column and below.
8615 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619 c.cls += ' col-' + size + '-' + config[size] + (
8620 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8632 renderBody : function()
8642 colspan : this.cm.getColumnCount()
8652 renderFooter : function()
8662 colspan : this.cm.getColumnCount()
8676 // Roo.log('ds onload');
8681 var ds = this.store;
8683 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8684 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8685 if (_this.store.sortInfo) {
8687 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8688 e.select('i', true).addClass(['fa-arrow-up']);
8691 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8692 e.select('i', true).addClass(['fa-arrow-down']);
8697 var tbody = this.mainBody;
8699 if(ds.getCount() > 0){
8700 ds.data.each(function(d,rowIndex){
8701 var row = this.renderRow(cm, ds, rowIndex);
8703 tbody.createChild(row);
8707 if(row.cellObjects.length){
8708 Roo.each(row.cellObjects, function(r){
8709 _this.renderCellObject(r);
8716 var tfoot = this.el.select('tfoot', true).first();
8718 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8720 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8722 var total = this.ds.getTotalCount();
8724 if(this.footer.pageSize < total){
8725 this.mainFoot.show();
8729 Roo.each(this.el.select('tbody td', true).elements, function(e){
8730 e.on('mouseover', _this.onMouseover, _this);
8733 Roo.each(this.el.select('tbody td', true).elements, function(e){
8734 e.on('mouseout', _this.onMouseout, _this);
8736 this.fireEvent('rowsrendered', this);
8742 onUpdate : function(ds,record)
8744 this.refreshRow(record);
8748 onRemove : function(ds, record, index, isUpdate){
8749 if(isUpdate !== true){
8750 this.fireEvent("beforerowremoved", this, index, record);
8752 var bt = this.mainBody.dom;
8754 var rows = this.el.select('tbody > tr', true).elements;
8756 if(typeof(rows[index]) != 'undefined'){
8757 bt.removeChild(rows[index].dom);
8760 // if(bt.rows[index]){
8761 // bt.removeChild(bt.rows[index]);
8764 if(isUpdate !== true){
8765 //this.stripeRows(index);
8766 //this.syncRowHeights(index, index);
8768 this.fireEvent("rowremoved", this, index, record);
8772 onAdd : function(ds, records, rowIndex)
8774 //Roo.log('on Add called');
8775 // - note this does not handle multiple adding very well..
8776 var bt = this.mainBody.dom;
8777 for (var i =0 ; i < records.length;i++) {
8778 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8779 //Roo.log(records[i]);
8780 //Roo.log(this.store.getAt(rowIndex+i));
8781 this.insertRow(this.store, rowIndex + i, false);
8788 refreshRow : function(record){
8789 var ds = this.store, index;
8790 if(typeof record == 'number'){
8792 record = ds.getAt(index);
8794 index = ds.indexOf(record);
8796 return; // should not happen - but seems to
8799 this.insertRow(ds, index, true);
8801 this.onRemove(ds, record, index+1, true);
8803 //this.syncRowHeights(index, index);
8805 this.fireEvent("rowupdated", this, index, record);
8808 insertRow : function(dm, rowIndex, isUpdate){
8811 this.fireEvent("beforerowsinserted", this, rowIndex);
8813 //var s = this.getScrollState();
8814 var row = this.renderRow(this.cm, this.store, rowIndex);
8815 // insert before rowIndex..
8816 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8820 if(row.cellObjects.length){
8821 Roo.each(row.cellObjects, function(r){
8822 _this.renderCellObject(r);
8827 this.fireEvent("rowsinserted", this, rowIndex);
8828 //this.syncRowHeights(firstRow, lastRow);
8829 //this.stripeRows(firstRow);
8836 getRowDom : function(rowIndex)
8838 var rows = this.el.select('tbody > tr', true).elements;
8840 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8843 // returns the object tree for a tr..
8846 renderRow : function(cm, ds, rowIndex)
8848 var d = ds.getAt(rowIndex);
8852 cls : 'x-row-' + rowIndex,
8856 var cellObjects = [];
8858 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8859 var config = cm.config[i];
8861 var renderer = cm.getRenderer(i);
8865 if(typeof(renderer) !== 'undefined'){
8866 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8868 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8869 // and are rendered into the cells after the row is rendered - using the id for the element.
8871 if(typeof(value) === 'object'){
8881 rowIndex : rowIndex,
8886 this.fireEvent('rowclass', this, rowcfg);
8890 // this might end up displaying HTML?
8891 // this is too messy... - better to only do it on columsn you know are going to be too long
8892 //tooltip : (typeof(value) === 'object') ? '' : value,
8893 cls : rowcfg.rowClass + ' x-col-' + i,
8895 html: (typeof(value) === 'object') ? '' : value
8902 if(typeof(config.colspan) != 'undefined'){
8903 td.colspan = config.colspan;
8906 if(typeof(config.hidden) != 'undefined' && config.hidden){
8907 td.style += ' display:none;';
8910 if(typeof(config.align) != 'undefined' && config.align.length){
8911 td.style += ' text-align:' + config.align + ';';
8913 if(typeof(config.valign) != 'undefined' && config.valign.length){
8914 td.style += ' vertical-align:' + config.valign + ';';
8917 if(typeof(config.width) != 'undefined'){
8918 td.style += ' width:' + config.width + 'px;';
8921 if(typeof(config.cursor) != 'undefined'){
8922 td.style += ' cursor:' + config.cursor + ';';
8925 if(typeof(config.cls) != 'undefined'){
8926 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8929 ['xs','sm','md','lg'].map(function(size){
8931 if(typeof(config[size]) == 'undefined'){
8937 if (!config[size]) { // 0 = hidden
8938 // BS 4 '0' is treated as hide that column and below.
8939 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8943 td.cls += ' col-' + size + '-' + config[size] + (
8944 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8954 row.cellObjects = cellObjects;
8962 onBeforeLoad : function()
8971 this.el.select('tbody', true).first().dom.innerHTML = '';
8974 * Show or hide a row.
8975 * @param {Number} rowIndex to show or hide
8976 * @param {Boolean} state hide
8978 setRowVisibility : function(rowIndex, state)
8980 var bt = this.mainBody.dom;
8982 var rows = this.el.select('tbody > tr', true).elements;
8984 if(typeof(rows[rowIndex]) == 'undefined'){
8987 rows[rowIndex].dom.style.display = state ? '' : 'none';
8991 getSelectionModel : function(){
8993 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8995 return this.selModel;
8998 * Render the Roo.bootstrap object from renderder
9000 renderCellObject : function(r)
9004 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9006 var t = r.cfg.render(r.container);
9009 Roo.each(r.cfg.cn, function(c){
9011 container: t.getChildContainer(),
9014 _this.renderCellObject(child);
9019 getRowIndex : function(row)
9023 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9034 * Returns the grid's underlying element = used by panel.Grid
9035 * @return {Element} The element
9037 getGridEl : function(){
9041 * Forces a resize - used by panel.Grid
9042 * @return {Element} The element
9044 autoSize : function()
9046 //var ctr = Roo.get(this.container.dom.parentElement);
9047 var ctr = Roo.get(this.el.dom);
9049 var thd = this.getGridEl().select('thead',true).first();
9050 var tbd = this.getGridEl().select('tbody', true).first();
9051 var tfd = this.getGridEl().select('tfoot', true).first();
9053 var cw = ctr.getWidth();
9054 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9058 tbd.setWidth(ctr.getWidth());
9059 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9060 // this needs fixing for various usage - currently only hydra job advers I think..
9062 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9064 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9067 cw = Math.max(cw, this.totalWidth);
9068 this.getGridEl().select('tbody tr',true).setWidth(cw);
9070 // resize 'expandable coloumn?
9072 return; // we doe not have a view in this design..
9075 onBodyScroll: function()
9077 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9079 this.mainHead.setStyle({
9080 'position' : 'relative',
9081 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9087 var scrollHeight = this.mainBody.dom.scrollHeight;
9089 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9091 var height = this.mainBody.getHeight();
9093 if(scrollHeight - height == scrollTop) {
9095 var total = this.ds.getTotalCount();
9097 if(this.footer.cursor + this.footer.pageSize < total){
9099 this.footer.ds.load({
9101 start : this.footer.cursor + this.footer.pageSize,
9102 limit : this.footer.pageSize
9112 onHeaderChange : function()
9114 var header = this.renderHeader();
9115 var table = this.el.select('table', true).first();
9117 this.mainHead.remove();
9118 this.mainHead = table.createChild(header, this.mainBody, false);
9120 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9121 e.on('click', this.sort, this);
9127 onHiddenChange : function(colModel, colIndex, hidden)
9129 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9130 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9132 this.CSS.updateRule(thSelector, "display", "");
9133 this.CSS.updateRule(tdSelector, "display", "");
9136 this.CSS.updateRule(thSelector, "display", "none");
9137 this.CSS.updateRule(tdSelector, "display", "none");
9140 this.onHeaderChange();
9144 setColumnWidth: function(col_index, width)
9146 // width = "md-2 xs-2..."
9147 if(!this.colModel.config[col_index]) {
9151 var w = width.split(" ");
9153 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9155 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9158 for(var j = 0; j < w.length; j++) {
9164 var size_cls = w[j].split("-");
9166 if(!Number.isInteger(size_cls[1] * 1)) {
9170 if(!this.colModel.config[col_index][size_cls[0]]) {
9174 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9178 h_row[0].classList.replace(
9179 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9180 "col-"+size_cls[0]+"-"+size_cls[1]
9183 for(var i = 0; i < rows.length; i++) {
9185 var size_cls = w[j].split("-");
9187 if(!Number.isInteger(size_cls[1] * 1)) {
9191 if(!this.colModel.config[col_index][size_cls[0]]) {
9195 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9199 rows[i].classList.replace(
9200 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9201 "col-"+size_cls[0]+"-"+size_cls[1]
9205 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9220 * @class Roo.bootstrap.TableCell
9221 * @extends Roo.bootstrap.Component
9222 * Bootstrap TableCell class
9223 * @cfg {String} html cell contain text
9224 * @cfg {String} cls cell class
9225 * @cfg {String} tag cell tag (td|th) default td
9226 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9227 * @cfg {String} align Aligns the content in a cell
9228 * @cfg {String} axis Categorizes cells
9229 * @cfg {String} bgcolor Specifies the background color of a cell
9230 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9231 * @cfg {Number} colspan Specifies the number of columns a cell should span
9232 * @cfg {String} headers Specifies one or more header cells a cell is related to
9233 * @cfg {Number} height Sets the height of a cell
9234 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9235 * @cfg {Number} rowspan Sets the number of rows a cell should span
9236 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9237 * @cfg {String} valign Vertical aligns the content in a cell
9238 * @cfg {Number} width Specifies the width of a cell
9241 * Create a new TableCell
9242 * @param {Object} config The config object
9245 Roo.bootstrap.TableCell = function(config){
9246 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9249 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9269 getAutoCreate : function(){
9270 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9290 cfg.align=this.align
9296 cfg.bgcolor=this.bgcolor
9299 cfg.charoff=this.charoff
9302 cfg.colspan=this.colspan
9305 cfg.headers=this.headers
9308 cfg.height=this.height
9311 cfg.nowrap=this.nowrap
9314 cfg.rowspan=this.rowspan
9317 cfg.scope=this.scope
9320 cfg.valign=this.valign
9323 cfg.width=this.width
9342 * @class Roo.bootstrap.TableRow
9343 * @extends Roo.bootstrap.Component
9344 * Bootstrap TableRow class
9345 * @cfg {String} cls row class
9346 * @cfg {String} align Aligns the content in a table row
9347 * @cfg {String} bgcolor Specifies a background color for a table row
9348 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9349 * @cfg {String} valign Vertical aligns the content in a table row
9352 * Create a new TableRow
9353 * @param {Object} config The config object
9356 Roo.bootstrap.TableRow = function(config){
9357 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9360 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9368 getAutoCreate : function(){
9369 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9379 cfg.align = this.align;
9382 cfg.bgcolor = this.bgcolor;
9385 cfg.charoff = this.charoff;
9388 cfg.valign = this.valign;
9406 * @class Roo.bootstrap.TableBody
9407 * @extends Roo.bootstrap.Component
9408 * Bootstrap TableBody class
9409 * @cfg {String} cls element class
9410 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9411 * @cfg {String} align Aligns the content inside the element
9412 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9413 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9416 * Create a new TableBody
9417 * @param {Object} config The config object
9420 Roo.bootstrap.TableBody = function(config){
9421 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9424 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9432 getAutoCreate : function(){
9433 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9447 cfg.align = this.align;
9450 cfg.charoff = this.charoff;
9453 cfg.valign = this.valign;
9460 // initEvents : function()
9467 // this.store = Roo.factory(this.store, Roo.data);
9468 // this.store.on('load', this.onLoad, this);
9470 // this.store.load();
9474 // onLoad: function ()
9476 // this.fireEvent('load', this);
9486 * Ext JS Library 1.1.1
9487 * Copyright(c) 2006-2007, Ext JS, LLC.
9489 * Originally Released Under LGPL - original licence link has changed is not relivant.
9492 * <script type="text/javascript">
9495 // as we use this in bootstrap.
9496 Roo.namespace('Roo.form');
9498 * @class Roo.form.Action
9499 * Internal Class used to handle form actions
9501 * @param {Roo.form.BasicForm} el The form element or its id
9502 * @param {Object} config Configuration options
9507 // define the action interface
9508 Roo.form.Action = function(form, options){
9510 this.options = options || {};
9513 * Client Validation Failed
9516 Roo.form.Action.CLIENT_INVALID = 'client';
9518 * Server Validation Failed
9521 Roo.form.Action.SERVER_INVALID = 'server';
9523 * Connect to Server Failed
9526 Roo.form.Action.CONNECT_FAILURE = 'connect';
9528 * Reading Data from Server Failed
9531 Roo.form.Action.LOAD_FAILURE = 'load';
9533 Roo.form.Action.prototype = {
9535 failureType : undefined,
9536 response : undefined,
9540 run : function(options){
9545 success : function(response){
9550 handleResponse : function(response){
9554 // default connection failure
9555 failure : function(response){
9557 this.response = response;
9558 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9559 this.form.afterAction(this, false);
9562 processResponse : function(response){
9563 this.response = response;
9564 if(!response.responseText){
9567 this.result = this.handleResponse(response);
9571 // utility functions used internally
9572 getUrl : function(appendParams){
9573 var url = this.options.url || this.form.url || this.form.el.dom.action;
9575 var p = this.getParams();
9577 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9583 getMethod : function(){
9584 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9587 getParams : function(){
9588 var bp = this.form.baseParams;
9589 var p = this.options.params;
9591 if(typeof p == "object"){
9592 p = Roo.urlEncode(Roo.applyIf(p, bp));
9593 }else if(typeof p == 'string' && bp){
9594 p += '&' + Roo.urlEncode(bp);
9597 p = Roo.urlEncode(bp);
9602 createCallback : function(){
9604 success: this.success,
9605 failure: this.failure,
9607 timeout: (this.form.timeout*1000),
9608 upload: this.form.fileUpload ? this.success : undefined
9613 Roo.form.Action.Submit = function(form, options){
9614 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9617 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9620 haveProgress : false,
9621 uploadComplete : false,
9623 // uploadProgress indicator.
9624 uploadProgress : function()
9626 if (!this.form.progressUrl) {
9630 if (!this.haveProgress) {
9631 Roo.MessageBox.progress("Uploading", "Uploading");
9633 if (this.uploadComplete) {
9634 Roo.MessageBox.hide();
9638 this.haveProgress = true;
9640 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9642 var c = new Roo.data.Connection();
9644 url : this.form.progressUrl,
9649 success : function(req){
9650 //console.log(data);
9654 rdata = Roo.decode(req.responseText)
9656 Roo.log("Invalid data from server..");
9660 if (!rdata || !rdata.success) {
9662 Roo.MessageBox.alert(Roo.encode(rdata));
9665 var data = rdata.data;
9667 if (this.uploadComplete) {
9668 Roo.MessageBox.hide();
9673 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9674 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9677 this.uploadProgress.defer(2000,this);
9680 failure: function(data) {
9681 Roo.log('progress url failed ');
9692 // run get Values on the form, so it syncs any secondary forms.
9693 this.form.getValues();
9695 var o = this.options;
9696 var method = this.getMethod();
9697 var isPost = method == 'POST';
9698 if(o.clientValidation === false || this.form.isValid()){
9700 if (this.form.progressUrl) {
9701 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9702 (new Date() * 1) + '' + Math.random());
9707 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9708 form:this.form.el.dom,
9709 url:this.getUrl(!isPost),
9711 params:isPost ? this.getParams() : null,
9712 isUpload: this.form.fileUpload,
9713 formData : this.form.formData
9716 this.uploadProgress();
9718 }else if (o.clientValidation !== false){ // client validation failed
9719 this.failureType = Roo.form.Action.CLIENT_INVALID;
9720 this.form.afterAction(this, false);
9724 success : function(response)
9726 this.uploadComplete= true;
9727 if (this.haveProgress) {
9728 Roo.MessageBox.hide();
9732 var result = this.processResponse(response);
9733 if(result === true || result.success){
9734 this.form.afterAction(this, true);
9738 this.form.markInvalid(result.errors);
9739 this.failureType = Roo.form.Action.SERVER_INVALID;
9741 this.form.afterAction(this, false);
9743 failure : function(response)
9745 this.uploadComplete= true;
9746 if (this.haveProgress) {
9747 Roo.MessageBox.hide();
9750 this.response = response;
9751 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9752 this.form.afterAction(this, false);
9755 handleResponse : function(response){
9756 if(this.form.errorReader){
9757 var rs = this.form.errorReader.read(response);
9760 for(var i = 0, len = rs.records.length; i < len; i++) {
9761 var r = rs.records[i];
9765 if(errors.length < 1){
9769 success : rs.success,
9775 ret = Roo.decode(response.responseText);
9779 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9789 Roo.form.Action.Load = function(form, options){
9790 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9791 this.reader = this.form.reader;
9794 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9799 Roo.Ajax.request(Roo.apply(
9800 this.createCallback(), {
9801 method:this.getMethod(),
9802 url:this.getUrl(false),
9803 params:this.getParams()
9807 success : function(response){
9809 var result = this.processResponse(response);
9810 if(result === true || !result.success || !result.data){
9811 this.failureType = Roo.form.Action.LOAD_FAILURE;
9812 this.form.afterAction(this, false);
9815 this.form.clearInvalid();
9816 this.form.setValues(result.data);
9817 this.form.afterAction(this, true);
9820 handleResponse : function(response){
9821 if(this.form.reader){
9822 var rs = this.form.reader.read(response);
9823 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9825 success : rs.success,
9829 return Roo.decode(response.responseText);
9833 Roo.form.Action.ACTION_TYPES = {
9834 'load' : Roo.form.Action.Load,
9835 'submit' : Roo.form.Action.Submit
9844 * @class Roo.bootstrap.Form
9845 * @extends Roo.bootstrap.Component
9846 * Bootstrap Form class
9847 * @cfg {String} method GET | POST (default POST)
9848 * @cfg {String} labelAlign top | left (default top)
9849 * @cfg {String} align left | right - for navbars
9850 * @cfg {Boolean} loadMask load mask when submit (default true)
9855 * @param {Object} config The config object
9859 Roo.bootstrap.Form = function(config){
9861 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9863 Roo.bootstrap.Form.popover.apply();
9867 * @event clientvalidation
9868 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9869 * @param {Form} this
9870 * @param {Boolean} valid true if the form has passed client-side validation
9872 clientvalidation: true,
9874 * @event beforeaction
9875 * Fires before any action is performed. Return false to cancel the action.
9876 * @param {Form} this
9877 * @param {Action} action The action to be performed
9881 * @event actionfailed
9882 * Fires when an action fails.
9883 * @param {Form} this
9884 * @param {Action} action The action that failed
9886 actionfailed : true,
9888 * @event actioncomplete
9889 * Fires when an action is completed.
9890 * @param {Form} this
9891 * @param {Action} action The action that completed
9893 actioncomplete : true
9897 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9900 * @cfg {String} method
9901 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9906 * The URL to use for form actions if one isn't supplied in the action options.
9909 * @cfg {Boolean} fileUpload
9910 * Set to true if this form is a file upload.
9914 * @cfg {Object} baseParams
9915 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9919 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9923 * @cfg {Sting} align (left|right) for navbar forms
9928 activeAction : null,
9931 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9932 * element by passing it or its id or mask the form itself by passing in true.
9935 waitMsgTarget : false,
9940 * @cfg {Boolean} errorMask (true|false) default false
9945 * @cfg {Number} maskOffset Default 100
9950 * @cfg {Boolean} maskBody
9954 getAutoCreate : function(){
9958 method : this.method || 'POST',
9959 id : this.id || Roo.id(),
9962 if (this.parent().xtype.match(/^Nav/)) {
9963 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9967 if (this.labelAlign == 'left' ) {
9968 cfg.cls += ' form-horizontal';
9974 initEvents : function()
9976 this.el.on('submit', this.onSubmit, this);
9977 // this was added as random key presses on the form where triggering form submit.
9978 this.el.on('keypress', function(e) {
9979 if (e.getCharCode() != 13) {
9982 // we might need to allow it for textareas.. and some other items.
9983 // check e.getTarget().
9985 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9989 Roo.log("keypress blocked");
9997 onSubmit : function(e){
10002 * Returns true if client-side validation on the form is successful.
10005 isValid : function(){
10006 var items = this.getItems();
10008 var target = false;
10010 items.each(function(f){
10016 Roo.log('invalid field: ' + f.name);
10020 if(!target && f.el.isVisible(true)){
10026 if(this.errorMask && !valid){
10027 Roo.bootstrap.Form.popover.mask(this, target);
10034 * Returns true if any fields in this form have changed since their original load.
10037 isDirty : function(){
10039 var items = this.getItems();
10040 items.each(function(f){
10050 * Performs a predefined action (submit or load) or custom actions you define on this form.
10051 * @param {String} actionName The name of the action type
10052 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10053 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10054 * accept other config options):
10056 Property Type Description
10057 ---------------- --------------- ----------------------------------------------------------------------------------
10058 url String The url for the action (defaults to the form's url)
10059 method String The form method to use (defaults to the form's method, or POST if not defined)
10060 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10061 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10062 validate the form on the client (defaults to false)
10064 * @return {BasicForm} this
10066 doAction : function(action, options){
10067 if(typeof action == 'string'){
10068 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10070 if(this.fireEvent('beforeaction', this, action) !== false){
10071 this.beforeAction(action);
10072 action.run.defer(100, action);
10078 beforeAction : function(action){
10079 var o = action.options;
10084 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10086 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10089 // not really supported yet.. ??
10091 //if(this.waitMsgTarget === true){
10092 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10093 //}else if(this.waitMsgTarget){
10094 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10095 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10097 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10103 afterAction : function(action, success){
10104 this.activeAction = null;
10105 var o = action.options;
10110 Roo.get(document.body).unmask();
10116 //if(this.waitMsgTarget === true){
10117 // this.el.unmask();
10118 //}else if(this.waitMsgTarget){
10119 // this.waitMsgTarget.unmask();
10121 // Roo.MessageBox.updateProgress(1);
10122 // Roo.MessageBox.hide();
10129 Roo.callback(o.success, o.scope, [this, action]);
10130 this.fireEvent('actioncomplete', this, action);
10134 // failure condition..
10135 // we have a scenario where updates need confirming.
10136 // eg. if a locking scenario exists..
10137 // we look for { errors : { needs_confirm : true }} in the response.
10139 (typeof(action.result) != 'undefined') &&
10140 (typeof(action.result.errors) != 'undefined') &&
10141 (typeof(action.result.errors.needs_confirm) != 'undefined')
10144 Roo.log("not supported yet");
10147 Roo.MessageBox.confirm(
10148 "Change requires confirmation",
10149 action.result.errorMsg,
10154 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10164 Roo.callback(o.failure, o.scope, [this, action]);
10165 // show an error message if no failed handler is set..
10166 if (!this.hasListener('actionfailed')) {
10167 Roo.log("need to add dialog support");
10169 Roo.MessageBox.alert("Error",
10170 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10171 action.result.errorMsg :
10172 "Saving Failed, please check your entries or try again"
10177 this.fireEvent('actionfailed', this, action);
10182 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10183 * @param {String} id The value to search for
10186 findField : function(id){
10187 var items = this.getItems();
10188 var field = items.get(id);
10190 items.each(function(f){
10191 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10198 return field || null;
10201 * Mark fields in this form invalid in bulk.
10202 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10203 * @return {BasicForm} this
10205 markInvalid : function(errors){
10206 if(errors instanceof Array){
10207 for(var i = 0, len = errors.length; i < len; i++){
10208 var fieldError = errors[i];
10209 var f = this.findField(fieldError.id);
10211 f.markInvalid(fieldError.msg);
10217 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10218 field.markInvalid(errors[id]);
10222 //Roo.each(this.childForms || [], function (f) {
10223 // f.markInvalid(errors);
10230 * Set values for fields in this form in bulk.
10231 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10232 * @return {BasicForm} this
10234 setValues : function(values){
10235 if(values instanceof Array){ // array of objects
10236 for(var i = 0, len = values.length; i < len; i++){
10238 var f = this.findField(v.id);
10240 f.setValue(v.value);
10241 if(this.trackResetOnLoad){
10242 f.originalValue = f.getValue();
10246 }else{ // object hash
10249 if(typeof values[id] != 'function' && (field = this.findField(id))){
10251 if (field.setFromData &&
10252 field.valueField &&
10253 field.displayField &&
10254 // combos' with local stores can
10255 // be queried via setValue()
10256 // to set their value..
10257 (field.store && !field.store.isLocal)
10261 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10262 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10263 field.setFromData(sd);
10265 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10267 field.setFromData(values);
10270 field.setValue(values[id]);
10274 if(this.trackResetOnLoad){
10275 field.originalValue = field.getValue();
10281 //Roo.each(this.childForms || [], function (f) {
10282 // f.setValues(values);
10289 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10290 * they are returned as an array.
10291 * @param {Boolean} asString
10294 getValues : function(asString){
10295 //if (this.childForms) {
10296 // copy values from the child forms
10297 // Roo.each(this.childForms, function (f) {
10298 // this.setValues(f.getValues());
10304 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10305 if(asString === true){
10308 return Roo.urlDecode(fs);
10312 * Returns the fields in this form as an object with key/value pairs.
10313 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10316 getFieldValues : function(with_hidden)
10318 var items = this.getItems();
10320 items.each(function(f){
10322 if (!f.getName()) {
10326 var v = f.getValue();
10328 if (f.inputType =='radio') {
10329 if (typeof(ret[f.getName()]) == 'undefined') {
10330 ret[f.getName()] = ''; // empty..
10333 if (!f.el.dom.checked) {
10337 v = f.el.dom.value;
10341 if(f.xtype == 'MoneyField'){
10342 ret[f.currencyName] = f.getCurrency();
10345 // not sure if this supported any more..
10346 if ((typeof(v) == 'object') && f.getRawValue) {
10347 v = f.getRawValue() ; // dates..
10349 // combo boxes where name != hiddenName...
10350 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10351 ret[f.name] = f.getRawValue();
10353 ret[f.getName()] = v;
10360 * Clears all invalid messages in this form.
10361 * @return {BasicForm} this
10363 clearInvalid : function(){
10364 var items = this.getItems();
10366 items.each(function(f){
10374 * Resets this form.
10375 * @return {BasicForm} this
10377 reset : function(){
10378 var items = this.getItems();
10379 items.each(function(f){
10383 Roo.each(this.childForms || [], function (f) {
10391 getItems : function()
10393 var r=new Roo.util.MixedCollection(false, function(o){
10394 return o.id || (o.id = Roo.id());
10396 var iter = function(el) {
10403 Roo.each(el.items,function(e) {
10412 hideFields : function(items)
10414 Roo.each(items, function(i){
10416 var f = this.findField(i);
10427 showFields : function(items)
10429 Roo.each(items, function(i){
10431 var f = this.findField(i);
10444 Roo.apply(Roo.bootstrap.Form, {
10460 intervalID : false,
10466 if(this.isApplied){
10471 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10472 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10473 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10474 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10477 this.maskEl.top.enableDisplayMode("block");
10478 this.maskEl.left.enableDisplayMode("block");
10479 this.maskEl.bottom.enableDisplayMode("block");
10480 this.maskEl.right.enableDisplayMode("block");
10482 this.toolTip = new Roo.bootstrap.Tooltip({
10483 cls : 'roo-form-error-popover',
10485 'left' : ['r-l', [-2,0], 'right'],
10486 'right' : ['l-r', [2,0], 'left'],
10487 'bottom' : ['tl-bl', [0,2], 'top'],
10488 'top' : [ 'bl-tl', [0,-2], 'bottom']
10492 this.toolTip.render(Roo.get(document.body));
10494 this.toolTip.el.enableDisplayMode("block");
10496 Roo.get(document.body).on('click', function(){
10500 Roo.get(document.body).on('touchstart', function(){
10504 this.isApplied = true
10507 mask : function(form, target)
10511 this.target = target;
10513 if(!this.form.errorMask || !target.el){
10517 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10519 Roo.log(scrollable);
10521 var ot = this.target.el.calcOffsetsTo(scrollable);
10523 var scrollTo = ot[1] - this.form.maskOffset;
10525 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10527 scrollable.scrollTo('top', scrollTo);
10529 var box = this.target.el.getBox();
10531 var zIndex = Roo.bootstrap.Modal.zIndex++;
10534 this.maskEl.top.setStyle('position', 'absolute');
10535 this.maskEl.top.setStyle('z-index', zIndex);
10536 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10537 this.maskEl.top.setLeft(0);
10538 this.maskEl.top.setTop(0);
10539 this.maskEl.top.show();
10541 this.maskEl.left.setStyle('position', 'absolute');
10542 this.maskEl.left.setStyle('z-index', zIndex);
10543 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10544 this.maskEl.left.setLeft(0);
10545 this.maskEl.left.setTop(box.y - this.padding);
10546 this.maskEl.left.show();
10548 this.maskEl.bottom.setStyle('position', 'absolute');
10549 this.maskEl.bottom.setStyle('z-index', zIndex);
10550 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10551 this.maskEl.bottom.setLeft(0);
10552 this.maskEl.bottom.setTop(box.bottom + this.padding);
10553 this.maskEl.bottom.show();
10555 this.maskEl.right.setStyle('position', 'absolute');
10556 this.maskEl.right.setStyle('z-index', zIndex);
10557 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10558 this.maskEl.right.setLeft(box.right + this.padding);
10559 this.maskEl.right.setTop(box.y - this.padding);
10560 this.maskEl.right.show();
10562 this.toolTip.bindEl = this.target.el;
10564 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10566 var tip = this.target.blankText;
10568 if(this.target.getValue() !== '' ) {
10570 if (this.target.invalidText.length) {
10571 tip = this.target.invalidText;
10572 } else if (this.target.regexText.length){
10573 tip = this.target.regexText;
10577 this.toolTip.show(tip);
10579 this.intervalID = window.setInterval(function() {
10580 Roo.bootstrap.Form.popover.unmask();
10583 window.onwheel = function(){ return false;};
10585 (function(){ this.isMasked = true; }).defer(500, this);
10589 unmask : function()
10591 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10595 this.maskEl.top.setStyle('position', 'absolute');
10596 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10597 this.maskEl.top.hide();
10599 this.maskEl.left.setStyle('position', 'absolute');
10600 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10601 this.maskEl.left.hide();
10603 this.maskEl.bottom.setStyle('position', 'absolute');
10604 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10605 this.maskEl.bottom.hide();
10607 this.maskEl.right.setStyle('position', 'absolute');
10608 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10609 this.maskEl.right.hide();
10611 this.toolTip.hide();
10613 this.toolTip.el.hide();
10615 window.onwheel = function(){ return true;};
10617 if(this.intervalID){
10618 window.clearInterval(this.intervalID);
10619 this.intervalID = false;
10622 this.isMasked = false;
10632 * Ext JS Library 1.1.1
10633 * Copyright(c) 2006-2007, Ext JS, LLC.
10635 * Originally Released Under LGPL - original licence link has changed is not relivant.
10638 * <script type="text/javascript">
10641 * @class Roo.form.VTypes
10642 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10645 Roo.form.VTypes = function(){
10646 // closure these in so they are only created once.
10647 var alpha = /^[a-zA-Z_]+$/;
10648 var alphanum = /^[a-zA-Z0-9_]+$/;
10649 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10650 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10652 // All these messages and functions are configurable
10655 * The function used to validate email addresses
10656 * @param {String} value The email address
10658 'email' : function(v){
10659 return email.test(v);
10662 * The error text to display when the email validation function returns false
10665 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10667 * The keystroke filter mask to be applied on email input
10670 'emailMask' : /[a-z0-9_\.\-@]/i,
10673 * The function used to validate URLs
10674 * @param {String} value The URL
10676 'url' : function(v){
10677 return url.test(v);
10680 * The error text to display when the url validation function returns false
10683 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10686 * The function used to validate alpha values
10687 * @param {String} value The value
10689 'alpha' : function(v){
10690 return alpha.test(v);
10693 * The error text to display when the alpha validation function returns false
10696 'alphaText' : 'This field should only contain letters and _',
10698 * The keystroke filter mask to be applied on alpha input
10701 'alphaMask' : /[a-z_]/i,
10704 * The function used to validate alphanumeric values
10705 * @param {String} value The value
10707 'alphanum' : function(v){
10708 return alphanum.test(v);
10711 * The error text to display when the alphanumeric validation function returns false
10714 'alphanumText' : 'This field should only contain letters, numbers and _',
10716 * The keystroke filter mask to be applied on alphanumeric input
10719 'alphanumMask' : /[a-z0-9_]/i
10729 * @class Roo.bootstrap.Input
10730 * @extends Roo.bootstrap.Component
10731 * Bootstrap Input class
10732 * @cfg {Boolean} disabled is it disabled
10733 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10734 * @cfg {String} name name of the input
10735 * @cfg {string} fieldLabel - the label associated
10736 * @cfg {string} placeholder - placeholder to put in text.
10737 * @cfg {string} before - input group add on before
10738 * @cfg {string} after - input group add on after
10739 * @cfg {string} size - (lg|sm) or leave empty..
10740 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10741 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10742 * @cfg {Number} md colspan out of 12 for computer-sized screens
10743 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10744 * @cfg {string} value default value of the input
10745 * @cfg {Number} labelWidth set the width of label
10746 * @cfg {Number} labellg set the width of label (1-12)
10747 * @cfg {Number} labelmd set the width of label (1-12)
10748 * @cfg {Number} labelsm set the width of label (1-12)
10749 * @cfg {Number} labelxs set the width of label (1-12)
10750 * @cfg {String} labelAlign (top|left)
10751 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10752 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10753 * @cfg {String} indicatorpos (left|right) default left
10754 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10755 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10756 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10758 * @cfg {String} align (left|center|right) Default left
10759 * @cfg {Boolean} forceFeedback (true|false) Default false
10762 * Create a new Input
10763 * @param {Object} config The config object
10766 Roo.bootstrap.Input = function(config){
10768 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10773 * Fires when this field receives input focus.
10774 * @param {Roo.form.Field} this
10779 * Fires when this field loses input focus.
10780 * @param {Roo.form.Field} this
10784 * @event specialkey
10785 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10786 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10787 * @param {Roo.form.Field} this
10788 * @param {Roo.EventObject} e The event object
10793 * Fires just before the field blurs if the field value has changed.
10794 * @param {Roo.form.Field} this
10795 * @param {Mixed} newValue The new value
10796 * @param {Mixed} oldValue The original value
10801 * Fires after the field has been marked as invalid.
10802 * @param {Roo.form.Field} this
10803 * @param {String} msg The validation message
10808 * Fires after the field has been validated with no errors.
10809 * @param {Roo.form.Field} this
10814 * Fires after the key up
10815 * @param {Roo.form.Field} this
10816 * @param {Roo.EventObject} e The event Object
10821 * Fires after the user pastes into input
10822 * @param {Roo.form.Field} this
10823 * @param {Roo.EventObject} e The event Object
10829 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10831 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10832 automatic validation (defaults to "keyup").
10834 validationEvent : "keyup",
10836 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10838 validateOnBlur : true,
10840 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10842 validationDelay : 250,
10844 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10846 focusClass : "x-form-focus", // not needed???
10850 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10852 invalidClass : "has-warning",
10855 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10857 validClass : "has-success",
10860 * @cfg {Boolean} hasFeedback (true|false) default true
10862 hasFeedback : true,
10865 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10867 invalidFeedbackClass : "glyphicon-warning-sign",
10870 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10872 validFeedbackClass : "glyphicon-ok",
10875 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10877 selectOnFocus : false,
10880 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10884 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10889 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10891 disableKeyFilter : false,
10894 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10898 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10902 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10904 blankText : "Please complete this mandatory field",
10907 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10911 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10913 maxLength : Number.MAX_VALUE,
10915 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10917 minLengthText : "The minimum length for this field is {0}",
10919 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10921 maxLengthText : "The maximum length for this field is {0}",
10925 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10926 * If available, this function will be called only after the basic validators all return true, and will be passed the
10927 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10931 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10932 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10933 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10937 * @cfg {String} regexText -- Depricated - use Invalid Text
10942 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10948 autocomplete: false,
10952 inputType : 'text',
10955 placeholder: false,
10960 preventMark: false,
10961 isFormField : true,
10964 labelAlign : false,
10967 formatedValue : false,
10968 forceFeedback : false,
10970 indicatorpos : 'left',
10980 parentLabelAlign : function()
10983 while (parent.parent()) {
10984 parent = parent.parent();
10985 if (typeof(parent.labelAlign) !='undefined') {
10986 return parent.labelAlign;
10993 getAutoCreate : function()
10995 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11001 if(this.inputType != 'hidden'){
11002 cfg.cls = 'form-group' //input-group
11008 type : this.inputType,
11009 value : this.value,
11010 cls : 'form-control',
11011 placeholder : this.placeholder || '',
11012 autocomplete : this.autocomplete || 'new-password'
11014 if (this.inputType == 'file') {
11015 input.style = 'overflow:hidden'; // why not in CSS?
11018 if(this.capture.length){
11019 input.capture = this.capture;
11022 if(this.accept.length){
11023 input.accept = this.accept + "/*";
11027 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11030 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11031 input.maxLength = this.maxLength;
11034 if (this.disabled) {
11035 input.disabled=true;
11038 if (this.readOnly) {
11039 input.readonly=true;
11043 input.name = this.name;
11047 input.cls += ' input-' + this.size;
11051 ['xs','sm','md','lg'].map(function(size){
11052 if (settings[size]) {
11053 cfg.cls += ' col-' + size + '-' + settings[size];
11057 var inputblock = input;
11061 cls: 'glyphicon form-control-feedback'
11064 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11067 cls : 'has-feedback',
11075 if (this.before || this.after) {
11078 cls : 'input-group',
11082 if (this.before && typeof(this.before) == 'string') {
11084 inputblock.cn.push({
11086 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11090 if (this.before && typeof(this.before) == 'object') {
11091 this.before = Roo.factory(this.before);
11093 inputblock.cn.push({
11095 cls : 'roo-input-before input-group-prepend input-group-' +
11096 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11100 inputblock.cn.push(input);
11102 if (this.after && typeof(this.after) == 'string') {
11103 inputblock.cn.push({
11105 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11109 if (this.after && typeof(this.after) == 'object') {
11110 this.after = Roo.factory(this.after);
11112 inputblock.cn.push({
11114 cls : 'roo-input-after input-group-append input-group-' +
11115 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11119 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11120 inputblock.cls += ' has-feedback';
11121 inputblock.cn.push(feedback);
11126 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11127 tooltip : 'This field is required'
11129 if (this.allowBlank ) {
11130 indicator.style = this.allowBlank ? ' display:none' : '';
11132 if (align ==='left' && this.fieldLabel.length) {
11134 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11141 cls : 'control-label col-form-label',
11142 html : this.fieldLabel
11153 var labelCfg = cfg.cn[1];
11154 var contentCfg = cfg.cn[2];
11156 if(this.indicatorpos == 'right'){
11161 cls : 'control-label col-form-label',
11165 html : this.fieldLabel
11179 labelCfg = cfg.cn[0];
11180 contentCfg = cfg.cn[1];
11184 if(this.labelWidth > 12){
11185 labelCfg.style = "width: " + this.labelWidth + 'px';
11188 if(this.labelWidth < 13 && this.labelmd == 0){
11189 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11192 if(this.labellg > 0){
11193 labelCfg.cls += ' col-lg-' + this.labellg;
11194 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11197 if(this.labelmd > 0){
11198 labelCfg.cls += ' col-md-' + this.labelmd;
11199 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11202 if(this.labelsm > 0){
11203 labelCfg.cls += ' col-sm-' + this.labelsm;
11204 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11207 if(this.labelxs > 0){
11208 labelCfg.cls += ' col-xs-' + this.labelxs;
11209 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11213 } else if ( this.fieldLabel.length) {
11220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11221 tooltip : 'This field is required',
11222 style : this.allowBlank ? ' display:none' : ''
11226 //cls : 'input-group-addon',
11227 html : this.fieldLabel
11235 if(this.indicatorpos == 'right'){
11240 //cls : 'input-group-addon',
11241 html : this.fieldLabel
11246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11247 tooltip : 'This field is required',
11248 style : this.allowBlank ? ' display:none' : ''
11268 if (this.parentType === 'Navbar' && this.parent().bar) {
11269 cfg.cls += ' navbar-form';
11272 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11273 // on BS4 we do this only if not form
11274 cfg.cls += ' navbar-form';
11282 * return the real input element.
11284 inputEl: function ()
11286 return this.el.select('input.form-control',true).first();
11289 tooltipEl : function()
11291 return this.inputEl();
11294 indicatorEl : function()
11296 if (Roo.bootstrap.version == 4) {
11297 return false; // not enabled in v4 yet.
11300 var indicator = this.el.select('i.roo-required-indicator',true).first();
11310 setDisabled : function(v)
11312 var i = this.inputEl().dom;
11314 i.removeAttribute('disabled');
11318 i.setAttribute('disabled','true');
11320 initEvents : function()
11323 this.inputEl().on("keydown" , this.fireKey, this);
11324 this.inputEl().on("focus", this.onFocus, this);
11325 this.inputEl().on("blur", this.onBlur, this);
11327 this.inputEl().relayEvent('keyup', this);
11328 this.inputEl().relayEvent('paste', this);
11330 this.indicator = this.indicatorEl();
11332 if(this.indicator){
11333 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11336 // reference to original value for reset
11337 this.originalValue = this.getValue();
11338 //Roo.form.TextField.superclass.initEvents.call(this);
11339 if(this.validationEvent == 'keyup'){
11340 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11341 this.inputEl().on('keyup', this.filterValidation, this);
11343 else if(this.validationEvent !== false){
11344 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11347 if(this.selectOnFocus){
11348 this.on("focus", this.preFocus, this);
11351 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11352 this.inputEl().on("keypress", this.filterKeys, this);
11354 this.inputEl().relayEvent('keypress', this);
11357 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11358 this.el.on("click", this.autoSize, this);
11361 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11362 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11365 if (typeof(this.before) == 'object') {
11366 this.before.render(this.el.select('.roo-input-before',true).first());
11368 if (typeof(this.after) == 'object') {
11369 this.after.render(this.el.select('.roo-input-after',true).first());
11372 this.inputEl().on('change', this.onChange, this);
11375 filterValidation : function(e){
11376 if(!e.isNavKeyPress()){
11377 this.validationTask.delay(this.validationDelay);
11381 * Validates the field value
11382 * @return {Boolean} True if the value is valid, else false
11384 validate : function(){
11385 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11386 if(this.disabled || this.validateValue(this.getRawValue())){
11391 this.markInvalid();
11397 * Validates a value according to the field's validation rules and marks the field as invalid
11398 * if the validation fails
11399 * @param {Mixed} value The value to validate
11400 * @return {Boolean} True if the value is valid, else false
11402 validateValue : function(value)
11404 if(this.getVisibilityEl().hasClass('hidden')){
11408 if(value.length < 1) { // if it's blank
11409 if(this.allowBlank){
11415 if(value.length < this.minLength){
11418 if(value.length > this.maxLength){
11422 var vt = Roo.form.VTypes;
11423 if(!vt[this.vtype](value, this)){
11427 if(typeof this.validator == "function"){
11428 var msg = this.validator(value);
11432 if (typeof(msg) == 'string') {
11433 this.invalidText = msg;
11437 if(this.regex && !this.regex.test(value)){
11445 fireKey : function(e){
11446 //Roo.log('field ' + e.getKey());
11447 if(e.isNavKeyPress()){
11448 this.fireEvent("specialkey", this, e);
11451 focus : function (selectText){
11453 this.inputEl().focus();
11454 if(selectText === true){
11455 this.inputEl().dom.select();
11461 onFocus : function(){
11462 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11463 // this.el.addClass(this.focusClass);
11465 if(!this.hasFocus){
11466 this.hasFocus = true;
11467 this.startValue = this.getValue();
11468 this.fireEvent("focus", this);
11472 beforeBlur : Roo.emptyFn,
11476 onBlur : function(){
11478 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11479 //this.el.removeClass(this.focusClass);
11481 this.hasFocus = false;
11482 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11485 var v = this.getValue();
11486 if(String(v) !== String(this.startValue)){
11487 this.fireEvent('change', this, v, this.startValue);
11489 this.fireEvent("blur", this);
11492 onChange : function(e)
11494 var v = this.getValue();
11495 if(String(v) !== String(this.startValue)){
11496 this.fireEvent('change', this, v, this.startValue);
11502 * Resets the current field value to the originally loaded value and clears any validation messages
11504 reset : function(){
11505 this.setValue(this.originalValue);
11509 * Returns the name of the field
11510 * @return {Mixed} name The name field
11512 getName: function(){
11516 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11517 * @return {Mixed} value The field value
11519 getValue : function(){
11521 var v = this.inputEl().getValue();
11526 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11527 * @return {Mixed} value The field value
11529 getRawValue : function(){
11530 var v = this.inputEl().getValue();
11536 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11537 * @param {Mixed} value The value to set
11539 setRawValue : function(v){
11540 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11543 selectText : function(start, end){
11544 var v = this.getRawValue();
11546 start = start === undefined ? 0 : start;
11547 end = end === undefined ? v.length : end;
11548 var d = this.inputEl().dom;
11549 if(d.setSelectionRange){
11550 d.setSelectionRange(start, end);
11551 }else if(d.createTextRange){
11552 var range = d.createTextRange();
11553 range.moveStart("character", start);
11554 range.moveEnd("character", v.length-end);
11561 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11562 * @param {Mixed} value The value to set
11564 setValue : function(v){
11567 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11573 processValue : function(value){
11574 if(this.stripCharsRe){
11575 var newValue = value.replace(this.stripCharsRe, '');
11576 if(newValue !== value){
11577 this.setRawValue(newValue);
11584 preFocus : function(){
11586 if(this.selectOnFocus){
11587 this.inputEl().dom.select();
11590 filterKeys : function(e){
11591 var k = e.getKey();
11592 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11595 var c = e.getCharCode(), cc = String.fromCharCode(c);
11596 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11599 if(!this.maskRe.test(cc)){
11604 * Clear any invalid styles/messages for this field
11606 clearInvalid : function(){
11608 if(!this.el || this.preventMark){ // not rendered
11613 this.el.removeClass([this.invalidClass, 'is-invalid']);
11615 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11617 var feedback = this.el.select('.form-control-feedback', true).first();
11620 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11625 if(this.indicator){
11626 this.indicator.removeClass('visible');
11627 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11630 this.fireEvent('valid', this);
11634 * Mark this field as valid
11636 markValid : function()
11638 if(!this.el || this.preventMark){ // not rendered...
11642 this.el.removeClass([this.invalidClass, this.validClass]);
11643 this.inputEl().removeClass(['is-valid', 'is-invalid']);
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]);
11651 if(this.indicator){
11652 this.indicator.removeClass('visible');
11653 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11661 if(this.allowBlank && !this.getRawValue().length){
11664 if (Roo.bootstrap.version == 3) {
11665 this.el.addClass(this.validClass);
11667 this.inputEl().addClass('is-valid');
11670 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11672 var feedback = this.el.select('.form-control-feedback', true).first();
11675 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11676 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11681 this.fireEvent('valid', this);
11685 * Mark this field as invalid
11686 * @param {String} msg The validation message
11688 markInvalid : function(msg)
11690 if(!this.el || this.preventMark){ // not rendered
11694 this.el.removeClass([this.invalidClass, this.validClass]);
11695 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11697 var feedback = this.el.select('.form-control-feedback', true).first();
11700 this.el.select('.form-control-feedback', true).first().removeClass(
11701 [this.invalidFeedbackClass, this.validFeedbackClass]);
11708 if(this.allowBlank && !this.getRawValue().length){
11712 if(this.indicator){
11713 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11714 this.indicator.addClass('visible');
11716 if (Roo.bootstrap.version == 3) {
11717 this.el.addClass(this.invalidClass);
11719 this.inputEl().addClass('is-invalid');
11724 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11726 var feedback = this.el.select('.form-control-feedback', true).first();
11729 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11731 if(this.getValue().length || this.forceFeedback){
11732 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11739 this.fireEvent('invalid', this, msg);
11742 SafariOnKeyDown : function(event)
11744 // this is a workaround for a password hang bug on chrome/ webkit.
11745 if (this.inputEl().dom.type != 'password') {
11749 var isSelectAll = false;
11751 if(this.inputEl().dom.selectionEnd > 0){
11752 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11754 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11755 event.preventDefault();
11760 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11762 event.preventDefault();
11763 // this is very hacky as keydown always get's upper case.
11765 var cc = String.fromCharCode(event.getCharCode());
11766 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11770 adjustWidth : function(tag, w){
11771 tag = tag.toLowerCase();
11772 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11773 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11774 if(tag == 'input'){
11777 if(tag == 'textarea'){
11780 }else if(Roo.isOpera){
11781 if(tag == 'input'){
11784 if(tag == 'textarea'){
11792 setFieldLabel : function(v)
11794 if(!this.rendered){
11798 if(this.indicatorEl()){
11799 var ar = this.el.select('label > span',true);
11801 if (ar.elements.length) {
11802 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11803 this.fieldLabel = v;
11807 var br = this.el.select('label',true);
11809 if(br.elements.length) {
11810 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11811 this.fieldLabel = v;
11815 Roo.log('Cannot Found any of label > span || label in input');
11819 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11820 this.fieldLabel = v;
11835 * @class Roo.bootstrap.TextArea
11836 * @extends Roo.bootstrap.Input
11837 * Bootstrap TextArea class
11838 * @cfg {Number} cols Specifies the visible width of a text area
11839 * @cfg {Number} rows Specifies the visible number of lines in a text area
11840 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11841 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11842 * @cfg {string} html text
11845 * Create a new TextArea
11846 * @param {Object} config The config object
11849 Roo.bootstrap.TextArea = function(config){
11850 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11854 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11864 getAutoCreate : function(){
11866 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11872 if(this.inputType != 'hidden'){
11873 cfg.cls = 'form-group' //input-group
11881 value : this.value || '',
11882 html: this.html || '',
11883 cls : 'form-control',
11884 placeholder : this.placeholder || ''
11888 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11889 input.maxLength = this.maxLength;
11893 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11897 input.cols = this.cols;
11900 if (this.readOnly) {
11901 input.readonly = true;
11905 input.name = this.name;
11909 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11913 ['xs','sm','md','lg'].map(function(size){
11914 if (settings[size]) {
11915 cfg.cls += ' col-' + size + '-' + settings[size];
11919 var inputblock = input;
11921 if(this.hasFeedback && !this.allowBlank){
11925 cls: 'glyphicon form-control-feedback'
11929 cls : 'has-feedback',
11938 if (this.before || this.after) {
11941 cls : 'input-group',
11945 inputblock.cn.push({
11947 cls : 'input-group-addon',
11952 inputblock.cn.push(input);
11954 if(this.hasFeedback && !this.allowBlank){
11955 inputblock.cls += ' has-feedback';
11956 inputblock.cn.push(feedback);
11960 inputblock.cn.push({
11962 cls : 'input-group-addon',
11969 if (align ==='left' && this.fieldLabel.length) {
11974 cls : 'control-label',
11975 html : this.fieldLabel
11986 if(this.labelWidth > 12){
11987 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11990 if(this.labelWidth < 13 && this.labelmd == 0){
11991 this.labelmd = this.labelWidth;
11994 if(this.labellg > 0){
11995 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11996 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11999 if(this.labelmd > 0){
12000 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12001 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12004 if(this.labelsm > 0){
12005 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12006 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12009 if(this.labelxs > 0){
12010 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12011 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12014 } else if ( this.fieldLabel.length) {
12019 //cls : 'input-group-addon',
12020 html : this.fieldLabel
12038 if (this.disabled) {
12039 input.disabled=true;
12046 * return the real textarea element.
12048 inputEl: function ()
12050 return this.el.select('textarea.form-control',true).first();
12054 * Clear any invalid styles/messages for this field
12056 clearInvalid : function()
12059 if(!this.el || this.preventMark){ // not rendered
12063 var label = this.el.select('label', true).first();
12064 var icon = this.el.select('i.fa-star', true).first();
12069 this.el.removeClass( this.validClass);
12070 this.inputEl().removeClass('is-invalid');
12072 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12074 var feedback = this.el.select('.form-control-feedback', true).first();
12077 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12082 this.fireEvent('valid', this);
12086 * Mark this field as valid
12088 markValid : function()
12090 if(!this.el || this.preventMark){ // not rendered
12094 this.el.removeClass([this.invalidClass, this.validClass]);
12095 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12097 var feedback = this.el.select('.form-control-feedback', true).first();
12100 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12103 if(this.disabled || this.allowBlank){
12107 var label = this.el.select('label', true).first();
12108 var icon = this.el.select('i.fa-star', true).first();
12113 if (Roo.bootstrap.version == 3) {
12114 this.el.addClass(this.validClass);
12116 this.inputEl().addClass('is-valid');
12120 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12122 var feedback = this.el.select('.form-control-feedback', true).first();
12125 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12126 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12131 this.fireEvent('valid', this);
12135 * Mark this field as invalid
12136 * @param {String} msg The validation message
12138 markInvalid : function(msg)
12140 if(!this.el || this.preventMark){ // not rendered
12144 this.el.removeClass([this.invalidClass, this.validClass]);
12145 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12147 var feedback = this.el.select('.form-control-feedback', true).first();
12150 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12153 if(this.disabled || this.allowBlank){
12157 var label = this.el.select('label', true).first();
12158 var icon = this.el.select('i.fa-star', true).first();
12160 if(!this.getValue().length && label && !icon){
12161 this.el.createChild({
12163 cls : 'text-danger fa fa-lg fa-star',
12164 tooltip : 'This field is required',
12165 style : 'margin-right:5px;'
12169 if (Roo.bootstrap.version == 3) {
12170 this.el.addClass(this.invalidClass);
12172 this.inputEl().addClass('is-invalid');
12175 // fixme ... this may be depricated need to test..
12176 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178 var feedback = this.el.select('.form-control-feedback', true).first();
12181 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12183 if(this.getValue().length || this.forceFeedback){
12184 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12191 this.fireEvent('invalid', this, msg);
12199 * trigger field - base class for combo..
12204 * @class Roo.bootstrap.TriggerField
12205 * @extends Roo.bootstrap.Input
12206 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12207 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12208 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12209 * for which you can provide a custom implementation. For example:
12211 var trigger = new Roo.bootstrap.TriggerField();
12212 trigger.onTriggerClick = myTriggerFn;
12213 trigger.applyTo('my-field');
12216 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12217 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12218 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12219 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12220 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12223 * Create a new TriggerField.
12224 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12225 * to the base TextField)
12227 Roo.bootstrap.TriggerField = function(config){
12228 this.mimicing = false;
12229 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12232 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12234 * @cfg {String} triggerClass A CSS class to apply to the trigger
12237 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12242 * @cfg {Boolean} removable (true|false) special filter default false
12246 /** @cfg {Boolean} grow @hide */
12247 /** @cfg {Number} growMin @hide */
12248 /** @cfg {Number} growMax @hide */
12254 autoSize: Roo.emptyFn,
12258 deferHeight : true,
12261 actionMode : 'wrap',
12266 getAutoCreate : function(){
12268 var align = this.labelAlign || this.parentLabelAlign();
12273 cls: 'form-group' //input-group
12280 type : this.inputType,
12281 cls : 'form-control',
12282 autocomplete: 'new-password',
12283 placeholder : this.placeholder || ''
12287 input.name = this.name;
12290 input.cls += ' input-' + this.size;
12293 if (this.disabled) {
12294 input.disabled=true;
12297 var inputblock = input;
12299 if(this.hasFeedback && !this.allowBlank){
12303 cls: 'glyphicon form-control-feedback'
12306 if(this.removable && !this.editable ){
12308 cls : 'has-feedback',
12314 cls : 'roo-combo-removable-btn close'
12321 cls : 'has-feedback',
12330 if(this.removable && !this.editable ){
12332 cls : 'roo-removable',
12338 cls : 'roo-combo-removable-btn close'
12345 if (this.before || this.after) {
12348 cls : 'input-group',
12352 inputblock.cn.push({
12354 cls : 'input-group-addon input-group-prepend input-group-text',
12359 inputblock.cn.push(input);
12361 if(this.hasFeedback && !this.allowBlank){
12362 inputblock.cls += ' has-feedback';
12363 inputblock.cn.push(feedback);
12367 inputblock.cn.push({
12369 cls : 'input-group-addon input-group-append input-group-text',
12378 var ibwrap = inputblock;
12383 cls: 'roo-select2-choices',
12387 cls: 'roo-select2-search-field',
12399 cls: 'roo-select2-container input-group',
12404 cls: 'form-hidden-field'
12410 if(!this.multiple && this.showToggleBtn){
12416 if (this.caret != false) {
12419 cls: 'fa fa-' + this.caret
12426 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12428 Roo.bootstrap.version == 3 ? caret : '',
12431 cls: 'combobox-clear',
12445 combobox.cls += ' roo-select2-container-multi';
12449 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12450 tooltip : 'This field is required'
12452 if (Roo.bootstrap.version == 4) {
12455 style : 'display:none'
12460 if (align ==='left' && this.fieldLabel.length) {
12462 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12469 cls : 'control-label',
12470 html : this.fieldLabel
12482 var labelCfg = cfg.cn[1];
12483 var contentCfg = cfg.cn[2];
12485 if(this.indicatorpos == 'right'){
12490 cls : 'control-label',
12494 html : this.fieldLabel
12508 labelCfg = cfg.cn[0];
12509 contentCfg = cfg.cn[1];
12512 if(this.labelWidth > 12){
12513 labelCfg.style = "width: " + this.labelWidth + 'px';
12516 if(this.labelWidth < 13 && this.labelmd == 0){
12517 this.labelmd = this.labelWidth;
12520 if(this.labellg > 0){
12521 labelCfg.cls += ' col-lg-' + this.labellg;
12522 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12525 if(this.labelmd > 0){
12526 labelCfg.cls += ' col-md-' + this.labelmd;
12527 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12530 if(this.labelsm > 0){
12531 labelCfg.cls += ' col-sm-' + this.labelsm;
12532 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12535 if(this.labelxs > 0){
12536 labelCfg.cls += ' col-xs-' + this.labelxs;
12537 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12540 } else if ( this.fieldLabel.length) {
12541 // Roo.log(" label");
12546 //cls : 'input-group-addon',
12547 html : this.fieldLabel
12555 if(this.indicatorpos == 'right'){
12563 html : this.fieldLabel
12577 // Roo.log(" no label && no align");
12584 ['xs','sm','md','lg'].map(function(size){
12585 if (settings[size]) {
12586 cfg.cls += ' col-' + size + '-' + settings[size];
12597 onResize : function(w, h){
12598 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12599 // if(typeof w == 'number'){
12600 // var x = w - this.trigger.getWidth();
12601 // this.inputEl().setWidth(this.adjustWidth('input', x));
12602 // this.trigger.setStyle('left', x+'px');
12607 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12610 getResizeEl : function(){
12611 return this.inputEl();
12615 getPositionEl : function(){
12616 return this.inputEl();
12620 alignErrorIcon : function(){
12621 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12625 initEvents : function(){
12629 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12630 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12631 if(!this.multiple && this.showToggleBtn){
12632 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12633 if(this.hideTrigger){
12634 this.trigger.setDisplayed(false);
12636 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12640 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12643 if(this.removable && !this.editable && !this.tickable){
12644 var close = this.closeTriggerEl();
12647 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12648 close.on('click', this.removeBtnClick, this, close);
12652 //this.trigger.addClassOnOver('x-form-trigger-over');
12653 //this.trigger.addClassOnClick('x-form-trigger-click');
12656 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12660 closeTriggerEl : function()
12662 var close = this.el.select('.roo-combo-removable-btn', true).first();
12663 return close ? close : false;
12666 removeBtnClick : function(e, h, el)
12668 e.preventDefault();
12670 if(this.fireEvent("remove", this) !== false){
12672 this.fireEvent("afterremove", this)
12676 createList : function()
12678 this.list = Roo.get(document.body).createChild({
12679 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12680 cls: 'typeahead typeahead-long dropdown-menu shadow',
12681 style: 'display:none'
12684 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12689 initTrigger : function(){
12694 onDestroy : function(){
12696 this.trigger.removeAllListeners();
12697 // this.trigger.remove();
12700 // this.wrap.remove();
12702 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12706 onFocus : function(){
12707 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12709 if(!this.mimicing){
12710 this.wrap.addClass('x-trigger-wrap-focus');
12711 this.mimicing = true;
12712 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12713 if(this.monitorTab){
12714 this.el.on("keydown", this.checkTab, this);
12721 checkTab : function(e){
12722 if(e.getKey() == e.TAB){
12723 this.triggerBlur();
12728 onBlur : function(){
12733 mimicBlur : function(e, t){
12735 if(!this.wrap.contains(t) && this.validateBlur()){
12736 this.triggerBlur();
12742 triggerBlur : function(){
12743 this.mimicing = false;
12744 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12745 if(this.monitorTab){
12746 this.el.un("keydown", this.checkTab, this);
12748 //this.wrap.removeClass('x-trigger-wrap-focus');
12749 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12753 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12754 validateBlur : function(e, t){
12759 onDisable : function(){
12760 this.inputEl().dom.disabled = true;
12761 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12763 // this.wrap.addClass('x-item-disabled');
12768 onEnable : function(){
12769 this.inputEl().dom.disabled = false;
12770 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12772 // this.el.removeClass('x-item-disabled');
12777 onShow : function(){
12778 var ae = this.getActionEl();
12781 ae.dom.style.display = '';
12782 ae.dom.style.visibility = 'visible';
12788 onHide : function(){
12789 var ae = this.getActionEl();
12790 ae.dom.style.display = 'none';
12794 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12795 * by an implementing function.
12797 * @param {EventObject} e
12799 onTriggerClick : Roo.emptyFn
12807 * @class Roo.bootstrap.CardUploader
12808 * @extends Roo.bootstrap.Button
12809 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12810 * @cfg {Number} errorTimeout default 3000
12811 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12812 * @cfg {Array} html The button text.
12816 * Create a new CardUploader
12817 * @param {Object} config The config object
12820 Roo.bootstrap.CardUploader = function(config){
12824 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12827 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12835 * When a image is clicked on - and needs to display a slideshow or similar..
12836 * @param {Roo.bootstrap.Card} this
12837 * @param {Object} The image information data
12843 * When a the download link is clicked
12844 * @param {Roo.bootstrap.Card} this
12845 * @param {Object} The image information data contains
12852 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12855 errorTimeout : 3000,
12859 fileCollection : false,
12862 getAutoCreate : function()
12866 cls :'form-group' ,
12871 //cls : 'input-group-addon',
12872 html : this.fieldLabel
12880 value : this.value,
12881 cls : 'd-none form-control'
12886 multiple : 'multiple',
12888 cls : 'd-none roo-card-upload-selector'
12892 cls : 'roo-card-uploader-button-container w-100 mb-2'
12895 cls : 'card-columns roo-card-uploader-container'
12905 getChildContainer : function() /// what children are added to.
12907 return this.containerEl;
12910 getButtonContainer : function() /// what children are added to.
12912 return this.el.select(".roo-card-uploader-button-container").first();
12915 initEvents : function()
12918 Roo.bootstrap.Input.prototype.initEvents.call(this);
12922 xns: Roo.bootstrap,
12925 container_method : 'getButtonContainer' ,
12926 html : this.html, // fix changable?
12929 'click' : function(btn, e) {
12938 this.urlAPI = (window.createObjectURL && window) ||
12939 (window.URL && URL.revokeObjectURL && URL) ||
12940 (window.webkitURL && webkitURL);
12945 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12947 this.selectorEl.on('change', this.onFileSelected, this);
12950 this.images.forEach(function(img) {
12953 this.images = false;
12955 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12961 onClick : function(e)
12963 e.preventDefault();
12965 this.selectorEl.dom.click();
12969 onFileSelected : function(e)
12971 e.preventDefault();
12973 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12977 Roo.each(this.selectorEl.dom.files, function(file){
12978 this.addFile(file);
12987 addFile : function(file)
12990 if(typeof(file) === 'string'){
12991 throw "Add file by name?"; // should not happen
12995 if(!file || !this.urlAPI){
13005 var url = _this.urlAPI.createObjectURL( file);
13008 id : Roo.bootstrap.CardUploader.ID--,
13009 is_uploaded : false,
13013 mimetype : file.type,
13021 * addCard - add an Attachment to the uploader
13022 * @param data - the data about the image to upload
13026 title : "Title of file",
13027 is_uploaded : false,
13028 src : "http://.....",
13029 srcfile : { the File upload object },
13030 mimetype : file.type,
13033 .. any other data...
13039 addCard : function (data)
13041 // hidden input element?
13042 // if the file is not an image...
13043 //then we need to use something other that and header_image
13048 xns : Roo.bootstrap,
13049 xtype : 'CardFooter',
13052 xns : Roo.bootstrap,
13058 xns : Roo.bootstrap,
13060 html : String.format("<small>{0}</small>", data.title),
13061 cls : 'col-10 text-left',
13066 click : function() {
13068 t.fireEvent( "download", t, data );
13074 xns : Roo.bootstrap,
13076 style: 'max-height: 28px; ',
13082 click : function() {
13083 t.removeCard(data.id)
13095 var cn = this.addxtype(
13098 xns : Roo.bootstrap,
13101 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13102 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13103 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13108 initEvents : function() {
13109 Roo.bootstrap.Card.prototype.initEvents.call(this);
13111 this.imgEl = this.el.select('.card-img-top').first();
13113 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13114 this.imgEl.set({ 'pointer' : 'cursor' });
13117 this.getCardFooter().addClass('p-1');
13124 // dont' really need ot update items.
13125 // this.items.push(cn);
13126 this.fileCollection.add(cn);
13128 if (!data.srcfile) {
13129 this.updateInput();
13134 var reader = new FileReader();
13135 reader.addEventListener("load", function() {
13136 data.srcdata = reader.result;
13139 reader.readAsDataURL(data.srcfile);
13144 removeCard : function(id)
13147 var card = this.fileCollection.get(id);
13148 card.data.is_deleted = 1;
13149 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13150 //this.fileCollection.remove(card);
13151 //this.items = this.items.filter(function(e) { return e != card });
13152 // dont' really need ot update items.
13153 card.el.dom.parentNode.removeChild(card.el.dom);
13154 this.updateInput();
13160 this.fileCollection.each(function(card) {
13161 if (card.el.dom && card.el.dom.parentNode) {
13162 card.el.dom.parentNode.removeChild(card.el.dom);
13165 this.fileCollection.clear();
13166 this.updateInput();
13169 updateInput : function()
13172 this.fileCollection.each(function(e) {
13176 this.inputEl().dom.value = JSON.stringify(data);
13186 Roo.bootstrap.CardUploader.ID = -1;/*
13188 * Ext JS Library 1.1.1
13189 * Copyright(c) 2006-2007, Ext JS, LLC.
13191 * Originally Released Under LGPL - original licence link has changed is not relivant.
13194 * <script type="text/javascript">
13199 * @class Roo.data.SortTypes
13201 * Defines the default sorting (casting?) comparison functions used when sorting data.
13203 Roo.data.SortTypes = {
13205 * Default sort that does nothing
13206 * @param {Mixed} s The value being converted
13207 * @return {Mixed} The comparison value
13209 none : function(s){
13214 * The regular expression used to strip tags
13218 stripTagsRE : /<\/?[^>]+>/gi,
13221 * Strips all HTML tags to sort on text only
13222 * @param {Mixed} s The value being converted
13223 * @return {String} The comparison value
13225 asText : function(s){
13226 return String(s).replace(this.stripTagsRE, "");
13230 * Strips all HTML tags to sort on text only - Case insensitive
13231 * @param {Mixed} s The value being converted
13232 * @return {String} The comparison value
13234 asUCText : function(s){
13235 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13239 * Case insensitive string
13240 * @param {Mixed} s The value being converted
13241 * @return {String} The comparison value
13243 asUCString : function(s) {
13244 return String(s).toUpperCase();
13249 * @param {Mixed} s The value being converted
13250 * @return {Number} The comparison value
13252 asDate : function(s) {
13256 if(s instanceof Date){
13257 return s.getTime();
13259 return Date.parse(String(s));
13264 * @param {Mixed} s The value being converted
13265 * @return {Float} The comparison value
13267 asFloat : function(s) {
13268 var val = parseFloat(String(s).replace(/,/g, ""));
13277 * @param {Mixed} s The value being converted
13278 * @return {Number} The comparison value
13280 asInt : function(s) {
13281 var val = parseInt(String(s).replace(/,/g, ""));
13289 * Ext JS Library 1.1.1
13290 * Copyright(c) 2006-2007, Ext JS, LLC.
13292 * Originally Released Under LGPL - original licence link has changed is not relivant.
13295 * <script type="text/javascript">
13299 * @class Roo.data.Record
13300 * Instances of this class encapsulate both record <em>definition</em> information, and record
13301 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13302 * to access Records cached in an {@link Roo.data.Store} object.<br>
13304 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13305 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13308 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13310 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13311 * {@link #create}. The parameters are the same.
13312 * @param {Array} data An associative Array of data values keyed by the field name.
13313 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13314 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13315 * not specified an integer id is generated.
13317 Roo.data.Record = function(data, id){
13318 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13323 * Generate a constructor for a specific record layout.
13324 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13325 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13326 * Each field definition object may contain the following properties: <ul>
13327 * <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,
13328 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13329 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13330 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13331 * is being used, then this is a string containing the javascript expression to reference the data relative to
13332 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13333 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13334 * this may be omitted.</p></li>
13335 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13336 * <ul><li>auto (Default, implies no conversion)</li>
13341 * <li>date</li></ul></p></li>
13342 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13343 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13344 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13345 * by the Reader into an object that will be stored in the Record. It is passed the
13346 * following parameters:<ul>
13347 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13349 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13351 * <br>usage:<br><pre><code>
13352 var TopicRecord = Roo.data.Record.create(
13353 {name: 'title', mapping: 'topic_title'},
13354 {name: 'author', mapping: 'username'},
13355 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13356 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13357 {name: 'lastPoster', mapping: 'user2'},
13358 {name: 'excerpt', mapping: 'post_text'}
13361 var myNewRecord = new TopicRecord({
13362 title: 'Do my job please',
13365 lastPost: new Date(),
13366 lastPoster: 'Animal',
13367 excerpt: 'No way dude!'
13369 myStore.add(myNewRecord);
13374 Roo.data.Record.create = function(o){
13375 var f = function(){
13376 f.superclass.constructor.apply(this, arguments);
13378 Roo.extend(f, Roo.data.Record);
13379 var p = f.prototype;
13380 p.fields = new Roo.util.MixedCollection(false, function(field){
13383 for(var i = 0, len = o.length; i < len; i++){
13384 p.fields.add(new Roo.data.Field(o[i]));
13386 f.getField = function(name){
13387 return p.fields.get(name);
13392 Roo.data.Record.AUTO_ID = 1000;
13393 Roo.data.Record.EDIT = 'edit';
13394 Roo.data.Record.REJECT = 'reject';
13395 Roo.data.Record.COMMIT = 'commit';
13397 Roo.data.Record.prototype = {
13399 * Readonly flag - true if this record has been modified.
13408 join : function(store){
13409 this.store = store;
13413 * Set the named field to the specified value.
13414 * @param {String} name The name of the field to set.
13415 * @param {Object} value The value to set the field to.
13417 set : function(name, value){
13418 if(this.data[name] == value){
13422 if(!this.modified){
13423 this.modified = {};
13425 if(typeof this.modified[name] == 'undefined'){
13426 this.modified[name] = this.data[name];
13428 this.data[name] = value;
13429 if(!this.editing && this.store){
13430 this.store.afterEdit(this);
13435 * Get the value of the named field.
13436 * @param {String} name The name of the field to get the value of.
13437 * @return {Object} The value of the field.
13439 get : function(name){
13440 return this.data[name];
13444 beginEdit : function(){
13445 this.editing = true;
13446 this.modified = {};
13450 cancelEdit : function(){
13451 this.editing = false;
13452 delete this.modified;
13456 endEdit : function(){
13457 this.editing = false;
13458 if(this.dirty && this.store){
13459 this.store.afterEdit(this);
13464 * Usually called by the {@link Roo.data.Store} which owns the Record.
13465 * Rejects all changes made to the Record since either creation, or the last commit operation.
13466 * Modified fields are reverted to their original values.
13468 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13469 * of reject operations.
13471 reject : function(){
13472 var m = this.modified;
13474 if(typeof m[n] != "function"){
13475 this.data[n] = m[n];
13478 this.dirty = false;
13479 delete this.modified;
13480 this.editing = false;
13482 this.store.afterReject(this);
13487 * Usually called by the {@link Roo.data.Store} which owns the Record.
13488 * Commits all changes made to the Record since either creation, or the last commit operation.
13490 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13491 * of commit operations.
13493 commit : function(){
13494 this.dirty = false;
13495 delete this.modified;
13496 this.editing = false;
13498 this.store.afterCommit(this);
13503 hasError : function(){
13504 return this.error != null;
13508 clearError : function(){
13513 * Creates a copy of this record.
13514 * @param {String} id (optional) A new record id if you don't want to use this record's id
13517 copy : function(newId) {
13518 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13522 * Ext JS Library 1.1.1
13523 * Copyright(c) 2006-2007, Ext JS, LLC.
13525 * Originally Released Under LGPL - original licence link has changed is not relivant.
13528 * <script type="text/javascript">
13534 * @class Roo.data.Store
13535 * @extends Roo.util.Observable
13536 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13537 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13539 * 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
13540 * has no knowledge of the format of the data returned by the Proxy.<br>
13542 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13543 * instances from the data object. These records are cached and made available through accessor functions.
13545 * Creates a new Store.
13546 * @param {Object} config A config object containing the objects needed for the Store to access data,
13547 * and read the data into Records.
13549 Roo.data.Store = function(config){
13550 this.data = new Roo.util.MixedCollection(false);
13551 this.data.getKey = function(o){
13554 this.baseParams = {};
13556 this.paramNames = {
13561 "multisort" : "_multisort"
13564 if(config && config.data){
13565 this.inlineData = config.data;
13566 delete config.data;
13569 Roo.apply(this, config);
13571 if(this.reader){ // reader passed
13572 this.reader = Roo.factory(this.reader, Roo.data);
13573 this.reader.xmodule = this.xmodule || false;
13574 if(!this.recordType){
13575 this.recordType = this.reader.recordType;
13577 if(this.reader.onMetaChange){
13578 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13582 if(this.recordType){
13583 this.fields = this.recordType.prototype.fields;
13585 this.modified = [];
13589 * @event datachanged
13590 * Fires when the data cache has changed, and a widget which is using this Store
13591 * as a Record cache should refresh its view.
13592 * @param {Store} this
13594 datachanged : true,
13596 * @event metachange
13597 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13598 * @param {Store} this
13599 * @param {Object} meta The JSON metadata
13604 * Fires when Records have been added to the Store
13605 * @param {Store} this
13606 * @param {Roo.data.Record[]} records The array of Records added
13607 * @param {Number} index The index at which the record(s) were added
13612 * Fires when a Record has been removed from the Store
13613 * @param {Store} this
13614 * @param {Roo.data.Record} record The Record that was removed
13615 * @param {Number} index The index at which the record was removed
13620 * Fires when a Record has been updated
13621 * @param {Store} this
13622 * @param {Roo.data.Record} record The Record that was updated
13623 * @param {String} operation The update operation being performed. Value may be one of:
13625 Roo.data.Record.EDIT
13626 Roo.data.Record.REJECT
13627 Roo.data.Record.COMMIT
13633 * Fires when the data cache has been cleared.
13634 * @param {Store} this
13638 * @event beforeload
13639 * Fires before a request is made for a new data object. If the beforeload handler returns false
13640 * the load action will be canceled.
13641 * @param {Store} this
13642 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13646 * @event beforeloadadd
13647 * Fires after a new set of Records has been loaded.
13648 * @param {Store} this
13649 * @param {Roo.data.Record[]} records The Records that were loaded
13650 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13652 beforeloadadd : true,
13655 * Fires after a new set of Records has been loaded, before they are added to the store.
13656 * @param {Store} this
13657 * @param {Roo.data.Record[]} records The Records that were loaded
13658 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13659 * @params {Object} return from reader
13663 * @event loadexception
13664 * Fires if an exception occurs in the Proxy during loading.
13665 * Called with the signature of the Proxy's "loadexception" event.
13666 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13669 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13670 * @param {Object} load options
13671 * @param {Object} jsonData from your request (normally this contains the Exception)
13673 loadexception : true
13677 this.proxy = Roo.factory(this.proxy, Roo.data);
13678 this.proxy.xmodule = this.xmodule || false;
13679 this.relayEvents(this.proxy, ["loadexception"]);
13681 this.sortToggle = {};
13682 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13684 Roo.data.Store.superclass.constructor.call(this);
13686 if(this.inlineData){
13687 this.loadData(this.inlineData);
13688 delete this.inlineData;
13692 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13694 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13695 * without a remote query - used by combo/forms at present.
13699 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13702 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13705 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13706 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13709 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13710 * on any HTTP request
13713 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13716 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13720 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13721 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13723 remoteSort : false,
13726 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13727 * loaded or when a record is removed. (defaults to false).
13729 pruneModifiedRecords : false,
13732 lastOptions : null,
13735 * Add Records to the Store and fires the add event.
13736 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13738 add : function(records){
13739 records = [].concat(records);
13740 for(var i = 0, len = records.length; i < len; i++){
13741 records[i].join(this);
13743 var index = this.data.length;
13744 this.data.addAll(records);
13745 this.fireEvent("add", this, records, index);
13749 * Remove a Record from the Store and fires the remove event.
13750 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13752 remove : function(record){
13753 var index = this.data.indexOf(record);
13754 this.data.removeAt(index);
13756 if(this.pruneModifiedRecords){
13757 this.modified.remove(record);
13759 this.fireEvent("remove", this, record, index);
13763 * Remove all Records from the Store and fires the clear event.
13765 removeAll : function(){
13767 if(this.pruneModifiedRecords){
13768 this.modified = [];
13770 this.fireEvent("clear", this);
13774 * Inserts Records to the Store at the given index and fires the add event.
13775 * @param {Number} index The start index at which to insert the passed Records.
13776 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13778 insert : function(index, records){
13779 records = [].concat(records);
13780 for(var i = 0, len = records.length; i < len; i++){
13781 this.data.insert(index, records[i]);
13782 records[i].join(this);
13784 this.fireEvent("add", this, records, index);
13788 * Get the index within the cache of the passed Record.
13789 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13790 * @return {Number} The index of the passed Record. Returns -1 if not found.
13792 indexOf : function(record){
13793 return this.data.indexOf(record);
13797 * Get the index within the cache of the Record with the passed id.
13798 * @param {String} id The id of the Record to find.
13799 * @return {Number} The index of the Record. Returns -1 if not found.
13801 indexOfId : function(id){
13802 return this.data.indexOfKey(id);
13806 * Get the Record with the specified id.
13807 * @param {String} id The id of the Record to find.
13808 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13810 getById : function(id){
13811 return this.data.key(id);
13815 * Get the Record at the specified index.
13816 * @param {Number} index The index of the Record to find.
13817 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13819 getAt : function(index){
13820 return this.data.itemAt(index);
13824 * Returns a range of Records between specified indices.
13825 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13826 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13827 * @return {Roo.data.Record[]} An array of Records
13829 getRange : function(start, end){
13830 return this.data.getRange(start, end);
13834 storeOptions : function(o){
13835 o = Roo.apply({}, o);
13838 this.lastOptions = o;
13842 * Loads the Record cache from the configured Proxy using the configured Reader.
13844 * If using remote paging, then the first load call must specify the <em>start</em>
13845 * and <em>limit</em> properties in the options.params property to establish the initial
13846 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13848 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13849 * and this call will return before the new data has been loaded. Perform any post-processing
13850 * in a callback function, or in a "load" event handler.</strong>
13852 * @param {Object} options An object containing properties which control loading options:<ul>
13853 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13854 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13855 * passed the following arguments:<ul>
13856 * <li>r : Roo.data.Record[]</li>
13857 * <li>options: Options object from the load call</li>
13858 * <li>success: Boolean success indicator</li></ul></li>
13859 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13860 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13863 load : function(options){
13864 options = options || {};
13865 if(this.fireEvent("beforeload", this, options) !== false){
13866 this.storeOptions(options);
13867 var p = Roo.apply(options.params || {}, this.baseParams);
13868 // if meta was not loaded from remote source.. try requesting it.
13869 if (!this.reader.metaFromRemote) {
13870 p._requestMeta = 1;
13872 if(this.sortInfo && this.remoteSort){
13873 var pn = this.paramNames;
13874 p[pn["sort"]] = this.sortInfo.field;
13875 p[pn["dir"]] = this.sortInfo.direction;
13877 if (this.multiSort) {
13878 var pn = this.paramNames;
13879 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13882 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13887 * Reloads the Record cache from the configured Proxy using the configured Reader and
13888 * the options from the last load operation performed.
13889 * @param {Object} options (optional) An object containing properties which may override the options
13890 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13891 * the most recently used options are reused).
13893 reload : function(options){
13894 this.load(Roo.applyIf(options||{}, this.lastOptions));
13898 // Called as a callback by the Reader during a load operation.
13899 loadRecords : function(o, options, success){
13900 if(!o || success === false){
13901 if(success !== false){
13902 this.fireEvent("load", this, [], options, o);
13904 if(options.callback){
13905 options.callback.call(options.scope || this, [], options, false);
13909 // if data returned failure - throw an exception.
13910 if (o.success === false) {
13911 // show a message if no listener is registered.
13912 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13913 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13915 // loadmask wil be hooked into this..
13916 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13919 var r = o.records, t = o.totalRecords || r.length;
13921 this.fireEvent("beforeloadadd", this, r, options, o);
13923 if(!options || options.add !== true){
13924 if(this.pruneModifiedRecords){
13925 this.modified = [];
13927 for(var i = 0, len = r.length; i < len; i++){
13931 this.data = this.snapshot;
13932 delete this.snapshot;
13935 this.data.addAll(r);
13936 this.totalLength = t;
13938 this.fireEvent("datachanged", this);
13940 this.totalLength = Math.max(t, this.data.length+r.length);
13944 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13946 var e = new Roo.data.Record({});
13948 e.set(this.parent.displayField, this.parent.emptyTitle);
13949 e.set(this.parent.valueField, '');
13954 this.fireEvent("load", this, r, options, o);
13955 if(options.callback){
13956 options.callback.call(options.scope || this, r, options, true);
13962 * Loads data from a passed data block. A Reader which understands the format of the data
13963 * must have been configured in the constructor.
13964 * @param {Object} data The data block from which to read the Records. The format of the data expected
13965 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13966 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13968 loadData : function(o, append){
13969 var r = this.reader.readRecords(o);
13970 this.loadRecords(r, {add: append}, true);
13974 * using 'cn' the nested child reader read the child array into it's child stores.
13975 * @param {Object} rec The record with a 'children array
13977 loadDataFromChildren : function(rec)
13979 this.loadData(this.reader.toLoadData(rec));
13984 * Gets the number of cached records.
13986 * <em>If using paging, this may not be the total size of the dataset. If the data object
13987 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13988 * the data set size</em>
13990 getCount : function(){
13991 return this.data.length || 0;
13995 * Gets the total number of records in the dataset as returned by the server.
13997 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13998 * the dataset size</em>
14000 getTotalCount : function(){
14001 return this.totalLength || 0;
14005 * Returns the sort state of the Store as an object with two properties:
14007 field {String} The name of the field by which the Records are sorted
14008 direction {String} The sort order, "ASC" or "DESC"
14011 getSortState : function(){
14012 return this.sortInfo;
14016 applySort : function(){
14017 if(this.sortInfo && !this.remoteSort){
14018 var s = this.sortInfo, f = s.field;
14019 var st = this.fields.get(f).sortType;
14020 var fn = function(r1, r2){
14021 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14022 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14024 this.data.sort(s.direction, fn);
14025 if(this.snapshot && this.snapshot != this.data){
14026 this.snapshot.sort(s.direction, fn);
14032 * Sets the default sort column and order to be used by the next load operation.
14033 * @param {String} fieldName The name of the field to sort by.
14034 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14036 setDefaultSort : function(field, dir){
14037 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14041 * Sort the Records.
14042 * If remote sorting is used, the sort is performed on the server, and the cache is
14043 * reloaded. If local sorting is used, the cache is sorted internally.
14044 * @param {String} fieldName The name of the field to sort by.
14045 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14047 sort : function(fieldName, dir){
14048 var f = this.fields.get(fieldName);
14050 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14052 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14053 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14058 this.sortToggle[f.name] = dir;
14059 this.sortInfo = {field: f.name, direction: dir};
14060 if(!this.remoteSort){
14062 this.fireEvent("datachanged", this);
14064 this.load(this.lastOptions);
14069 * Calls the specified function for each of the Records in the cache.
14070 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14071 * Returning <em>false</em> aborts and exits the iteration.
14072 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14074 each : function(fn, scope){
14075 this.data.each(fn, scope);
14079 * Gets all records modified since the last commit. Modified records are persisted across load operations
14080 * (e.g., during paging).
14081 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14083 getModifiedRecords : function(){
14084 return this.modified;
14088 createFilterFn : function(property, value, anyMatch){
14089 if(!value.exec){ // not a regex
14090 value = String(value);
14091 if(value.length == 0){
14094 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14096 return function(r){
14097 return value.test(r.data[property]);
14102 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14103 * @param {String} property A field on your records
14104 * @param {Number} start The record index to start at (defaults to 0)
14105 * @param {Number} end The last record index to include (defaults to length - 1)
14106 * @return {Number} The sum
14108 sum : function(property, start, end){
14109 var rs = this.data.items, v = 0;
14110 start = start || 0;
14111 end = (end || end === 0) ? end : rs.length-1;
14113 for(var i = start; i <= end; i++){
14114 v += (rs[i].data[property] || 0);
14120 * Filter the records by a specified property.
14121 * @param {String} field A field on your records
14122 * @param {String/RegExp} value Either a string that the field
14123 * should start with or a RegExp to test against the field
14124 * @param {Boolean} anyMatch True to match any part not just the beginning
14126 filter : function(property, value, anyMatch){
14127 var fn = this.createFilterFn(property, value, anyMatch);
14128 return fn ? this.filterBy(fn) : this.clearFilter();
14132 * Filter by a function. The specified function will be called with each
14133 * record in this data source. If the function returns true the record is included,
14134 * otherwise it is filtered.
14135 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14136 * @param {Object} scope (optional) The scope of the function (defaults to this)
14138 filterBy : function(fn, scope){
14139 this.snapshot = this.snapshot || this.data;
14140 this.data = this.queryBy(fn, scope||this);
14141 this.fireEvent("datachanged", this);
14145 * Query the records by a specified property.
14146 * @param {String} field A field on your records
14147 * @param {String/RegExp} value Either a string that the field
14148 * should start with or a RegExp to test against the field
14149 * @param {Boolean} anyMatch True to match any part not just the beginning
14150 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14152 query : function(property, value, anyMatch){
14153 var fn = this.createFilterFn(property, value, anyMatch);
14154 return fn ? this.queryBy(fn) : this.data.clone();
14158 * Query by a function. The specified function will be called with each
14159 * record in this data source. If the function returns true the record is included
14161 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14162 * @param {Object} scope (optional) The scope of the function (defaults to this)
14163 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14165 queryBy : function(fn, scope){
14166 var data = this.snapshot || this.data;
14167 return data.filterBy(fn, scope||this);
14171 * Collects unique values for a particular dataIndex from this store.
14172 * @param {String} dataIndex The property to collect
14173 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14174 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14175 * @return {Array} An array of the unique values
14177 collect : function(dataIndex, allowNull, bypassFilter){
14178 var d = (bypassFilter === true && this.snapshot) ?
14179 this.snapshot.items : this.data.items;
14180 var v, sv, r = [], l = {};
14181 for(var i = 0, len = d.length; i < len; i++){
14182 v = d[i].data[dataIndex];
14184 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14193 * Revert to a view of the Record cache with no filtering applied.
14194 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14196 clearFilter : function(suppressEvent){
14197 if(this.snapshot && this.snapshot != this.data){
14198 this.data = this.snapshot;
14199 delete this.snapshot;
14200 if(suppressEvent !== true){
14201 this.fireEvent("datachanged", this);
14207 afterEdit : function(record){
14208 if(this.modified.indexOf(record) == -1){
14209 this.modified.push(record);
14211 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14215 afterReject : function(record){
14216 this.modified.remove(record);
14217 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14221 afterCommit : function(record){
14222 this.modified.remove(record);
14223 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14227 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14228 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14230 commitChanges : function(){
14231 var m = this.modified.slice(0);
14232 this.modified = [];
14233 for(var i = 0, len = m.length; i < len; i++){
14239 * Cancel outstanding changes on all changed records.
14241 rejectChanges : function(){
14242 var m = this.modified.slice(0);
14243 this.modified = [];
14244 for(var i = 0, len = m.length; i < len; i++){
14249 onMetaChange : function(meta, rtype, o){
14250 this.recordType = rtype;
14251 this.fields = rtype.prototype.fields;
14252 delete this.snapshot;
14253 this.sortInfo = meta.sortInfo || this.sortInfo;
14254 this.modified = [];
14255 this.fireEvent('metachange', this, this.reader.meta);
14258 moveIndex : function(data, type)
14260 var index = this.indexOf(data);
14262 var newIndex = index + type;
14266 this.insert(newIndex, data);
14271 * Ext JS Library 1.1.1
14272 * Copyright(c) 2006-2007, Ext JS, LLC.
14274 * Originally Released Under LGPL - original licence link has changed is not relivant.
14277 * <script type="text/javascript">
14281 * @class Roo.data.SimpleStore
14282 * @extends Roo.data.Store
14283 * Small helper class to make creating Stores from Array data easier.
14284 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14285 * @cfg {Array} fields An array of field definition objects, or field name strings.
14286 * @cfg {Object} an existing reader (eg. copied from another store)
14287 * @cfg {Array} data The multi-dimensional array of data
14289 * @param {Object} config
14291 Roo.data.SimpleStore = function(config)
14293 Roo.data.SimpleStore.superclass.constructor.call(this, {
14295 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14298 Roo.data.Record.create(config.fields)
14300 proxy : new Roo.data.MemoryProxy(config.data)
14304 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14306 * Ext JS Library 1.1.1
14307 * Copyright(c) 2006-2007, Ext JS, LLC.
14309 * Originally Released Under LGPL - original licence link has changed is not relivant.
14312 * <script type="text/javascript">
14317 * @extends Roo.data.Store
14318 * @class Roo.data.JsonStore
14319 * Small helper class to make creating Stores for JSON data easier. <br/>
14321 var store = new Roo.data.JsonStore({
14322 url: 'get-images.php',
14324 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14327 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14328 * JsonReader and HttpProxy (unless inline data is provided).</b>
14329 * @cfg {Array} fields An array of field definition objects, or field name strings.
14331 * @param {Object} config
14333 Roo.data.JsonStore = function(c){
14334 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14335 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14336 reader: new Roo.data.JsonReader(c, c.fields)
14339 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14341 * Ext JS Library 1.1.1
14342 * Copyright(c) 2006-2007, Ext JS, LLC.
14344 * Originally Released Under LGPL - original licence link has changed is not relivant.
14347 * <script type="text/javascript">
14351 Roo.data.Field = function(config){
14352 if(typeof config == "string"){
14353 config = {name: config};
14355 Roo.apply(this, config);
14358 this.type = "auto";
14361 var st = Roo.data.SortTypes;
14362 // named sortTypes are supported, here we look them up
14363 if(typeof this.sortType == "string"){
14364 this.sortType = st[this.sortType];
14367 // set default sortType for strings and dates
14368 if(!this.sortType){
14371 this.sortType = st.asUCString;
14374 this.sortType = st.asDate;
14377 this.sortType = st.none;
14382 var stripRe = /[\$,%]/g;
14384 // prebuilt conversion function for this field, instead of
14385 // switching every time we're reading a value
14387 var cv, dateFormat = this.dateFormat;
14392 cv = function(v){ return v; };
14395 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14399 return v !== undefined && v !== null && v !== '' ?
14400 parseInt(String(v).replace(stripRe, ""), 10) : '';
14405 return v !== undefined && v !== null && v !== '' ?
14406 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14411 cv = function(v){ return v === true || v === "true" || v == 1; };
14418 if(v instanceof Date){
14422 if(dateFormat == "timestamp"){
14423 return new Date(v*1000);
14425 return Date.parseDate(v, dateFormat);
14427 var parsed = Date.parse(v);
14428 return parsed ? new Date(parsed) : null;
14437 Roo.data.Field.prototype = {
14445 * Ext JS Library 1.1.1
14446 * Copyright(c) 2006-2007, Ext JS, LLC.
14448 * Originally Released Under LGPL - original licence link has changed is not relivant.
14451 * <script type="text/javascript">
14454 // Base class for reading structured data from a data source. This class is intended to be
14455 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14458 * @class Roo.data.DataReader
14459 * Base class for reading structured data from a data source. This class is intended to be
14460 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14463 Roo.data.DataReader = function(meta, recordType){
14467 this.recordType = recordType instanceof Array ?
14468 Roo.data.Record.create(recordType) : recordType;
14471 Roo.data.DataReader.prototype = {
14474 readerType : 'Data',
14476 * Create an empty record
14477 * @param {Object} data (optional) - overlay some values
14478 * @return {Roo.data.Record} record created.
14480 newRow : function(d) {
14482 this.recordType.prototype.fields.each(function(c) {
14484 case 'int' : da[c.name] = 0; break;
14485 case 'date' : da[c.name] = new Date(); break;
14486 case 'float' : da[c.name] = 0.0; break;
14487 case 'boolean' : da[c.name] = false; break;
14488 default : da[c.name] = ""; break;
14492 return new this.recordType(Roo.apply(da, d));
14498 * Ext JS Library 1.1.1
14499 * Copyright(c) 2006-2007, Ext JS, LLC.
14501 * Originally Released Under LGPL - original licence link has changed is not relivant.
14504 * <script type="text/javascript">
14508 * @class Roo.data.DataProxy
14509 * @extends Roo.data.Observable
14510 * This class is an abstract base class for implementations which provide retrieval of
14511 * unformatted data objects.<br>
14513 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14514 * (of the appropriate type which knows how to parse the data object) to provide a block of
14515 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14517 * Custom implementations must implement the load method as described in
14518 * {@link Roo.data.HttpProxy#load}.
14520 Roo.data.DataProxy = function(){
14523 * @event beforeload
14524 * Fires before a network request is made to retrieve a data object.
14525 * @param {Object} This DataProxy object.
14526 * @param {Object} params The params parameter to the load function.
14531 * Fires before the load method's callback is called.
14532 * @param {Object} This DataProxy object.
14533 * @param {Object} o The data object.
14534 * @param {Object} arg The callback argument object passed to the load function.
14538 * @event loadexception
14539 * Fires if an Exception occurs during data retrieval.
14540 * @param {Object} This DataProxy object.
14541 * @param {Object} o The data object.
14542 * @param {Object} arg The callback argument object passed to the load function.
14543 * @param {Object} e The Exception.
14545 loadexception : true
14547 Roo.data.DataProxy.superclass.constructor.call(this);
14550 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14553 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14557 * Ext JS Library 1.1.1
14558 * Copyright(c) 2006-2007, Ext JS, LLC.
14560 * Originally Released Under LGPL - original licence link has changed is not relivant.
14563 * <script type="text/javascript">
14566 * @class Roo.data.MemoryProxy
14567 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14568 * to the Reader when its load method is called.
14570 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14572 Roo.data.MemoryProxy = function(data){
14576 Roo.data.MemoryProxy.superclass.constructor.call(this);
14580 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14583 * Load data from the requested source (in this case an in-memory
14584 * data object passed to the constructor), read the data object into
14585 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14586 * process that block using the passed callback.
14587 * @param {Object} params This parameter is not used by the MemoryProxy class.
14588 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14589 * object into a block of Roo.data.Records.
14590 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14591 * The function must be passed <ul>
14592 * <li>The Record block object</li>
14593 * <li>The "arg" argument from the load function</li>
14594 * <li>A boolean success indicator</li>
14596 * @param {Object} scope The scope in which to call the callback
14597 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14599 load : function(params, reader, callback, scope, arg){
14600 params = params || {};
14603 result = reader.readRecords(params.data ? params.data :this.data);
14605 this.fireEvent("loadexception", this, arg, null, e);
14606 callback.call(scope, null, arg, false);
14609 callback.call(scope, result, arg, true);
14613 update : function(params, records){
14618 * Ext JS Library 1.1.1
14619 * Copyright(c) 2006-2007, Ext JS, LLC.
14621 * Originally Released Under LGPL - original licence link has changed is not relivant.
14624 * <script type="text/javascript">
14627 * @class Roo.data.HttpProxy
14628 * @extends Roo.data.DataProxy
14629 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14630 * configured to reference a certain URL.<br><br>
14632 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14633 * from which the running page was served.<br><br>
14635 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14637 * Be aware that to enable the browser to parse an XML document, the server must set
14638 * the Content-Type header in the HTTP response to "text/xml".
14640 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14641 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14642 * will be used to make the request.
14644 Roo.data.HttpProxy = function(conn){
14645 Roo.data.HttpProxy.superclass.constructor.call(this);
14646 // is conn a conn config or a real conn?
14648 this.useAjax = !conn || !conn.events;
14652 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14653 // thse are take from connection...
14656 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14659 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14660 * extra parameters to each request made by this object. (defaults to undefined)
14663 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14664 * to each request made by this object. (defaults to undefined)
14667 * @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)
14670 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14673 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14679 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14683 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14684 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14685 * a finer-grained basis than the DataProxy events.
14687 getConnection : function(){
14688 return this.useAjax ? Roo.Ajax : this.conn;
14692 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14693 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14694 * process that block using the passed callback.
14695 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14696 * for the request to the remote server.
14697 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14698 * object into a block of Roo.data.Records.
14699 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14700 * The function must be passed <ul>
14701 * <li>The Record block object</li>
14702 * <li>The "arg" argument from the load function</li>
14703 * <li>A boolean success indicator</li>
14705 * @param {Object} scope The scope in which to call the callback
14706 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14708 load : function(params, reader, callback, scope, arg){
14709 if(this.fireEvent("beforeload", this, params) !== false){
14711 params : params || {},
14713 callback : callback,
14718 callback : this.loadResponse,
14722 Roo.applyIf(o, this.conn);
14723 if(this.activeRequest){
14724 Roo.Ajax.abort(this.activeRequest);
14726 this.activeRequest = Roo.Ajax.request(o);
14728 this.conn.request(o);
14731 callback.call(scope||this, null, arg, false);
14736 loadResponse : function(o, success, response){
14737 delete this.activeRequest;
14739 this.fireEvent("loadexception", this, o, response);
14740 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14745 result = o.reader.read(response);
14747 this.fireEvent("loadexception", this, o, response, e);
14748 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14752 this.fireEvent("load", this, o, o.request.arg);
14753 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14757 update : function(dataSet){
14762 updateResponse : function(dataSet){
14767 * Ext JS Library 1.1.1
14768 * Copyright(c) 2006-2007, Ext JS, LLC.
14770 * Originally Released Under LGPL - original licence link has changed is not relivant.
14773 * <script type="text/javascript">
14777 * @class Roo.data.ScriptTagProxy
14778 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14779 * other than the originating domain of the running page.<br><br>
14781 * <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
14782 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14784 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14785 * source code that is used as the source inside a <script> tag.<br><br>
14787 * In order for the browser to process the returned data, the server must wrap the data object
14788 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14789 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14790 * depending on whether the callback name was passed:
14793 boolean scriptTag = false;
14794 String cb = request.getParameter("callback");
14797 response.setContentType("text/javascript");
14799 response.setContentType("application/x-json");
14801 Writer out = response.getWriter();
14803 out.write(cb + "(");
14805 out.print(dataBlock.toJsonString());
14812 * @param {Object} config A configuration object.
14814 Roo.data.ScriptTagProxy = function(config){
14815 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14816 Roo.apply(this, config);
14817 this.head = document.getElementsByTagName("head")[0];
14820 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14822 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14824 * @cfg {String} url The URL from which to request the data object.
14827 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14831 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14832 * the server the name of the callback function set up by the load call to process the returned data object.
14833 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14834 * javascript output which calls this named function passing the data object as its only parameter.
14836 callbackParam : "callback",
14838 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14839 * name to the request.
14844 * Load data from the configured URL, read the data object into
14845 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14846 * process that block using the passed callback.
14847 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14848 * for the request to the remote server.
14849 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14850 * object into a block of Roo.data.Records.
14851 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14852 * The function must be passed <ul>
14853 * <li>The Record block object</li>
14854 * <li>The "arg" argument from the load function</li>
14855 * <li>A boolean success indicator</li>
14857 * @param {Object} scope The scope in which to call the callback
14858 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14860 load : function(params, reader, callback, scope, arg){
14861 if(this.fireEvent("beforeload", this, params) !== false){
14863 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14865 var url = this.url;
14866 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14868 url += "&_dc=" + (new Date().getTime());
14870 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14873 cb : "stcCallback"+transId,
14874 scriptId : "stcScript"+transId,
14878 callback : callback,
14884 window[trans.cb] = function(o){
14885 conn.handleResponse(o, trans);
14888 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14890 if(this.autoAbort !== false){
14894 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14896 var script = document.createElement("script");
14897 script.setAttribute("src", url);
14898 script.setAttribute("type", "text/javascript");
14899 script.setAttribute("id", trans.scriptId);
14900 this.head.appendChild(script);
14902 this.trans = trans;
14904 callback.call(scope||this, null, arg, false);
14909 isLoading : function(){
14910 return this.trans ? true : false;
14914 * Abort the current server request.
14916 abort : function(){
14917 if(this.isLoading()){
14918 this.destroyTrans(this.trans);
14923 destroyTrans : function(trans, isLoaded){
14924 this.head.removeChild(document.getElementById(trans.scriptId));
14925 clearTimeout(trans.timeoutId);
14927 window[trans.cb] = undefined;
14929 delete window[trans.cb];
14932 // if hasn't been loaded, wait for load to remove it to prevent script error
14933 window[trans.cb] = function(){
14934 window[trans.cb] = undefined;
14936 delete window[trans.cb];
14943 handleResponse : function(o, trans){
14944 this.trans = false;
14945 this.destroyTrans(trans, true);
14948 result = trans.reader.readRecords(o);
14950 this.fireEvent("loadexception", this, o, trans.arg, e);
14951 trans.callback.call(trans.scope||window, null, trans.arg, false);
14954 this.fireEvent("load", this, o, trans.arg);
14955 trans.callback.call(trans.scope||window, result, trans.arg, true);
14959 handleFailure : function(trans){
14960 this.trans = false;
14961 this.destroyTrans(trans, false);
14962 this.fireEvent("loadexception", this, null, trans.arg);
14963 trans.callback.call(trans.scope||window, null, trans.arg, false);
14967 * Ext JS Library 1.1.1
14968 * Copyright(c) 2006-2007, Ext JS, LLC.
14970 * Originally Released Under LGPL - original licence link has changed is not relivant.
14973 * <script type="text/javascript">
14977 * @class Roo.data.JsonReader
14978 * @extends Roo.data.DataReader
14979 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14980 * based on mappings in a provided Roo.data.Record constructor.
14982 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14983 * in the reply previously.
14988 var RecordDef = Roo.data.Record.create([
14989 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14990 {name: 'occupation'} // This field will use "occupation" as the mapping.
14992 var myReader = new Roo.data.JsonReader({
14993 totalProperty: "results", // The property which contains the total dataset size (optional)
14994 root: "rows", // The property which contains an Array of row objects
14995 id: "id" // The property within each row object that provides an ID for the record (optional)
14999 * This would consume a JSON file like this:
15001 { 'results': 2, 'rows': [
15002 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15003 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15006 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15007 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15008 * paged from the remote server.
15009 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15010 * @cfg {String} root name of the property which contains the Array of row objects.
15011 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15012 * @cfg {Array} fields Array of field definition objects
15014 * Create a new JsonReader
15015 * @param {Object} meta Metadata configuration options
15016 * @param {Object} recordType Either an Array of field definition objects,
15017 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15019 Roo.data.JsonReader = function(meta, recordType){
15022 // set some defaults:
15023 Roo.applyIf(meta, {
15024 totalProperty: 'total',
15025 successProperty : 'success',
15030 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15032 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15034 readerType : 'Json',
15037 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15038 * Used by Store query builder to append _requestMeta to params.
15041 metaFromRemote : false,
15043 * This method is only used by a DataProxy which has retrieved data from a remote server.
15044 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15045 * @return {Object} data A data block which is used by an Roo.data.Store object as
15046 * a cache of Roo.data.Records.
15048 read : function(response){
15049 var json = response.responseText;
15051 var o = /* eval:var:o */ eval("("+json+")");
15053 throw {message: "JsonReader.read: Json object not found"};
15059 this.metaFromRemote = true;
15060 this.meta = o.metaData;
15061 this.recordType = Roo.data.Record.create(o.metaData.fields);
15062 this.onMetaChange(this.meta, this.recordType, o);
15064 return this.readRecords(o);
15067 // private function a store will implement
15068 onMetaChange : function(meta, recordType, o){
15075 simpleAccess: function(obj, subsc) {
15082 getJsonAccessor: function(){
15084 return function(expr) {
15086 return(re.test(expr))
15087 ? new Function("obj", "return obj." + expr)
15092 return Roo.emptyFn;
15097 * Create a data block containing Roo.data.Records from an XML document.
15098 * @param {Object} o An object which contains an Array of row objects in the property specified
15099 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15100 * which contains the total size of the dataset.
15101 * @return {Object} data A data block which is used by an Roo.data.Store object as
15102 * a cache of Roo.data.Records.
15104 readRecords : function(o){
15106 * After any data loads, the raw JSON data is available for further custom processing.
15110 var s = this.meta, Record = this.recordType,
15111 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15113 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15115 if(s.totalProperty) {
15116 this.getTotal = this.getJsonAccessor(s.totalProperty);
15118 if(s.successProperty) {
15119 this.getSuccess = this.getJsonAccessor(s.successProperty);
15121 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15123 var g = this.getJsonAccessor(s.id);
15124 this.getId = function(rec) {
15126 return (r === undefined || r === "") ? null : r;
15129 this.getId = function(){return null;};
15132 for(var jj = 0; jj < fl; jj++){
15134 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15135 this.ef[jj] = this.getJsonAccessor(map);
15139 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15140 if(s.totalProperty){
15141 var vt = parseInt(this.getTotal(o), 10);
15146 if(s.successProperty){
15147 var vs = this.getSuccess(o);
15148 if(vs === false || vs === 'false'){
15153 for(var i = 0; i < c; i++){
15156 var id = this.getId(n);
15157 for(var j = 0; j < fl; j++){
15159 var v = this.ef[j](n);
15161 Roo.log('missing convert for ' + f.name);
15165 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15167 var record = new Record(values, id);
15169 records[i] = record;
15175 totalRecords : totalRecords
15178 // used when loading children.. @see loadDataFromChildren
15179 toLoadData: function(rec)
15181 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15182 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15183 return { data : data, total : data.length };
15188 * Ext JS Library 1.1.1
15189 * Copyright(c) 2006-2007, Ext JS, LLC.
15191 * Originally Released Under LGPL - original licence link has changed is not relivant.
15194 * <script type="text/javascript">
15198 * @class Roo.data.ArrayReader
15199 * @extends Roo.data.DataReader
15200 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15201 * Each element of that Array represents a row of data fields. The
15202 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15203 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15207 var RecordDef = Roo.data.Record.create([
15208 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15209 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15211 var myReader = new Roo.data.ArrayReader({
15212 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15216 * This would consume an Array like this:
15218 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15222 * Create a new JsonReader
15223 * @param {Object} meta Metadata configuration options.
15224 * @param {Object|Array} recordType Either an Array of field definition objects
15226 * @cfg {Array} fields Array of field definition objects
15227 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15228 * as specified to {@link Roo.data.Record#create},
15229 * or an {@link Roo.data.Record} object
15232 * created using {@link Roo.data.Record#create}.
15234 Roo.data.ArrayReader = function(meta, recordType)
15236 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15239 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15242 * Create a data block containing Roo.data.Records from an XML document.
15243 * @param {Object} o An Array of row objects which represents the dataset.
15244 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15245 * a cache of Roo.data.Records.
15247 readRecords : function(o)
15249 var sid = this.meta ? this.meta.id : null;
15250 var recordType = this.recordType, fields = recordType.prototype.fields;
15253 for(var i = 0; i < root.length; i++){
15256 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15257 for(var j = 0, jlen = fields.length; j < jlen; j++){
15258 var f = fields.items[j];
15259 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15260 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15262 values[f.name] = v;
15264 var record = new recordType(values, id);
15266 records[records.length] = record;
15270 totalRecords : records.length
15273 // used when loading children.. @see loadDataFromChildren
15274 toLoadData: function(rec)
15276 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15277 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15288 * @class Roo.bootstrap.ComboBox
15289 * @extends Roo.bootstrap.TriggerField
15290 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15291 * @cfg {Boolean} append (true|false) default false
15292 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15293 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15294 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15295 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15296 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15297 * @cfg {Boolean} animate default true
15298 * @cfg {Boolean} emptyResultText only for touch device
15299 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15300 * @cfg {String} emptyTitle default ''
15301 * @cfg {Number} width fixed with? experimental
15303 * Create a new ComboBox.
15304 * @param {Object} config Configuration options
15306 Roo.bootstrap.ComboBox = function(config){
15307 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15311 * Fires when the dropdown list is expanded
15312 * @param {Roo.bootstrap.ComboBox} combo This combo box
15317 * Fires when the dropdown list is collapsed
15318 * @param {Roo.bootstrap.ComboBox} combo This combo box
15322 * @event beforeselect
15323 * Fires before a list item is selected. Return false to cancel the selection.
15324 * @param {Roo.bootstrap.ComboBox} combo This combo box
15325 * @param {Roo.data.Record} record The data record returned from the underlying store
15326 * @param {Number} index The index of the selected item in the dropdown list
15328 'beforeselect' : true,
15331 * Fires when a list item is selected
15332 * @param {Roo.bootstrap.ComboBox} combo This combo box
15333 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15334 * @param {Number} index The index of the selected item in the dropdown list
15338 * @event beforequery
15339 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15340 * The event object passed has these properties:
15341 * @param {Roo.bootstrap.ComboBox} combo This combo box
15342 * @param {String} query The query
15343 * @param {Boolean} forceAll true to force "all" query
15344 * @param {Boolean} cancel true to cancel the query
15345 * @param {Object} e The query event object
15347 'beforequery': true,
15350 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15351 * @param {Roo.bootstrap.ComboBox} combo This combo box
15356 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15357 * @param {Roo.bootstrap.ComboBox} combo This combo box
15358 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15363 * Fires when the remove value from the combobox array
15364 * @param {Roo.bootstrap.ComboBox} combo This combo box
15368 * @event afterremove
15369 * Fires when the remove value from the combobox array
15370 * @param {Roo.bootstrap.ComboBox} combo This combo box
15372 'afterremove' : true,
15374 * @event specialfilter
15375 * Fires when specialfilter
15376 * @param {Roo.bootstrap.ComboBox} combo This combo box
15378 'specialfilter' : true,
15381 * Fires when tick the element
15382 * @param {Roo.bootstrap.ComboBox} combo This combo box
15386 * @event touchviewdisplay
15387 * Fires when touch view require special display (default is using displayField)
15388 * @param {Roo.bootstrap.ComboBox} combo This combo box
15389 * @param {Object} cfg set html .
15391 'touchviewdisplay' : true
15396 this.tickItems = [];
15398 this.selectedIndex = -1;
15399 if(this.mode == 'local'){
15400 if(config.queryDelay === undefined){
15401 this.queryDelay = 10;
15403 if(config.minChars === undefined){
15409 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15412 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15413 * rendering into an Roo.Editor, defaults to false)
15416 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15417 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15420 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15423 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15424 * the dropdown list (defaults to undefined, with no header element)
15428 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15432 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15434 listWidth: undefined,
15436 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15437 * mode = 'remote' or 'text' if mode = 'local')
15439 displayField: undefined,
15442 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15443 * mode = 'remote' or 'value' if mode = 'local').
15444 * Note: use of a valueField requires the user make a selection
15445 * in order for a value to be mapped.
15447 valueField: undefined,
15449 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15454 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15455 * field's data value (defaults to the underlying DOM element's name)
15457 hiddenName: undefined,
15459 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15463 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15465 selectedClass: 'active',
15468 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15472 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15473 * anchor positions (defaults to 'tl-bl')
15475 listAlign: 'tl-bl?',
15477 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15481 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15482 * query specified by the allQuery config option (defaults to 'query')
15484 triggerAction: 'query',
15486 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15487 * (defaults to 4, does not apply if editable = false)
15491 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15492 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15496 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15497 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15501 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15502 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15506 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15507 * when editable = true (defaults to false)
15509 selectOnFocus:false,
15511 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15513 queryParam: 'query',
15515 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15516 * when mode = 'remote' (defaults to 'Loading...')
15518 loadingText: 'Loading...',
15520 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15524 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15528 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15529 * traditional select (defaults to true)
15533 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15537 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15541 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15542 * listWidth has a higher value)
15546 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15547 * allow the user to set arbitrary text into the field (defaults to false)
15549 forceSelection:false,
15551 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15552 * if typeAhead = true (defaults to 250)
15554 typeAheadDelay : 250,
15556 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15557 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15559 valueNotFoundText : undefined,
15561 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15563 blockFocus : false,
15566 * @cfg {Boolean} disableClear Disable showing of clear button.
15568 disableClear : false,
15570 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15572 alwaysQuery : false,
15575 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15580 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15582 invalidClass : "has-warning",
15585 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15587 validClass : "has-success",
15590 * @cfg {Boolean} specialFilter (true|false) special filter default false
15592 specialFilter : false,
15595 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15597 mobileTouchView : true,
15600 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15602 useNativeIOS : false,
15605 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15607 mobile_restrict_height : false,
15609 ios_options : false,
15621 btnPosition : 'right',
15622 triggerList : true,
15623 showToggleBtn : true,
15625 emptyResultText: 'Empty',
15626 triggerText : 'Select',
15630 // element that contains real text value.. (when hidden is used..)
15632 getAutoCreate : function()
15637 * Render classic select for iso
15640 if(Roo.isIOS && this.useNativeIOS){
15641 cfg = this.getAutoCreateNativeIOS();
15649 if(Roo.isTouch && this.mobileTouchView){
15650 cfg = this.getAutoCreateTouchView();
15657 if(!this.tickable){
15658 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15663 * ComboBox with tickable selections
15666 var align = this.labelAlign || this.parentLabelAlign();
15669 cls : 'form-group roo-combobox-tickable' //input-group
15672 var btn_text_select = '';
15673 var btn_text_done = '';
15674 var btn_text_cancel = '';
15676 if (this.btn_text_show) {
15677 btn_text_select = 'Select';
15678 btn_text_done = 'Done';
15679 btn_text_cancel = 'Cancel';
15684 cls : 'tickable-buttons',
15689 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15690 //html : this.triggerText
15691 html: btn_text_select
15697 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15699 html: btn_text_done
15705 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15707 html: btn_text_cancel
15713 buttons.cn.unshift({
15715 cls: 'roo-select2-search-field-input'
15721 Roo.each(buttons.cn, function(c){
15723 c.cls += ' btn-' + _this.size;
15726 if (_this.disabled) {
15733 style : 'display: contents',
15738 cls: 'form-hidden-field'
15742 cls: 'roo-select2-choices',
15746 cls: 'roo-select2-search-field',
15757 cls: 'roo-select2-container input-group roo-select2-container-multi',
15763 // cls: 'typeahead typeahead-long dropdown-menu',
15764 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15769 if(this.hasFeedback && !this.allowBlank){
15773 cls: 'glyphicon form-control-feedback'
15776 combobox.cn.push(feedback);
15783 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15784 tooltip : 'This field is required'
15786 if (Roo.bootstrap.version == 4) {
15789 style : 'display:none'
15792 if (align ==='left' && this.fieldLabel.length) {
15794 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15801 cls : 'control-label col-form-label',
15802 html : this.fieldLabel
15814 var labelCfg = cfg.cn[1];
15815 var contentCfg = cfg.cn[2];
15818 if(this.indicatorpos == 'right'){
15824 cls : 'control-label col-form-label',
15828 html : this.fieldLabel
15844 labelCfg = cfg.cn[0];
15845 contentCfg = cfg.cn[1];
15849 if(this.labelWidth > 12){
15850 labelCfg.style = "width: " + this.labelWidth + 'px';
15852 if(this.width * 1 > 0){
15853 contentCfg.style = "width: " + this.width + 'px';
15855 if(this.labelWidth < 13 && this.labelmd == 0){
15856 this.labelmd = this.labelWidth;
15859 if(this.labellg > 0){
15860 labelCfg.cls += ' col-lg-' + this.labellg;
15861 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15864 if(this.labelmd > 0){
15865 labelCfg.cls += ' col-md-' + this.labelmd;
15866 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15869 if(this.labelsm > 0){
15870 labelCfg.cls += ' col-sm-' + this.labelsm;
15871 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15874 if(this.labelxs > 0){
15875 labelCfg.cls += ' col-xs-' + this.labelxs;
15876 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15880 } else if ( this.fieldLabel.length) {
15881 // Roo.log(" label");
15886 //cls : 'input-group-addon',
15887 html : this.fieldLabel
15892 if(this.indicatorpos == 'right'){
15896 //cls : 'input-group-addon',
15897 html : this.fieldLabel
15907 // Roo.log(" no label && no align");
15914 ['xs','sm','md','lg'].map(function(size){
15915 if (settings[size]) {
15916 cfg.cls += ' col-' + size + '-' + settings[size];
15924 _initEventsCalled : false,
15927 initEvents: function()
15929 if (this._initEventsCalled) { // as we call render... prevent looping...
15932 this._initEventsCalled = true;
15935 throw "can not find store for combo";
15938 this.indicator = this.indicatorEl();
15940 this.store = Roo.factory(this.store, Roo.data);
15941 this.store.parent = this;
15943 // if we are building from html. then this element is so complex, that we can not really
15944 // use the rendered HTML.
15945 // so we have to trash and replace the previous code.
15946 if (Roo.XComponent.build_from_html) {
15947 // remove this element....
15948 var e = this.el.dom, k=0;
15949 while (e ) { e = e.previousSibling; ++k;}
15954 this.rendered = false;
15956 this.render(this.parent().getChildContainer(true), k);
15959 if(Roo.isIOS && this.useNativeIOS){
15960 this.initIOSView();
15968 if(Roo.isTouch && this.mobileTouchView){
15969 this.initTouchView();
15974 this.initTickableEvents();
15978 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15980 if(this.hiddenName){
15982 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15984 this.hiddenField.dom.value =
15985 this.hiddenValue !== undefined ? this.hiddenValue :
15986 this.value !== undefined ? this.value : '';
15988 // prevent input submission
15989 this.el.dom.removeAttribute('name');
15990 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15995 // this.el.dom.setAttribute('autocomplete', 'off');
15998 var cls = 'x-combo-list';
16000 //this.list = new Roo.Layer({
16001 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16007 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16008 _this.list.setWidth(lw);
16011 this.list.on('mouseover', this.onViewOver, this);
16012 this.list.on('mousemove', this.onViewMove, this);
16013 this.list.on('scroll', this.onViewScroll, this);
16016 this.list.swallowEvent('mousewheel');
16017 this.assetHeight = 0;
16020 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16021 this.assetHeight += this.header.getHeight();
16024 this.innerList = this.list.createChild({cls:cls+'-inner'});
16025 this.innerList.on('mouseover', this.onViewOver, this);
16026 this.innerList.on('mousemove', this.onViewMove, this);
16027 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16029 if(this.allowBlank && !this.pageSize && !this.disableClear){
16030 this.footer = this.list.createChild({cls:cls+'-ft'});
16031 this.pageTb = new Roo.Toolbar(this.footer);
16035 this.footer = this.list.createChild({cls:cls+'-ft'});
16036 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16037 {pageSize: this.pageSize});
16041 if (this.pageTb && this.allowBlank && !this.disableClear) {
16043 this.pageTb.add(new Roo.Toolbar.Fill(), {
16044 cls: 'x-btn-icon x-btn-clear',
16046 handler: function()
16049 _this.clearValue();
16050 _this.onSelect(false, -1);
16055 this.assetHeight += this.footer.getHeight();
16060 this.tpl = Roo.bootstrap.version == 4 ?
16061 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16062 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16065 this.view = new Roo.View(this.list, this.tpl, {
16066 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16068 //this.view.wrapEl.setDisplayed(false);
16069 this.view.on('click', this.onViewClick, this);
16072 this.store.on('beforeload', this.onBeforeLoad, this);
16073 this.store.on('load', this.onLoad, this);
16074 this.store.on('loadexception', this.onLoadException, this);
16076 if(this.resizable){
16077 this.resizer = new Roo.Resizable(this.list, {
16078 pinned:true, handles:'se'
16080 this.resizer.on('resize', function(r, w, h){
16081 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16082 this.listWidth = w;
16083 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16084 this.restrictHeight();
16086 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16089 if(!this.editable){
16090 this.editable = true;
16091 this.setEditable(false);
16096 if (typeof(this.events.add.listeners) != 'undefined') {
16098 this.addicon = this.wrap.createChild(
16099 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16101 this.addicon.on('click', function(e) {
16102 this.fireEvent('add', this);
16105 if (typeof(this.events.edit.listeners) != 'undefined') {
16107 this.editicon = this.wrap.createChild(
16108 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16109 if (this.addicon) {
16110 this.editicon.setStyle('margin-left', '40px');
16112 this.editicon.on('click', function(e) {
16114 // we fire even if inothing is selected..
16115 this.fireEvent('edit', this, this.lastData );
16121 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16122 "up" : function(e){
16123 this.inKeyMode = true;
16127 "down" : function(e){
16128 if(!this.isExpanded()){
16129 this.onTriggerClick();
16131 this.inKeyMode = true;
16136 "enter" : function(e){
16137 // this.onViewClick();
16141 if(this.fireEvent("specialkey", this, e)){
16142 this.onViewClick(false);
16148 "esc" : function(e){
16152 "tab" : function(e){
16155 if(this.fireEvent("specialkey", this, e)){
16156 this.onViewClick(false);
16164 doRelay : function(foo, bar, hname){
16165 if(hname == 'down' || this.scope.isExpanded()){
16166 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16175 this.queryDelay = Math.max(this.queryDelay || 10,
16176 this.mode == 'local' ? 10 : 250);
16179 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16181 if(this.typeAhead){
16182 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16184 if(this.editable !== false){
16185 this.inputEl().on("keyup", this.onKeyUp, this);
16187 if(this.forceSelection){
16188 this.inputEl().on('blur', this.doForce, this);
16192 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16193 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16197 initTickableEvents: function()
16201 if(this.hiddenName){
16203 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16205 this.hiddenField.dom.value =
16206 this.hiddenValue !== undefined ? this.hiddenValue :
16207 this.value !== undefined ? this.value : '';
16209 // prevent input submission
16210 this.el.dom.removeAttribute('name');
16211 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16216 // this.list = this.el.select('ul.dropdown-menu',true).first();
16218 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16219 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16220 if(this.triggerList){
16221 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16224 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16225 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16227 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16228 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16230 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16231 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16233 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16234 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16235 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16238 this.cancelBtn.hide();
16243 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16244 _this.list.setWidth(lw);
16247 this.list.on('mouseover', this.onViewOver, this);
16248 this.list.on('mousemove', this.onViewMove, this);
16250 this.list.on('scroll', this.onViewScroll, this);
16253 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16254 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16257 this.view = new Roo.View(this.list, this.tpl, {
16262 selectedClass: this.selectedClass
16265 //this.view.wrapEl.setDisplayed(false);
16266 this.view.on('click', this.onViewClick, this);
16270 this.store.on('beforeload', this.onBeforeLoad, this);
16271 this.store.on('load', this.onLoad, this);
16272 this.store.on('loadexception', this.onLoadException, this);
16275 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16276 "up" : function(e){
16277 this.inKeyMode = true;
16281 "down" : function(e){
16282 this.inKeyMode = true;
16286 "enter" : function(e){
16287 if(this.fireEvent("specialkey", this, e)){
16288 this.onViewClick(false);
16294 "esc" : function(e){
16295 this.onTickableFooterButtonClick(e, false, false);
16298 "tab" : function(e){
16299 this.fireEvent("specialkey", this, e);
16301 this.onTickableFooterButtonClick(e, false, false);
16308 doRelay : function(e, fn, key){
16309 if(this.scope.isExpanded()){
16310 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16319 this.queryDelay = Math.max(this.queryDelay || 10,
16320 this.mode == 'local' ? 10 : 250);
16323 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16325 if(this.typeAhead){
16326 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16329 if(this.editable !== false){
16330 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16333 this.indicator = this.indicatorEl();
16335 if(this.indicator){
16336 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16337 this.indicator.hide();
16342 onDestroy : function(){
16344 this.view.setStore(null);
16345 this.view.el.removeAllListeners();
16346 this.view.el.remove();
16347 this.view.purgeListeners();
16350 this.list.dom.innerHTML = '';
16354 this.store.un('beforeload', this.onBeforeLoad, this);
16355 this.store.un('load', this.onLoad, this);
16356 this.store.un('loadexception', this.onLoadException, this);
16358 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16362 fireKey : function(e){
16363 if(e.isNavKeyPress() && !this.list.isVisible()){
16364 this.fireEvent("specialkey", this, e);
16369 onResize: function(w, h)
16373 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16375 // if(typeof w != 'number'){
16376 // // we do not handle it!?!?
16379 // var tw = this.trigger.getWidth();
16380 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16381 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16383 // this.inputEl().setWidth( this.adjustWidth('input', x));
16385 // //this.trigger.setStyle('left', x+'px');
16387 // if(this.list && this.listWidth === undefined){
16388 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16389 // this.list.setWidth(lw);
16390 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16398 * Allow or prevent the user from directly editing the field text. If false is passed,
16399 * the user will only be able to select from the items defined in the dropdown list. This method
16400 * is the runtime equivalent of setting the 'editable' config option at config time.
16401 * @param {Boolean} value True to allow the user to directly edit the field text
16403 setEditable : function(value){
16404 if(value == this.editable){
16407 this.editable = value;
16409 this.inputEl().dom.setAttribute('readOnly', true);
16410 this.inputEl().on('mousedown', this.onTriggerClick, this);
16411 this.inputEl().addClass('x-combo-noedit');
16413 this.inputEl().dom.removeAttribute('readOnly');
16414 this.inputEl().un('mousedown', this.onTriggerClick, this);
16415 this.inputEl().removeClass('x-combo-noedit');
16421 onBeforeLoad : function(combo,opts){
16422 if(!this.hasFocus){
16426 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16428 this.restrictHeight();
16429 this.selectedIndex = -1;
16433 onLoad : function(){
16435 this.hasQuery = false;
16437 if(!this.hasFocus){
16441 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442 this.loading.hide();
16445 if(this.store.getCount() > 0){
16448 this.restrictHeight();
16449 if(this.lastQuery == this.allQuery){
16450 if(this.editable && !this.tickable){
16451 this.inputEl().dom.select();
16455 !this.selectByValue(this.value, true) &&
16458 !this.store.lastOptions ||
16459 typeof(this.store.lastOptions.add) == 'undefined' ||
16460 this.store.lastOptions.add != true
16463 this.select(0, true);
16466 if(this.autoFocus){
16469 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16470 this.taTask.delay(this.typeAheadDelay);
16474 this.onEmptyResults();
16480 onLoadException : function()
16482 this.hasQuery = false;
16484 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16485 this.loading.hide();
16488 if(this.tickable && this.editable){
16493 // only causes errors at present
16494 //Roo.log(this.store.reader.jsonData);
16495 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16497 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16503 onTypeAhead : function(){
16504 if(this.store.getCount() > 0){
16505 var r = this.store.getAt(0);
16506 var newValue = r.data[this.displayField];
16507 var len = newValue.length;
16508 var selStart = this.getRawValue().length;
16510 if(selStart != len){
16511 this.setRawValue(newValue);
16512 this.selectText(selStart, newValue.length);
16518 onSelect : function(record, index){
16520 if(this.fireEvent('beforeselect', this, record, index) !== false){
16522 this.setFromData(index > -1 ? record.data : false);
16525 this.fireEvent('select', this, record, index);
16530 * Returns the currently selected field value or empty string if no value is set.
16531 * @return {String} value The selected value
16533 getValue : function()
16535 if(Roo.isIOS && this.useNativeIOS){
16536 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16540 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16543 if(this.valueField){
16544 return typeof this.value != 'undefined' ? this.value : '';
16546 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16550 getRawValue : function()
16552 if(Roo.isIOS && this.useNativeIOS){
16553 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16556 var v = this.inputEl().getValue();
16562 * Clears any text/value currently set in the field
16564 clearValue : function(){
16566 if(this.hiddenField){
16567 this.hiddenField.dom.value = '';
16570 this.setRawValue('');
16571 this.lastSelectionText = '';
16572 this.lastData = false;
16574 var close = this.closeTriggerEl();
16585 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16586 * will be displayed in the field. If the value does not match the data value of an existing item,
16587 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16588 * Otherwise the field will be blank (although the value will still be set).
16589 * @param {String} value The value to match
16591 setValue : function(v)
16593 if(Roo.isIOS && this.useNativeIOS){
16594 this.setIOSValue(v);
16604 if(this.valueField){
16605 var r = this.findRecord(this.valueField, v);
16607 text = r.data[this.displayField];
16608 }else if(this.valueNotFoundText !== undefined){
16609 text = this.valueNotFoundText;
16612 this.lastSelectionText = text;
16613 if(this.hiddenField){
16614 this.hiddenField.dom.value = v;
16616 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16619 var close = this.closeTriggerEl();
16622 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16628 * @property {Object} the last set data for the element
16633 * Sets the value of the field based on a object which is related to the record format for the store.
16634 * @param {Object} value the value to set as. or false on reset?
16636 setFromData : function(o){
16643 var dv = ''; // display value
16644 var vv = ''; // value value..
16646 if (this.displayField) {
16647 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16649 // this is an error condition!!!
16650 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16653 if(this.valueField){
16654 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16657 var close = this.closeTriggerEl();
16660 if(dv.length || vv * 1 > 0){
16662 this.blockFocus=true;
16668 if(this.hiddenField){
16669 this.hiddenField.dom.value = vv;
16671 this.lastSelectionText = dv;
16672 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16676 // no hidden field.. - we store the value in 'value', but still display
16677 // display field!!!!
16678 this.lastSelectionText = dv;
16679 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16686 reset : function(){
16687 // overridden so that last data is reset..
16694 this.setValue(this.originalValue);
16695 //this.clearInvalid();
16696 this.lastData = false;
16698 this.view.clearSelections();
16704 findRecord : function(prop, value){
16706 if(this.store.getCount() > 0){
16707 this.store.each(function(r){
16708 if(r.data[prop] == value){
16718 getName: function()
16720 // returns hidden if it's set..
16721 if (!this.rendered) {return ''};
16722 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16726 onViewMove : function(e, t){
16727 this.inKeyMode = false;
16731 onViewOver : function(e, t){
16732 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16735 var item = this.view.findItemFromChild(t);
16738 var index = this.view.indexOf(item);
16739 this.select(index, false);
16744 onViewClick : function(view, doFocus, el, e)
16746 var index = this.view.getSelectedIndexes()[0];
16748 var r = this.store.getAt(index);
16752 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16759 Roo.each(this.tickItems, function(v,k){
16761 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16763 _this.tickItems.splice(k, 1);
16765 if(typeof(e) == 'undefined' && view == false){
16766 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16778 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16779 this.tickItems.push(r.data);
16782 if(typeof(e) == 'undefined' && view == false){
16783 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16790 this.onSelect(r, index);
16792 if(doFocus !== false && !this.blockFocus){
16793 this.inputEl().focus();
16798 restrictHeight : function(){
16799 //this.innerList.dom.style.height = '';
16800 //var inner = this.innerList.dom;
16801 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16802 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16803 //this.list.beginUpdate();
16804 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16805 this.list.alignTo(this.inputEl(), this.listAlign);
16806 this.list.alignTo(this.inputEl(), this.listAlign);
16807 //this.list.endUpdate();
16811 onEmptyResults : function(){
16813 if(this.tickable && this.editable){
16814 this.hasFocus = false;
16815 this.restrictHeight();
16823 * Returns true if the dropdown list is expanded, else false.
16825 isExpanded : function(){
16826 return this.list.isVisible();
16830 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16831 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16832 * @param {String} value The data value of the item to select
16833 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16834 * selected item if it is not currently in view (defaults to true)
16835 * @return {Boolean} True if the value matched an item in the list, else false
16837 selectByValue : function(v, scrollIntoView){
16838 if(v !== undefined && v !== null){
16839 var r = this.findRecord(this.valueField || this.displayField, v);
16841 this.select(this.store.indexOf(r), scrollIntoView);
16849 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16850 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16851 * @param {Number} index The zero-based index of the list item to select
16852 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16853 * selected item if it is not currently in view (defaults to true)
16855 select : function(index, scrollIntoView){
16856 this.selectedIndex = index;
16857 this.view.select(index);
16858 if(scrollIntoView !== false){
16859 var el = this.view.getNode(index);
16861 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16864 this.list.scrollChildIntoView(el, false);
16870 selectNext : function(){
16871 var ct = this.store.getCount();
16873 if(this.selectedIndex == -1){
16875 }else if(this.selectedIndex < ct-1){
16876 this.select(this.selectedIndex+1);
16882 selectPrev : function(){
16883 var ct = this.store.getCount();
16885 if(this.selectedIndex == -1){
16887 }else if(this.selectedIndex != 0){
16888 this.select(this.selectedIndex-1);
16894 onKeyUp : function(e){
16895 if(this.editable !== false && !e.isSpecialKey()){
16896 this.lastKey = e.getKey();
16897 this.dqTask.delay(this.queryDelay);
16902 validateBlur : function(){
16903 return !this.list || !this.list.isVisible();
16907 initQuery : function(){
16909 var v = this.getRawValue();
16911 if(this.tickable && this.editable){
16912 v = this.tickableInputEl().getValue();
16919 doForce : function(){
16920 if(this.inputEl().dom.value.length > 0){
16921 this.inputEl().dom.value =
16922 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16928 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16929 * query allowing the query action to be canceled if needed.
16930 * @param {String} query The SQL query to execute
16931 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16932 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16933 * saved in the current store (defaults to false)
16935 doQuery : function(q, forceAll){
16937 if(q === undefined || q === null){
16942 forceAll: forceAll,
16946 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16951 forceAll = qe.forceAll;
16952 if(forceAll === true || (q.length >= this.minChars)){
16954 this.hasQuery = true;
16956 if(this.lastQuery != q || this.alwaysQuery){
16957 this.lastQuery = q;
16958 if(this.mode == 'local'){
16959 this.selectedIndex = -1;
16961 this.store.clearFilter();
16964 if(this.specialFilter){
16965 this.fireEvent('specialfilter', this);
16970 this.store.filter(this.displayField, q);
16973 this.store.fireEvent("datachanged", this.store);
16980 this.store.baseParams[this.queryParam] = q;
16982 var options = {params : this.getParams(q)};
16985 options.add = true;
16986 options.params.start = this.page * this.pageSize;
16989 this.store.load(options);
16992 * this code will make the page width larger, at the beginning, the list not align correctly,
16993 * we should expand the list on onLoad
16994 * so command out it
16999 this.selectedIndex = -1;
17004 this.loadNext = false;
17008 getParams : function(q){
17010 //p[this.queryParam] = q;
17014 p.limit = this.pageSize;
17020 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17022 collapse : function(){
17023 if(!this.isExpanded()){
17029 this.hasFocus = false;
17033 this.cancelBtn.hide();
17034 this.trigger.show();
17037 this.tickableInputEl().dom.value = '';
17038 this.tickableInputEl().blur();
17043 Roo.get(document).un('mousedown', this.collapseIf, this);
17044 Roo.get(document).un('mousewheel', this.collapseIf, this);
17045 if (!this.editable) {
17046 Roo.get(document).un('keydown', this.listKeyPress, this);
17048 this.fireEvent('collapse', this);
17054 collapseIf : function(e){
17055 var in_combo = e.within(this.el);
17056 var in_list = e.within(this.list);
17057 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17059 if (in_combo || in_list || is_list) {
17060 //e.stopPropagation();
17065 this.onTickableFooterButtonClick(e, false, false);
17073 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17075 expand : function(){
17077 if(this.isExpanded() || !this.hasFocus){
17081 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17082 this.list.setWidth(lw);
17088 this.restrictHeight();
17092 this.tickItems = Roo.apply([], this.item);
17095 this.cancelBtn.show();
17096 this.trigger.hide();
17099 this.tickableInputEl().focus();
17104 Roo.get(document).on('mousedown', this.collapseIf, this);
17105 Roo.get(document).on('mousewheel', this.collapseIf, this);
17106 if (!this.editable) {
17107 Roo.get(document).on('keydown', this.listKeyPress, this);
17110 this.fireEvent('expand', this);
17114 // Implements the default empty TriggerField.onTriggerClick function
17115 onTriggerClick : function(e)
17117 Roo.log('trigger click');
17119 if(this.disabled || !this.triggerList){
17124 this.loadNext = false;
17126 if(this.isExpanded()){
17128 if (!this.blockFocus) {
17129 this.inputEl().focus();
17133 this.hasFocus = true;
17134 if(this.triggerAction == 'all') {
17135 this.doQuery(this.allQuery, true);
17137 this.doQuery(this.getRawValue());
17139 if (!this.blockFocus) {
17140 this.inputEl().focus();
17145 onTickableTriggerClick : function(e)
17152 this.loadNext = false;
17153 this.hasFocus = true;
17155 if(this.triggerAction == 'all') {
17156 this.doQuery(this.allQuery, true);
17158 this.doQuery(this.getRawValue());
17162 onSearchFieldClick : function(e)
17164 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17165 this.onTickableFooterButtonClick(e, false, false);
17169 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17174 this.loadNext = false;
17175 this.hasFocus = true;
17177 if(this.triggerAction == 'all') {
17178 this.doQuery(this.allQuery, true);
17180 this.doQuery(this.getRawValue());
17184 listKeyPress : function(e)
17186 //Roo.log('listkeypress');
17187 // scroll to first matching element based on key pres..
17188 if (e.isSpecialKey()) {
17191 var k = String.fromCharCode(e.getKey()).toUpperCase();
17194 var csel = this.view.getSelectedNodes();
17195 var cselitem = false;
17197 var ix = this.view.indexOf(csel[0]);
17198 cselitem = this.store.getAt(ix);
17199 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17205 this.store.each(function(v) {
17207 // start at existing selection.
17208 if (cselitem.id == v.id) {
17214 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17215 match = this.store.indexOf(v);
17221 if (match === false) {
17222 return true; // no more action?
17225 this.view.select(match);
17226 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17227 sn.scrollIntoView(sn.dom.parentNode, false);
17230 onViewScroll : function(e, t){
17232 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){
17236 this.hasQuery = true;
17238 this.loading = this.list.select('.loading', true).first();
17240 if(this.loading === null){
17241 this.list.createChild({
17243 cls: 'loading roo-select2-more-results roo-select2-active',
17244 html: 'Loading more results...'
17247 this.loading = this.list.select('.loading', true).first();
17249 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17251 this.loading.hide();
17254 this.loading.show();
17259 this.loadNext = true;
17261 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17266 addItem : function(o)
17268 var dv = ''; // display value
17270 if (this.displayField) {
17271 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17273 // this is an error condition!!!
17274 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17281 var choice = this.choices.createChild({
17283 cls: 'roo-select2-search-choice',
17292 cls: 'roo-select2-search-choice-close fa fa-times',
17297 }, this.searchField);
17299 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17301 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17309 this.inputEl().dom.value = '';
17314 onRemoveItem : function(e, _self, o)
17316 e.preventDefault();
17318 this.lastItem = Roo.apply([], this.item);
17320 var index = this.item.indexOf(o.data) * 1;
17323 Roo.log('not this item?!');
17327 this.item.splice(index, 1);
17332 this.fireEvent('remove', this, e);
17338 syncValue : function()
17340 if(!this.item.length){
17347 Roo.each(this.item, function(i){
17348 if(_this.valueField){
17349 value.push(i[_this.valueField]);
17356 this.value = value.join(',');
17358 if(this.hiddenField){
17359 this.hiddenField.dom.value = this.value;
17362 this.store.fireEvent("datachanged", this.store);
17367 clearItem : function()
17369 if(!this.multiple){
17375 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17383 if(this.tickable && !Roo.isTouch){
17384 this.view.refresh();
17388 inputEl: function ()
17390 if(Roo.isIOS && this.useNativeIOS){
17391 return this.el.select('select.roo-ios-select', true).first();
17394 if(Roo.isTouch && this.mobileTouchView){
17395 return this.el.select('input.form-control',true).first();
17399 return this.searchField;
17402 return this.el.select('input.form-control',true).first();
17405 onTickableFooterButtonClick : function(e, btn, el)
17407 e.preventDefault();
17409 this.lastItem = Roo.apply([], this.item);
17411 if(btn && btn.name == 'cancel'){
17412 this.tickItems = Roo.apply([], this.item);
17421 Roo.each(this.tickItems, function(o){
17429 validate : function()
17431 if(this.getVisibilityEl().hasClass('hidden')){
17435 var v = this.getRawValue();
17438 v = this.getValue();
17441 if(this.disabled || this.allowBlank || v.length){
17446 this.markInvalid();
17450 tickableInputEl : function()
17452 if(!this.tickable || !this.editable){
17453 return this.inputEl();
17456 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17460 getAutoCreateTouchView : function()
17465 cls: 'form-group' //input-group
17471 type : this.inputType,
17472 cls : 'form-control x-combo-noedit',
17473 autocomplete: 'new-password',
17474 placeholder : this.placeholder || '',
17479 input.name = this.name;
17483 input.cls += ' input-' + this.size;
17486 if (this.disabled) {
17487 input.disabled = true;
17491 cls : 'roo-combobox-wrap',
17498 inputblock.cls += ' input-group';
17500 inputblock.cn.unshift({
17502 cls : 'input-group-addon input-group-prepend input-group-text',
17507 if(this.removable && !this.multiple){
17508 inputblock.cls += ' roo-removable';
17510 inputblock.cn.push({
17513 cls : 'roo-combo-removable-btn close'
17517 if(this.hasFeedback && !this.allowBlank){
17519 inputblock.cls += ' has-feedback';
17521 inputblock.cn.push({
17523 cls: 'glyphicon form-control-feedback'
17530 inputblock.cls += (this.before) ? '' : ' input-group';
17532 inputblock.cn.push({
17534 cls : 'input-group-addon input-group-append input-group-text',
17540 var ibwrap = inputblock;
17545 cls: 'roo-select2-choices',
17549 cls: 'roo-select2-search-field',
17562 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17567 cls: 'form-hidden-field'
17573 if(!this.multiple && this.showToggleBtn){
17579 if (this.caret != false) {
17582 cls: 'fa fa-' + this.caret
17589 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17591 Roo.bootstrap.version == 3 ? caret : '',
17594 cls: 'combobox-clear',
17608 combobox.cls += ' roo-select2-container-multi';
17611 var required = this.allowBlank ? {
17613 style: 'display: none'
17616 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17617 tooltip : 'This field is required'
17620 var align = this.labelAlign || this.parentLabelAlign();
17622 if (align ==='left' && this.fieldLabel.length) {
17628 cls : 'control-label col-form-label',
17629 html : this.fieldLabel
17633 cls : 'roo-combobox-wrap ',
17640 var labelCfg = cfg.cn[1];
17641 var contentCfg = cfg.cn[2];
17644 if(this.indicatorpos == 'right'){
17649 cls : 'control-label col-form-label',
17653 html : this.fieldLabel
17659 cls : "roo-combobox-wrap ",
17667 labelCfg = cfg.cn[0];
17668 contentCfg = cfg.cn[1];
17673 if(this.labelWidth > 12){
17674 labelCfg.style = "width: " + this.labelWidth + 'px';
17677 if(this.labelWidth < 13 && this.labelmd == 0){
17678 this.labelmd = this.labelWidth;
17681 if(this.labellg > 0){
17682 labelCfg.cls += ' col-lg-' + this.labellg;
17683 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17686 if(this.labelmd > 0){
17687 labelCfg.cls += ' col-md-' + this.labelmd;
17688 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17691 if(this.labelsm > 0){
17692 labelCfg.cls += ' col-sm-' + this.labelsm;
17693 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17696 if(this.labelxs > 0){
17697 labelCfg.cls += ' col-xs-' + this.labelxs;
17698 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17702 } else if ( this.fieldLabel.length) {
17707 cls : 'control-label',
17708 html : this.fieldLabel
17719 if(this.indicatorpos == 'right'){
17723 cls : 'control-label',
17724 html : this.fieldLabel,
17742 var settings = this;
17744 ['xs','sm','md','lg'].map(function(size){
17745 if (settings[size]) {
17746 cfg.cls += ' col-' + size + '-' + settings[size];
17753 initTouchView : function()
17755 this.renderTouchView();
17757 this.touchViewEl.on('scroll', function(){
17758 this.el.dom.scrollTop = 0;
17761 this.originalValue = this.getValue();
17763 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17765 this.inputEl().on("click", this.showTouchView, this);
17766 if (this.triggerEl) {
17767 this.triggerEl.on("click", this.showTouchView, this);
17771 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17772 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17774 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17776 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17777 this.store.on('load', this.onTouchViewLoad, this);
17778 this.store.on('loadexception', this.onTouchViewLoadException, this);
17780 if(this.hiddenName){
17782 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17784 this.hiddenField.dom.value =
17785 this.hiddenValue !== undefined ? this.hiddenValue :
17786 this.value !== undefined ? this.value : '';
17788 this.el.dom.removeAttribute('name');
17789 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17793 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17794 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17797 if(this.removable && !this.multiple){
17798 var close = this.closeTriggerEl();
17800 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17801 close.on('click', this.removeBtnClick, this, close);
17805 * fix the bug in Safari iOS8
17807 this.inputEl().on("focus", function(e){
17808 document.activeElement.blur();
17811 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17818 renderTouchView : function()
17820 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17821 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17824 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17827 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17828 this.touchViewBodyEl.setStyle('overflow', 'auto');
17830 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17831 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17833 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17834 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17838 showTouchView : function()
17844 this.touchViewHeaderEl.hide();
17846 if(this.modalTitle.length){
17847 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17848 this.touchViewHeaderEl.show();
17851 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17852 this.touchViewEl.show();
17854 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17856 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17857 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17859 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17861 if(this.modalTitle.length){
17862 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17865 this.touchViewBodyEl.setHeight(bodyHeight);
17869 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17871 this.touchViewEl.addClass(['in','show']);
17874 if(this._touchViewMask){
17875 Roo.get(document.body).addClass("x-body-masked");
17876 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17877 this._touchViewMask.setStyle('z-index', 10000);
17878 this._touchViewMask.addClass('show');
17881 this.doTouchViewQuery();
17885 hideTouchView : function()
17887 this.touchViewEl.removeClass(['in','show']);
17891 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17893 this.touchViewEl.setStyle('display', 'none');
17896 if(this._touchViewMask){
17897 this._touchViewMask.removeClass('show');
17898 Roo.get(document.body).removeClass("x-body-masked");
17902 setTouchViewValue : function()
17909 Roo.each(this.tickItems, function(o){
17914 this.hideTouchView();
17917 doTouchViewQuery : function()
17926 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17930 if(!this.alwaysQuery || this.mode == 'local'){
17931 this.onTouchViewLoad();
17938 onTouchViewBeforeLoad : function(combo,opts)
17944 onTouchViewLoad : function()
17946 if(this.store.getCount() < 1){
17947 this.onTouchViewEmptyResults();
17951 this.clearTouchView();
17953 var rawValue = this.getRawValue();
17955 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17957 this.tickItems = [];
17959 this.store.data.each(function(d, rowIndex){
17960 var row = this.touchViewListGroup.createChild(template);
17962 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17963 row.addClass(d.data.cls);
17966 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17969 html : d.data[this.displayField]
17972 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17973 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17976 row.removeClass('selected');
17977 if(!this.multiple && this.valueField &&
17978 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17981 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17982 row.addClass('selected');
17985 if(this.multiple && this.valueField &&
17986 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17990 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17991 this.tickItems.push(d.data);
17994 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17998 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18000 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18002 if(this.modalTitle.length){
18003 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18006 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18008 if(this.mobile_restrict_height && listHeight < bodyHeight){
18009 this.touchViewBodyEl.setHeight(listHeight);
18014 if(firstChecked && listHeight > bodyHeight){
18015 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18020 onTouchViewLoadException : function()
18022 this.hideTouchView();
18025 onTouchViewEmptyResults : function()
18027 this.clearTouchView();
18029 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18031 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18035 clearTouchView : function()
18037 this.touchViewListGroup.dom.innerHTML = '';
18040 onTouchViewClick : function(e, el, o)
18042 e.preventDefault();
18045 var rowIndex = o.rowIndex;
18047 var r = this.store.getAt(rowIndex);
18049 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18051 if(!this.multiple){
18052 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18053 c.dom.removeAttribute('checked');
18056 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18058 this.setFromData(r.data);
18060 var close = this.closeTriggerEl();
18066 this.hideTouchView();
18068 this.fireEvent('select', this, r, rowIndex);
18073 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18074 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18075 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18079 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18080 this.addItem(r.data);
18081 this.tickItems.push(r.data);
18085 getAutoCreateNativeIOS : function()
18088 cls: 'form-group' //input-group,
18093 cls : 'roo-ios-select'
18097 combobox.name = this.name;
18100 if (this.disabled) {
18101 combobox.disabled = true;
18104 var settings = this;
18106 ['xs','sm','md','lg'].map(function(size){
18107 if (settings[size]) {
18108 cfg.cls += ' col-' + size + '-' + settings[size];
18118 initIOSView : function()
18120 this.store.on('load', this.onIOSViewLoad, this);
18125 onIOSViewLoad : function()
18127 if(this.store.getCount() < 1){
18131 this.clearIOSView();
18133 if(this.allowBlank) {
18135 var default_text = '-- SELECT --';
18137 if(this.placeholder.length){
18138 default_text = this.placeholder;
18141 if(this.emptyTitle.length){
18142 default_text += ' - ' + this.emptyTitle + ' -';
18145 var opt = this.inputEl().createChild({
18148 html : default_text
18152 o[this.valueField] = 0;
18153 o[this.displayField] = default_text;
18155 this.ios_options.push({
18162 this.store.data.each(function(d, rowIndex){
18166 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18167 html = d.data[this.displayField];
18172 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18173 value = d.data[this.valueField];
18182 if(this.value == d.data[this.valueField]){
18183 option['selected'] = true;
18186 var opt = this.inputEl().createChild(option);
18188 this.ios_options.push({
18195 this.inputEl().on('change', function(){
18196 this.fireEvent('select', this);
18201 clearIOSView: function()
18203 this.inputEl().dom.innerHTML = '';
18205 this.ios_options = [];
18208 setIOSValue: function(v)
18212 if(!this.ios_options){
18216 Roo.each(this.ios_options, function(opts){
18218 opts.el.dom.removeAttribute('selected');
18220 if(opts.data[this.valueField] != v){
18224 opts.el.dom.setAttribute('selected', true);
18230 * @cfg {Boolean} grow
18234 * @cfg {Number} growMin
18238 * @cfg {Number} growMax
18247 Roo.apply(Roo.bootstrap.ComboBox, {
18251 cls: 'modal-header',
18273 cls: 'list-group-item',
18277 cls: 'roo-combobox-list-group-item-value'
18281 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18295 listItemCheckbox : {
18297 cls: 'list-group-item',
18301 cls: 'roo-combobox-list-group-item-value'
18305 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18321 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18326 cls: 'modal-footer',
18334 cls: 'col-xs-6 text-left',
18337 cls: 'btn btn-danger roo-touch-view-cancel',
18343 cls: 'col-xs-6 text-right',
18346 cls: 'btn btn-success roo-touch-view-ok',
18357 Roo.apply(Roo.bootstrap.ComboBox, {
18359 touchViewTemplate : {
18361 cls: 'modal fade roo-combobox-touch-view',
18365 cls: 'modal-dialog',
18366 style : 'position:fixed', // we have to fix position....
18370 cls: 'modal-content',
18372 Roo.bootstrap.ComboBox.header,
18373 Roo.bootstrap.ComboBox.body,
18374 Roo.bootstrap.ComboBox.footer
18383 * Ext JS Library 1.1.1
18384 * Copyright(c) 2006-2007, Ext JS, LLC.
18386 * Originally Released Under LGPL - original licence link has changed is not relivant.
18389 * <script type="text/javascript">
18394 * @extends Roo.util.Observable
18395 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18396 * This class also supports single and multi selection modes. <br>
18397 * Create a data model bound view:
18399 var store = new Roo.data.Store(...);
18401 var view = new Roo.View({
18403 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18405 singleSelect: true,
18406 selectedClass: "ydataview-selected",
18410 // listen for node click?
18411 view.on("click", function(vw, index, node, e){
18412 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18416 dataModel.load("foobar.xml");
18418 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18420 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18421 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18423 * Note: old style constructor is still suported (container, template, config)
18426 * Create a new View
18427 * @param {Object} config The config object
18430 Roo.View = function(config, depreciated_tpl, depreciated_config){
18432 this.parent = false;
18434 if (typeof(depreciated_tpl) == 'undefined') {
18435 // new way.. - universal constructor.
18436 Roo.apply(this, config);
18437 this.el = Roo.get(this.el);
18440 this.el = Roo.get(config);
18441 this.tpl = depreciated_tpl;
18442 Roo.apply(this, depreciated_config);
18444 this.wrapEl = this.el.wrap().wrap();
18445 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18448 if(typeof(this.tpl) == "string"){
18449 this.tpl = new Roo.Template(this.tpl);
18451 // support xtype ctors..
18452 this.tpl = new Roo.factory(this.tpl, Roo);
18456 this.tpl.compile();
18461 * @event beforeclick
18462 * Fires before a click is processed. Returns false to cancel the default action.
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 "beforeclick" : true,
18471 * Fires when a template node is clicked.
18472 * @param {Roo.View} this
18473 * @param {Number} index The index of the target node
18474 * @param {HTMLElement} node The target node
18475 * @param {Roo.EventObject} e The raw event object
18480 * Fires when a template node is double clicked.
18481 * @param {Roo.View} this
18482 * @param {Number} index The index of the target node
18483 * @param {HTMLElement} node The target node
18484 * @param {Roo.EventObject} e The raw event object
18488 * @event contextmenu
18489 * Fires when a template node is right clicked.
18490 * @param {Roo.View} this
18491 * @param {Number} index The index of the target node
18492 * @param {HTMLElement} node The target node
18493 * @param {Roo.EventObject} e The raw event object
18495 "contextmenu" : true,
18497 * @event selectionchange
18498 * Fires when the selected nodes change.
18499 * @param {Roo.View} this
18500 * @param {Array} selections Array of the selected nodes
18502 "selectionchange" : true,
18505 * @event beforeselect
18506 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18507 * @param {Roo.View} this
18508 * @param {HTMLElement} node The node to be selected
18509 * @param {Array} selections Array of currently selected nodes
18511 "beforeselect" : true,
18513 * @event preparedata
18514 * Fires on every row to render, to allow you to change the data.
18515 * @param {Roo.View} this
18516 * @param {Object} data to be rendered (change this)
18518 "preparedata" : true
18526 "click": this.onClick,
18527 "dblclick": this.onDblClick,
18528 "contextmenu": this.onContextMenu,
18532 this.selections = [];
18534 this.cmp = new Roo.CompositeElementLite([]);
18536 this.store = Roo.factory(this.store, Roo.data);
18537 this.setStore(this.store, true);
18540 if ( this.footer && this.footer.xtype) {
18542 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18544 this.footer.dataSource = this.store;
18545 this.footer.container = fctr;
18546 this.footer = Roo.factory(this.footer, Roo);
18547 fctr.insertFirst(this.el);
18549 // this is a bit insane - as the paging toolbar seems to detach the el..
18550 // dom.parentNode.parentNode.parentNode
18551 // they get detached?
18555 Roo.View.superclass.constructor.call(this);
18560 Roo.extend(Roo.View, Roo.util.Observable, {
18563 * @cfg {Roo.data.Store} store Data store to load data from.
18568 * @cfg {String|Roo.Element} el The container element.
18573 * @cfg {String|Roo.Template} tpl The template used by this View
18577 * @cfg {String} dataName the named area of the template to use as the data area
18578 * Works with domtemplates roo-name="name"
18582 * @cfg {String} selectedClass The css class to add to selected nodes
18584 selectedClass : "x-view-selected",
18586 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18591 * @cfg {String} text to display on mask (default Loading)
18595 * @cfg {Boolean} multiSelect Allow multiple selection
18597 multiSelect : false,
18599 * @cfg {Boolean} singleSelect Allow single selection
18601 singleSelect: false,
18604 * @cfg {Boolean} toggleSelect - selecting
18606 toggleSelect : false,
18609 * @cfg {Boolean} tickable - selecting
18614 * Returns the element this view is bound to.
18615 * @return {Roo.Element}
18617 getEl : function(){
18618 return this.wrapEl;
18624 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18626 refresh : function(){
18627 //Roo.log('refresh');
18630 // if we are using something like 'domtemplate', then
18631 // the what gets used is:
18632 // t.applySubtemplate(NAME, data, wrapping data..)
18633 // the outer template then get' applied with
18634 // the store 'extra data'
18635 // and the body get's added to the
18636 // roo-name="data" node?
18637 // <span class='roo-tpl-{name}'></span> ?????
18641 this.clearSelections();
18642 this.el.update("");
18644 var records = this.store.getRange();
18645 if(records.length < 1) {
18647 // is this valid?? = should it render a template??
18649 this.el.update(this.emptyText);
18653 if (this.dataName) {
18654 this.el.update(t.apply(this.store.meta)); //????
18655 el = this.el.child('.roo-tpl-' + this.dataName);
18658 for(var i = 0, len = records.length; i < len; i++){
18659 var data = this.prepareData(records[i].data, i, records[i]);
18660 this.fireEvent("preparedata", this, data, i, records[i]);
18662 var d = Roo.apply({}, data);
18665 Roo.apply(d, {'roo-id' : Roo.id()});
18669 Roo.each(this.parent.item, function(item){
18670 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18673 Roo.apply(d, {'roo-data-checked' : 'checked'});
18677 html[html.length] = Roo.util.Format.trim(
18679 t.applySubtemplate(this.dataName, d, this.store.meta) :
18686 el.update(html.join(""));
18687 this.nodes = el.dom.childNodes;
18688 this.updateIndexes(0);
18693 * Function to override to reformat the data that is sent to
18694 * the template for each node.
18695 * DEPRICATED - use the preparedata event handler.
18696 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18697 * a JSON object for an UpdateManager bound view).
18699 prepareData : function(data, index, record)
18701 this.fireEvent("preparedata", this, data, index, record);
18705 onUpdate : function(ds, record){
18706 // Roo.log('on update');
18707 this.clearSelections();
18708 var index = this.store.indexOf(record);
18709 var n = this.nodes[index];
18710 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18711 n.parentNode.removeChild(n);
18712 this.updateIndexes(index, index);
18718 onAdd : function(ds, records, index)
18720 //Roo.log(['on Add', ds, records, index] );
18721 this.clearSelections();
18722 if(this.nodes.length == 0){
18726 var n = this.nodes[index];
18727 for(var i = 0, len = records.length; i < len; i++){
18728 var d = this.prepareData(records[i].data, i, records[i]);
18730 this.tpl.insertBefore(n, d);
18733 this.tpl.append(this.el, d);
18736 this.updateIndexes(index);
18739 onRemove : function(ds, record, index){
18740 // Roo.log('onRemove');
18741 this.clearSelections();
18742 var el = this.dataName ?
18743 this.el.child('.roo-tpl-' + this.dataName) :
18746 el.dom.removeChild(this.nodes[index]);
18747 this.updateIndexes(index);
18751 * Refresh an individual node.
18752 * @param {Number} index
18754 refreshNode : function(index){
18755 this.onUpdate(this.store, this.store.getAt(index));
18758 updateIndexes : function(startIndex, endIndex){
18759 var ns = this.nodes;
18760 startIndex = startIndex || 0;
18761 endIndex = endIndex || ns.length - 1;
18762 for(var i = startIndex; i <= endIndex; i++){
18763 ns[i].nodeIndex = i;
18768 * Changes the data store this view uses and refresh the view.
18769 * @param {Store} store
18771 setStore : function(store, initial){
18772 if(!initial && this.store){
18773 this.store.un("datachanged", this.refresh);
18774 this.store.un("add", this.onAdd);
18775 this.store.un("remove", this.onRemove);
18776 this.store.un("update", this.onUpdate);
18777 this.store.un("clear", this.refresh);
18778 this.store.un("beforeload", this.onBeforeLoad);
18779 this.store.un("load", this.onLoad);
18780 this.store.un("loadexception", this.onLoad);
18784 store.on("datachanged", this.refresh, this);
18785 store.on("add", this.onAdd, this);
18786 store.on("remove", this.onRemove, this);
18787 store.on("update", this.onUpdate, this);
18788 store.on("clear", this.refresh, this);
18789 store.on("beforeload", this.onBeforeLoad, this);
18790 store.on("load", this.onLoad, this);
18791 store.on("loadexception", this.onLoad, this);
18799 * onbeforeLoad - masks the loading area.
18802 onBeforeLoad : function(store,opts)
18804 //Roo.log('onBeforeLoad');
18806 this.el.update("");
18808 this.el.mask(this.mask ? this.mask : "Loading" );
18810 onLoad : function ()
18817 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18818 * @param {HTMLElement} node
18819 * @return {HTMLElement} The template node
18821 findItemFromChild : function(node){
18822 var el = this.dataName ?
18823 this.el.child('.roo-tpl-' + this.dataName,true) :
18826 if(!node || node.parentNode == el){
18829 var p = node.parentNode;
18830 while(p && p != el){
18831 if(p.parentNode == el){
18840 onClick : function(e){
18841 var item = this.findItemFromChild(e.getTarget());
18843 var index = this.indexOf(item);
18844 if(this.onItemClick(item, index, e) !== false){
18845 this.fireEvent("click", this, index, item, e);
18848 this.clearSelections();
18853 onContextMenu : function(e){
18854 var item = this.findItemFromChild(e.getTarget());
18856 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18861 onDblClick : function(e){
18862 var item = this.findItemFromChild(e.getTarget());
18864 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18868 onItemClick : function(item, index, e)
18870 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18873 if (this.toggleSelect) {
18874 var m = this.isSelected(item) ? 'unselect' : 'select';
18877 _t[m](item, true, false);
18880 if(this.multiSelect || this.singleSelect){
18881 if(this.multiSelect && e.shiftKey && this.lastSelection){
18882 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18884 this.select(item, this.multiSelect && e.ctrlKey);
18885 this.lastSelection = item;
18888 if(!this.tickable){
18889 e.preventDefault();
18897 * Get the number of selected nodes.
18900 getSelectionCount : function(){
18901 return this.selections.length;
18905 * Get the currently selected nodes.
18906 * @return {Array} An array of HTMLElements
18908 getSelectedNodes : function(){
18909 return this.selections;
18913 * Get the indexes of the selected nodes.
18916 getSelectedIndexes : function(){
18917 var indexes = [], s = this.selections;
18918 for(var i = 0, len = s.length; i < len; i++){
18919 indexes.push(s[i].nodeIndex);
18925 * Clear all selections
18926 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18928 clearSelections : function(suppressEvent){
18929 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18930 this.cmp.elements = this.selections;
18931 this.cmp.removeClass(this.selectedClass);
18932 this.selections = [];
18933 if(!suppressEvent){
18934 this.fireEvent("selectionchange", this, this.selections);
18940 * Returns true if the passed node is selected
18941 * @param {HTMLElement/Number} node The node or node index
18942 * @return {Boolean}
18944 isSelected : function(node){
18945 var s = this.selections;
18949 node = this.getNode(node);
18950 return s.indexOf(node) !== -1;
18955 * @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
18956 * @param {Boolean} keepExisting (optional) true to keep existing selections
18957 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18959 select : function(nodeInfo, keepExisting, suppressEvent){
18960 if(nodeInfo instanceof Array){
18962 this.clearSelections(true);
18964 for(var i = 0, len = nodeInfo.length; i < len; i++){
18965 this.select(nodeInfo[i], true, true);
18969 var node = this.getNode(nodeInfo);
18970 if(!node || this.isSelected(node)){
18971 return; // already selected.
18974 this.clearSelections(true);
18977 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18978 Roo.fly(node).addClass(this.selectedClass);
18979 this.selections.push(node);
18980 if(!suppressEvent){
18981 this.fireEvent("selectionchange", this, this.selections);
18989 * @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
18990 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18991 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18993 unselect : function(nodeInfo, keepExisting, suppressEvent)
18995 if(nodeInfo instanceof Array){
18996 Roo.each(this.selections, function(s) {
18997 this.unselect(s, nodeInfo);
19001 var node = this.getNode(nodeInfo);
19002 if(!node || !this.isSelected(node)){
19003 //Roo.log("not selected");
19004 return; // not selected.
19008 Roo.each(this.selections, function(s) {
19010 Roo.fly(node).removeClass(this.selectedClass);
19017 this.selections= ns;
19018 this.fireEvent("selectionchange", this, this.selections);
19022 * Gets a template node.
19023 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19024 * @return {HTMLElement} The node or null if it wasn't found
19026 getNode : function(nodeInfo){
19027 if(typeof nodeInfo == "string"){
19028 return document.getElementById(nodeInfo);
19029 }else if(typeof nodeInfo == "number"){
19030 return this.nodes[nodeInfo];
19036 * Gets a range template nodes.
19037 * @param {Number} startIndex
19038 * @param {Number} endIndex
19039 * @return {Array} An array of nodes
19041 getNodes : function(start, end){
19042 var ns = this.nodes;
19043 start = start || 0;
19044 end = typeof end == "undefined" ? ns.length - 1 : end;
19047 for(var i = start; i <= end; i++){
19051 for(var i = start; i >= end; i--){
19059 * Finds the index of the passed node
19060 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19061 * @return {Number} The index of the node or -1
19063 indexOf : function(node){
19064 node = this.getNode(node);
19065 if(typeof node.nodeIndex == "number"){
19066 return node.nodeIndex;
19068 var ns = this.nodes;
19069 for(var i = 0, len = ns.length; i < len; i++){
19080 * based on jquery fullcalendar
19084 Roo.bootstrap = Roo.bootstrap || {};
19086 * @class Roo.bootstrap.Calendar
19087 * @extends Roo.bootstrap.Component
19088 * Bootstrap Calendar class
19089 * @cfg {Boolean} loadMask (true|false) default false
19090 * @cfg {Object} header generate the user specific header of the calendar, default false
19093 * Create a new Container
19094 * @param {Object} config The config object
19099 Roo.bootstrap.Calendar = function(config){
19100 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19104 * Fires when a date is selected
19105 * @param {DatePicker} this
19106 * @param {Date} date The selected date
19110 * @event monthchange
19111 * Fires when the displayed month changes
19112 * @param {DatePicker} this
19113 * @param {Date} date The selected month
19115 'monthchange': true,
19117 * @event evententer
19118 * Fires when mouse over an event
19119 * @param {Calendar} this
19120 * @param {event} Event
19122 'evententer': true,
19124 * @event eventleave
19125 * Fires when the mouse leaves an
19126 * @param {Calendar} this
19129 'eventleave': true,
19131 * @event eventclick
19132 * Fires when the mouse click an
19133 * @param {Calendar} this
19142 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19145 * @cfg {Number} startDay
19146 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19154 getAutoCreate : function(){
19157 var fc_button = function(name, corner, style, content ) {
19158 return Roo.apply({},{
19160 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19162 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19165 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19176 style : 'width:100%',
19183 cls : 'fc-header-left',
19185 fc_button('prev', 'left', 'arrow', '‹' ),
19186 fc_button('next', 'right', 'arrow', '›' ),
19187 { tag: 'span', cls: 'fc-header-space' },
19188 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19196 cls : 'fc-header-center',
19200 cls: 'fc-header-title',
19203 html : 'month / year'
19211 cls : 'fc-header-right',
19213 /* fc_button('month', 'left', '', 'month' ),
19214 fc_button('week', '', '', 'week' ),
19215 fc_button('day', 'right', '', 'day' )
19227 header = this.header;
19230 var cal_heads = function() {
19232 // fixme - handle this.
19234 for (var i =0; i < Date.dayNames.length; i++) {
19235 var d = Date.dayNames[i];
19238 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19239 html : d.substring(0,3)
19243 ret[0].cls += ' fc-first';
19244 ret[6].cls += ' fc-last';
19247 var cal_cell = function(n) {
19250 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19255 cls: 'fc-day-number',
19259 cls: 'fc-day-content',
19263 style: 'position: relative;' // height: 17px;
19275 var cal_rows = function() {
19278 for (var r = 0; r < 6; r++) {
19285 for (var i =0; i < Date.dayNames.length; i++) {
19286 var d = Date.dayNames[i];
19287 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19290 row.cn[0].cls+=' fc-first';
19291 row.cn[0].cn[0].style = 'min-height:90px';
19292 row.cn[6].cls+=' fc-last';
19296 ret[0].cls += ' fc-first';
19297 ret[4].cls += ' fc-prev-last';
19298 ret[5].cls += ' fc-last';
19305 cls: 'fc-border-separate',
19306 style : 'width:100%',
19314 cls : 'fc-first fc-last',
19332 cls : 'fc-content',
19333 style : "position: relative;",
19336 cls : 'fc-view fc-view-month fc-grid',
19337 style : 'position: relative',
19338 unselectable : 'on',
19341 cls : 'fc-event-container',
19342 style : 'position:absolute;z-index:8;top:0;left:0;'
19360 initEvents : function()
19363 throw "can not find store for calendar";
19369 style: "text-align:center",
19373 style: "background-color:white;width:50%;margin:250 auto",
19377 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19388 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19390 var size = this.el.select('.fc-content', true).first().getSize();
19391 this.maskEl.setSize(size.width, size.height);
19392 this.maskEl.enableDisplayMode("block");
19393 if(!this.loadMask){
19394 this.maskEl.hide();
19397 this.store = Roo.factory(this.store, Roo.data);
19398 this.store.on('load', this.onLoad, this);
19399 this.store.on('beforeload', this.onBeforeLoad, this);
19403 this.cells = this.el.select('.fc-day',true);
19404 //Roo.log(this.cells);
19405 this.textNodes = this.el.query('.fc-day-number');
19406 this.cells.addClassOnOver('fc-state-hover');
19408 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19409 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19410 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19411 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19413 this.on('monthchange', this.onMonthChange, this);
19415 this.update(new Date().clearTime());
19418 resize : function() {
19419 var sz = this.el.getSize();
19421 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19422 this.el.select('.fc-day-content div',true).setHeight(34);
19427 showPrevMonth : function(e){
19428 this.update(this.activeDate.add("mo", -1));
19430 showToday : function(e){
19431 this.update(new Date().clearTime());
19434 showNextMonth : function(e){
19435 this.update(this.activeDate.add("mo", 1));
19439 showPrevYear : function(){
19440 this.update(this.activeDate.add("y", -1));
19444 showNextYear : function(){
19445 this.update(this.activeDate.add("y", 1));
19450 update : function(date)
19452 var vd = this.activeDate;
19453 this.activeDate = date;
19454 // if(vd && this.el){
19455 // var t = date.getTime();
19456 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19457 // Roo.log('using add remove');
19459 // this.fireEvent('monthchange', this, date);
19461 // this.cells.removeClass("fc-state-highlight");
19462 // this.cells.each(function(c){
19463 // if(c.dateValue == t){
19464 // c.addClass("fc-state-highlight");
19465 // setTimeout(function(){
19466 // try{c.dom.firstChild.focus();}catch(e){}
19476 var days = date.getDaysInMonth();
19478 var firstOfMonth = date.getFirstDateOfMonth();
19479 var startingPos = firstOfMonth.getDay()-this.startDay;
19481 if(startingPos < this.startDay){
19485 var pm = date.add(Date.MONTH, -1);
19486 var prevStart = pm.getDaysInMonth()-startingPos;
19488 this.cells = this.el.select('.fc-day',true);
19489 this.textNodes = this.el.query('.fc-day-number');
19490 this.cells.addClassOnOver('fc-state-hover');
19492 var cells = this.cells.elements;
19493 var textEls = this.textNodes;
19495 Roo.each(cells, function(cell){
19496 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19499 days += startingPos;
19501 // convert everything to numbers so it's fast
19502 var day = 86400000;
19503 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19506 //Roo.log(prevStart);
19508 var today = new Date().clearTime().getTime();
19509 var sel = date.clearTime().getTime();
19510 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19511 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19512 var ddMatch = this.disabledDatesRE;
19513 var ddText = this.disabledDatesText;
19514 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19515 var ddaysText = this.disabledDaysText;
19516 var format = this.format;
19518 var setCellClass = function(cal, cell){
19522 //Roo.log('set Cell Class');
19524 var t = d.getTime();
19528 cell.dateValue = t;
19530 cell.className += " fc-today";
19531 cell.className += " fc-state-highlight";
19532 cell.title = cal.todayText;
19535 // disable highlight in other month..
19536 //cell.className += " fc-state-highlight";
19541 cell.className = " fc-state-disabled";
19542 cell.title = cal.minText;
19546 cell.className = " fc-state-disabled";
19547 cell.title = cal.maxText;
19551 if(ddays.indexOf(d.getDay()) != -1){
19552 cell.title = ddaysText;
19553 cell.className = " fc-state-disabled";
19556 if(ddMatch && format){
19557 var fvalue = d.dateFormat(format);
19558 if(ddMatch.test(fvalue)){
19559 cell.title = ddText.replace("%0", fvalue);
19560 cell.className = " fc-state-disabled";
19564 if (!cell.initialClassName) {
19565 cell.initialClassName = cell.dom.className;
19568 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19573 for(; i < startingPos; i++) {
19574 textEls[i].innerHTML = (++prevStart);
19575 d.setDate(d.getDate()+1);
19577 cells[i].className = "fc-past fc-other-month";
19578 setCellClass(this, cells[i]);
19583 for(; i < days; i++){
19584 intDay = i - startingPos + 1;
19585 textEls[i].innerHTML = (intDay);
19586 d.setDate(d.getDate()+1);
19588 cells[i].className = ''; // "x-date-active";
19589 setCellClass(this, cells[i]);
19593 for(; i < 42; i++) {
19594 textEls[i].innerHTML = (++extraDays);
19595 d.setDate(d.getDate()+1);
19597 cells[i].className = "fc-future fc-other-month";
19598 setCellClass(this, cells[i]);
19601 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19603 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19605 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19606 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19608 if(totalRows != 6){
19609 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19610 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19613 this.fireEvent('monthchange', this, date);
19617 if(!this.internalRender){
19618 var main = this.el.dom.firstChild;
19619 var w = main.offsetWidth;
19620 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19621 Roo.fly(main).setWidth(w);
19622 this.internalRender = true;
19623 // opera does not respect the auto grow header center column
19624 // then, after it gets a width opera refuses to recalculate
19625 // without a second pass
19626 if(Roo.isOpera && !this.secondPass){
19627 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19628 this.secondPass = true;
19629 this.update.defer(10, this, [date]);
19636 findCell : function(dt) {
19637 dt = dt.clearTime().getTime();
19639 this.cells.each(function(c){
19640 //Roo.log("check " +c.dateValue + '?=' + dt);
19641 if(c.dateValue == dt){
19651 findCells : function(ev) {
19652 var s = ev.start.clone().clearTime().getTime();
19654 var e= ev.end.clone().clearTime().getTime();
19657 this.cells.each(function(c){
19658 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19660 if(c.dateValue > e){
19663 if(c.dateValue < s){
19672 // findBestRow: function(cells)
19676 // for (var i =0 ; i < cells.length;i++) {
19677 // ret = Math.max(cells[i].rows || 0,ret);
19684 addItem : function(ev)
19686 // look for vertical location slot in
19687 var cells = this.findCells(ev);
19689 // ev.row = this.findBestRow(cells);
19691 // work out the location.
19695 for(var i =0; i < cells.length; i++) {
19697 cells[i].row = cells[0].row;
19700 cells[i].row = cells[i].row + 1;
19710 if (crow.start.getY() == cells[i].getY()) {
19712 crow.end = cells[i];
19729 cells[0].events.push(ev);
19731 this.calevents.push(ev);
19734 clearEvents: function() {
19736 if(!this.calevents){
19740 Roo.each(this.cells.elements, function(c){
19746 Roo.each(this.calevents, function(e) {
19747 Roo.each(e.els, function(el) {
19748 el.un('mouseenter' ,this.onEventEnter, this);
19749 el.un('mouseleave' ,this.onEventLeave, this);
19754 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19760 renderEvents: function()
19764 this.cells.each(function(c) {
19773 if(c.row != c.events.length){
19774 r = 4 - (4 - (c.row - c.events.length));
19777 c.events = ev.slice(0, r);
19778 c.more = ev.slice(r);
19780 if(c.more.length && c.more.length == 1){
19781 c.events.push(c.more.pop());
19784 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19788 this.cells.each(function(c) {
19790 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19793 for (var e = 0; e < c.events.length; e++){
19794 var ev = c.events[e];
19795 var rows = ev.rows;
19797 for(var i = 0; i < rows.length; i++) {
19799 // how many rows should it span..
19802 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19803 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19805 unselectable : "on",
19808 cls: 'fc-event-inner',
19812 // cls: 'fc-event-time',
19813 // html : cells.length > 1 ? '' : ev.time
19817 cls: 'fc-event-title',
19818 html : String.format('{0}', ev.title)
19825 cls: 'ui-resizable-handle ui-resizable-e',
19826 html : '  '
19833 cfg.cls += ' fc-event-start';
19835 if ((i+1) == rows.length) {
19836 cfg.cls += ' fc-event-end';
19839 var ctr = _this.el.select('.fc-event-container',true).first();
19840 var cg = ctr.createChild(cfg);
19842 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19843 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19845 var r = (c.more.length) ? 1 : 0;
19846 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19847 cg.setWidth(ebox.right - sbox.x -2);
19849 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19850 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19851 cg.on('click', _this.onEventClick, _this, ev);
19862 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19863 style : 'position: absolute',
19864 unselectable : "on",
19867 cls: 'fc-event-inner',
19871 cls: 'fc-event-title',
19879 cls: 'ui-resizable-handle ui-resizable-e',
19880 html : '  '
19886 var ctr = _this.el.select('.fc-event-container',true).first();
19887 var cg = ctr.createChild(cfg);
19889 var sbox = c.select('.fc-day-content',true).first().getBox();
19890 var ebox = c.select('.fc-day-content',true).first().getBox();
19892 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19893 cg.setWidth(ebox.right - sbox.x -2);
19895 cg.on('click', _this.onMoreEventClick, _this, c.more);
19905 onEventEnter: function (e, el,event,d) {
19906 this.fireEvent('evententer', this, el, event);
19909 onEventLeave: function (e, el,event,d) {
19910 this.fireEvent('eventleave', this, el, event);
19913 onEventClick: function (e, el,event,d) {
19914 this.fireEvent('eventclick', this, el, event);
19917 onMonthChange: function () {
19921 onMoreEventClick: function(e, el, more)
19925 this.calpopover.placement = 'right';
19926 this.calpopover.setTitle('More');
19928 this.calpopover.setContent('');
19930 var ctr = this.calpopover.el.select('.popover-content', true).first();
19932 Roo.each(more, function(m){
19934 cls : 'fc-event-hori fc-event-draggable',
19937 var cg = ctr.createChild(cfg);
19939 cg.on('click', _this.onEventClick, _this, m);
19942 this.calpopover.show(el);
19947 onLoad: function ()
19949 this.calevents = [];
19952 if(this.store.getCount() > 0){
19953 this.store.data.each(function(d){
19956 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19957 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19958 time : d.data.start_time,
19959 title : d.data.title,
19960 description : d.data.description,
19961 venue : d.data.venue
19966 this.renderEvents();
19968 if(this.calevents.length && this.loadMask){
19969 this.maskEl.hide();
19973 onBeforeLoad: function()
19975 this.clearEvents();
19977 this.maskEl.show();
19991 * @class Roo.bootstrap.Popover
19992 * @extends Roo.bootstrap.Component
19993 * Bootstrap Popover class
19994 * @cfg {String} html contents of the popover (or false to use children..)
19995 * @cfg {String} title of popover (or false to hide)
19996 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19997 * @cfg {String} trigger click || hover (or false to trigger manually)
19998 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19999 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20000 * - if false and it has a 'parent' then it will be automatically added to that element
20001 * - if string - Roo.get will be called
20002 * @cfg {Number} delay - delay before showing
20005 * Create a new Popover
20006 * @param {Object} config The config object
20009 Roo.bootstrap.Popover = function(config){
20010 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20016 * After the popover show
20018 * @param {Roo.bootstrap.Popover} this
20023 * After the popover hide
20025 * @param {Roo.bootstrap.Popover} this
20031 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20036 placement : 'right',
20037 trigger : 'hover', // hover
20043 can_build_overlaid : false,
20045 maskEl : false, // the mask element
20048 alignEl : false, // when show is called with an element - this get's stored.
20050 getChildContainer : function()
20052 return this.contentEl;
20055 getPopoverHeader : function()
20057 this.title = true; // flag not to hide it..
20058 this.headerEl.addClass('p-0');
20059 return this.headerEl
20063 getAutoCreate : function(){
20066 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20067 style: 'display:block',
20073 cls : 'popover-inner ',
20077 cls: 'popover-title popover-header',
20078 html : this.title === false ? '' : this.title
20081 cls : 'popover-content popover-body ' + (this.cls || ''),
20082 html : this.html || ''
20093 * @param {string} the title
20095 setTitle: function(str)
20099 this.headerEl.dom.innerHTML = str;
20104 * @param {string} the body content
20106 setContent: function(str)
20109 if (this.contentEl) {
20110 this.contentEl.dom.innerHTML = str;
20114 // as it get's added to the bottom of the page.
20115 onRender : function(ct, position)
20117 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20122 var cfg = Roo.apply({}, this.getAutoCreate());
20126 cfg.cls += ' ' + this.cls;
20129 cfg.style = this.style;
20131 //Roo.log("adding to ");
20132 this.el = Roo.get(document.body).createChild(cfg, position);
20133 // Roo.log(this.el);
20136 this.contentEl = this.el.select('.popover-content',true).first();
20137 this.headerEl = this.el.select('.popover-title',true).first();
20140 if(typeof(this.items) != 'undefined'){
20141 var items = this.items;
20144 for(var i =0;i < items.length;i++) {
20145 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20149 this.items = nitems;
20151 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20152 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20159 resizeMask : function()
20161 this.maskEl.setSize(
20162 Roo.lib.Dom.getViewWidth(true),
20163 Roo.lib.Dom.getViewHeight(true)
20167 initEvents : function()
20171 Roo.bootstrap.Popover.register(this);
20174 this.arrowEl = this.el.select('.arrow',true).first();
20175 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20176 this.el.enableDisplayMode('block');
20180 if (this.over === false && !this.parent()) {
20183 if (this.triggers === false) {
20188 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20189 var triggers = this.trigger ? this.trigger.split(' ') : [];
20190 Roo.each(triggers, function(trigger) {
20192 if (trigger == 'click') {
20193 on_el.on('click', this.toggle, this);
20194 } else if (trigger != 'manual') {
20195 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20196 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20198 on_el.on(eventIn ,this.enter, this);
20199 on_el.on(eventOut, this.leave, this);
20209 toggle : function () {
20210 this.hoverState == 'in' ? this.leave() : this.enter();
20213 enter : function () {
20215 clearTimeout(this.timeout);
20217 this.hoverState = 'in';
20219 if (!this.delay || !this.delay.show) {
20224 this.timeout = setTimeout(function () {
20225 if (_t.hoverState == 'in') {
20228 }, this.delay.show)
20231 leave : function() {
20232 clearTimeout(this.timeout);
20234 this.hoverState = 'out';
20236 if (!this.delay || !this.delay.hide) {
20241 this.timeout = setTimeout(function () {
20242 if (_t.hoverState == 'out') {
20245 }, this.delay.hide)
20249 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20250 * @param {string} (left|right|top|bottom) position
20252 show : function (on_el, placement)
20254 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20255 on_el = on_el || false; // default to false
20258 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20259 on_el = this.parent().el;
20260 } else if (this.over) {
20261 on_el = Roo.get(this.over);
20266 this.alignEl = Roo.get( on_el );
20269 this.render(document.body);
20275 if (this.title === false) {
20276 this.headerEl.hide();
20281 this.el.dom.style.display = 'block';
20284 if (this.alignEl) {
20285 this.updatePosition(this.placement, true);
20288 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20289 var es = this.el.getSize();
20290 var x = Roo.lib.Dom.getViewWidth()/2;
20291 var y = Roo.lib.Dom.getViewHeight()/2;
20292 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20297 //var arrow = this.el.select('.arrow',true).first();
20298 //arrow.set(align[2],
20300 this.el.addClass('in');
20304 this.hoverState = 'in';
20307 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20308 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20309 this.maskEl.dom.style.display = 'block';
20310 this.maskEl.addClass('show');
20312 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20314 this.fireEvent('show', this);
20318 * fire this manually after loading a grid in the table for example
20319 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20320 * @param {Boolean} try and move it if we cant get right position.
20322 updatePosition : function(placement, try_move)
20324 // allow for calling with no parameters
20325 placement = placement ? placement : this.placement;
20326 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20328 this.el.removeClass([
20329 'fade','top','bottom', 'left', 'right','in',
20330 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20332 this.el.addClass(placement + ' bs-popover-' + placement);
20334 if (!this.alignEl ) {
20338 switch (placement) {
20340 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20341 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20342 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20343 //normal display... or moved up/down.
20344 this.el.setXY(offset);
20345 var xy = this.alignEl.getAnchorXY('tr', false);
20347 this.arrowEl.setXY(xy);
20350 // continue through...
20351 return this.updatePosition('left', false);
20355 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20356 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20357 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20358 //normal display... or moved up/down.
20359 this.el.setXY(offset);
20360 var xy = this.alignEl.getAnchorXY('tl', false);
20361 xy[0]-=10;xy[1]+=5; // << fix me
20362 this.arrowEl.setXY(xy);
20366 return this.updatePosition('right', false);
20369 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20370 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20371 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20372 //normal display... or moved up/down.
20373 this.el.setXY(offset);
20374 var xy = this.alignEl.getAnchorXY('t', false);
20375 xy[1]-=10; // << fix me
20376 this.arrowEl.setXY(xy);
20380 return this.updatePosition('bottom', false);
20383 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20384 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20385 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20386 //normal display... or moved up/down.
20387 this.el.setXY(offset);
20388 var xy = this.alignEl.getAnchorXY('b', false);
20389 xy[1]+=2; // << fix me
20390 this.arrowEl.setXY(xy);
20394 return this.updatePosition('top', false);
20405 this.el.setXY([0,0]);
20406 this.el.removeClass('in');
20408 this.hoverState = null;
20409 this.maskEl.hide(); // always..
20410 this.fireEvent('hide', this);
20416 Roo.apply(Roo.bootstrap.Popover, {
20419 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20420 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20421 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20422 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20427 clickHander : false,
20431 onMouseDown : function(e)
20433 if (this.popups.length && !e.getTarget(".roo-popover")) {
20434 /// what is nothing is showing..
20443 register : function(popup)
20445 if (!Roo.bootstrap.Popover.clickHandler) {
20446 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20448 // hide other popups.
20449 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20450 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20451 this.hideAll(); //<< why?
20452 //this.popups.push(popup);
20454 hideAll : function()
20456 this.popups.forEach(function(p) {
20460 onShow : function() {
20461 Roo.bootstrap.Popover.popups.push(this);
20463 onHide : function() {
20464 Roo.bootstrap.Popover.popups.remove(this);
20470 * Card header - holder for the card header elements.
20475 * @class Roo.bootstrap.PopoverNav
20476 * @extends Roo.bootstrap.NavGroup
20477 * Bootstrap Popover header navigation class
20479 * Create a new Popover Header Navigation
20480 * @param {Object} config The config object
20483 Roo.bootstrap.PopoverNav = function(config){
20484 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20487 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20490 container_method : 'getPopoverHeader'
20508 * @class Roo.bootstrap.Progress
20509 * @extends Roo.bootstrap.Component
20510 * Bootstrap Progress class
20511 * @cfg {Boolean} striped striped of the progress bar
20512 * @cfg {Boolean} active animated of the progress bar
20516 * Create a new Progress
20517 * @param {Object} config The config object
20520 Roo.bootstrap.Progress = function(config){
20521 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20524 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20529 getAutoCreate : function(){
20537 cfg.cls += ' progress-striped';
20541 cfg.cls += ' active';
20560 * @class Roo.bootstrap.ProgressBar
20561 * @extends Roo.bootstrap.Component
20562 * Bootstrap ProgressBar class
20563 * @cfg {Number} aria_valuenow aria-value now
20564 * @cfg {Number} aria_valuemin aria-value min
20565 * @cfg {Number} aria_valuemax aria-value max
20566 * @cfg {String} label label for the progress bar
20567 * @cfg {String} panel (success | info | warning | danger )
20568 * @cfg {String} role role of the progress bar
20569 * @cfg {String} sr_only text
20573 * Create a new ProgressBar
20574 * @param {Object} config The config object
20577 Roo.bootstrap.ProgressBar = function(config){
20578 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20581 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20585 aria_valuemax : 100,
20591 getAutoCreate : function()
20596 cls: 'progress-bar',
20597 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20609 cfg.role = this.role;
20612 if(this.aria_valuenow){
20613 cfg['aria-valuenow'] = this.aria_valuenow;
20616 if(this.aria_valuemin){
20617 cfg['aria-valuemin'] = this.aria_valuemin;
20620 if(this.aria_valuemax){
20621 cfg['aria-valuemax'] = this.aria_valuemax;
20624 if(this.label && !this.sr_only){
20625 cfg.html = this.label;
20629 cfg.cls += ' progress-bar-' + this.panel;
20635 update : function(aria_valuenow)
20637 this.aria_valuenow = aria_valuenow;
20639 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20654 * @class Roo.bootstrap.TabGroup
20655 * @extends Roo.bootstrap.Column
20656 * Bootstrap Column class
20657 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20658 * @cfg {Boolean} carousel true to make the group behave like a carousel
20659 * @cfg {Boolean} bullets show bullets for the panels
20660 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20661 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20662 * @cfg {Boolean} showarrow (true|false) show arrow default true
20665 * Create a new TabGroup
20666 * @param {Object} config The config object
20669 Roo.bootstrap.TabGroup = function(config){
20670 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20672 this.navId = Roo.id();
20675 Roo.bootstrap.TabGroup.register(this);
20679 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20682 transition : false,
20687 slideOnTouch : false,
20690 getAutoCreate : function()
20692 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20694 cfg.cls += ' tab-content';
20696 if (this.carousel) {
20697 cfg.cls += ' carousel slide';
20700 cls : 'carousel-inner',
20704 if(this.bullets && !Roo.isTouch){
20707 cls : 'carousel-bullets',
20711 if(this.bullets_cls){
20712 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20719 cfg.cn[0].cn.push(bullets);
20722 if(this.showarrow){
20723 cfg.cn[0].cn.push({
20725 class : 'carousel-arrow',
20729 class : 'carousel-prev',
20733 class : 'fa fa-chevron-left'
20739 class : 'carousel-next',
20743 class : 'fa fa-chevron-right'
20756 initEvents: function()
20758 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20759 // this.el.on("touchstart", this.onTouchStart, this);
20762 if(this.autoslide){
20765 this.slideFn = window.setInterval(function() {
20766 _this.showPanelNext();
20770 if(this.showarrow){
20771 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20772 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20778 // onTouchStart : function(e, el, o)
20780 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20784 // this.showPanelNext();
20788 getChildContainer : function()
20790 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20794 * register a Navigation item
20795 * @param {Roo.bootstrap.NavItem} the navitem to add
20797 register : function(item)
20799 this.tabs.push( item);
20800 item.navId = this.navId; // not really needed..
20805 getActivePanel : function()
20808 Roo.each(this.tabs, function(t) {
20818 getPanelByName : function(n)
20821 Roo.each(this.tabs, function(t) {
20822 if (t.tabId == n) {
20830 indexOfPanel : function(p)
20833 Roo.each(this.tabs, function(t,i) {
20834 if (t.tabId == p.tabId) {
20843 * show a specific panel
20844 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20845 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20847 showPanel : function (pan)
20849 if(this.transition || typeof(pan) == 'undefined'){
20850 Roo.log("waiting for the transitionend");
20854 if (typeof(pan) == 'number') {
20855 pan = this.tabs[pan];
20858 if (typeof(pan) == 'string') {
20859 pan = this.getPanelByName(pan);
20862 var cur = this.getActivePanel();
20865 Roo.log('pan or acitve pan is undefined');
20869 if (pan.tabId == this.getActivePanel().tabId) {
20873 if (false === cur.fireEvent('beforedeactivate')) {
20877 if(this.bullets > 0 && !Roo.isTouch){
20878 this.setActiveBullet(this.indexOfPanel(pan));
20881 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20883 //class="carousel-item carousel-item-next carousel-item-left"
20885 this.transition = true;
20886 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20887 var lr = dir == 'next' ? 'left' : 'right';
20888 pan.el.addClass(dir); // or prev
20889 pan.el.addClass('carousel-item-' + dir); // or prev
20890 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20891 cur.el.addClass(lr); // or right
20892 pan.el.addClass(lr);
20893 cur.el.addClass('carousel-item-' +lr); // or right
20894 pan.el.addClass('carousel-item-' +lr);
20898 cur.el.on('transitionend', function() {
20899 Roo.log("trans end?");
20901 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20902 pan.setActive(true);
20904 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20905 cur.setActive(false);
20907 _this.transition = false;
20909 }, this, { single: true } );
20914 cur.setActive(false);
20915 pan.setActive(true);
20920 showPanelNext : function()
20922 var i = this.indexOfPanel(this.getActivePanel());
20924 if (i >= this.tabs.length - 1 && !this.autoslide) {
20928 if (i >= this.tabs.length - 1 && this.autoslide) {
20932 this.showPanel(this.tabs[i+1]);
20935 showPanelPrev : function()
20937 var i = this.indexOfPanel(this.getActivePanel());
20939 if (i < 1 && !this.autoslide) {
20943 if (i < 1 && this.autoslide) {
20944 i = this.tabs.length;
20947 this.showPanel(this.tabs[i-1]);
20951 addBullet: function()
20953 if(!this.bullets || Roo.isTouch){
20956 var ctr = this.el.select('.carousel-bullets',true).first();
20957 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20958 var bullet = ctr.createChild({
20959 cls : 'bullet bullet-' + i
20960 },ctr.dom.lastChild);
20965 bullet.on('click', (function(e, el, o, ii, t){
20967 e.preventDefault();
20969 this.showPanel(ii);
20971 if(this.autoslide && this.slideFn){
20972 clearInterval(this.slideFn);
20973 this.slideFn = window.setInterval(function() {
20974 _this.showPanelNext();
20978 }).createDelegate(this, [i, bullet], true));
20983 setActiveBullet : function(i)
20989 Roo.each(this.el.select('.bullet', true).elements, function(el){
20990 el.removeClass('selected');
20993 var bullet = this.el.select('.bullet-' + i, true).first();
20999 bullet.addClass('selected');
21010 Roo.apply(Roo.bootstrap.TabGroup, {
21014 * register a Navigation Group
21015 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21017 register : function(navgrp)
21019 this.groups[navgrp.navId] = navgrp;
21023 * fetch a Navigation Group based on the navigation ID
21024 * if one does not exist , it will get created.
21025 * @param {string} the navgroup to add
21026 * @returns {Roo.bootstrap.NavGroup} the navgroup
21028 get: function(navId) {
21029 if (typeof(this.groups[navId]) == 'undefined') {
21030 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21032 return this.groups[navId] ;
21047 * @class Roo.bootstrap.TabPanel
21048 * @extends Roo.bootstrap.Component
21049 * Bootstrap TabPanel class
21050 * @cfg {Boolean} active panel active
21051 * @cfg {String} html panel content
21052 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21053 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21054 * @cfg {String} href click to link..
21055 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21059 * Create a new TabPanel
21060 * @param {Object} config The config object
21063 Roo.bootstrap.TabPanel = function(config){
21064 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21068 * Fires when the active status changes
21069 * @param {Roo.bootstrap.TabPanel} this
21070 * @param {Boolean} state the new state
21075 * @event beforedeactivate
21076 * Fires before a tab is de-activated - can be used to do validation on a form.
21077 * @param {Roo.bootstrap.TabPanel} this
21078 * @return {Boolean} false if there is an error
21081 'beforedeactivate': true
21084 this.tabId = this.tabId || Roo.id();
21088 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21095 touchSlide : false,
21096 getAutoCreate : function(){
21101 // item is needed for carousel - not sure if it has any effect otherwise
21102 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21103 html: this.html || ''
21107 cfg.cls += ' active';
21111 cfg.tabId = this.tabId;
21119 initEvents: function()
21121 var p = this.parent();
21123 this.navId = this.navId || p.navId;
21125 if (typeof(this.navId) != 'undefined') {
21126 // not really needed.. but just in case.. parent should be a NavGroup.
21127 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21131 var i = tg.tabs.length - 1;
21133 if(this.active && tg.bullets > 0 && i < tg.bullets){
21134 tg.setActiveBullet(i);
21138 this.el.on('click', this.onClick, this);
21140 if(Roo.isTouch && this.touchSlide){
21141 this.el.on("touchstart", this.onTouchStart, this);
21142 this.el.on("touchmove", this.onTouchMove, this);
21143 this.el.on("touchend", this.onTouchEnd, this);
21148 onRender : function(ct, position)
21150 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21153 setActive : function(state)
21155 Roo.log("panel - set active " + this.tabId + "=" + state);
21157 this.active = state;
21159 this.el.removeClass('active');
21161 } else if (!this.el.hasClass('active')) {
21162 this.el.addClass('active');
21165 this.fireEvent('changed', this, state);
21168 onClick : function(e)
21170 e.preventDefault();
21172 if(!this.href.length){
21176 window.location.href = this.href;
21185 onTouchStart : function(e)
21187 this.swiping = false;
21189 this.startX = e.browserEvent.touches[0].clientX;
21190 this.startY = e.browserEvent.touches[0].clientY;
21193 onTouchMove : function(e)
21195 this.swiping = true;
21197 this.endX = e.browserEvent.touches[0].clientX;
21198 this.endY = e.browserEvent.touches[0].clientY;
21201 onTouchEnd : function(e)
21208 var tabGroup = this.parent();
21210 if(this.endX > this.startX){ // swiping right
21211 tabGroup.showPanelPrev();
21215 if(this.startX > this.endX){ // swiping left
21216 tabGroup.showPanelNext();
21235 * @class Roo.bootstrap.DateField
21236 * @extends Roo.bootstrap.Input
21237 * Bootstrap DateField class
21238 * @cfg {Number} weekStart default 0
21239 * @cfg {String} viewMode default empty, (months|years)
21240 * @cfg {String} minViewMode default empty, (months|years)
21241 * @cfg {Number} startDate default -Infinity
21242 * @cfg {Number} endDate default Infinity
21243 * @cfg {Boolean} todayHighlight default false
21244 * @cfg {Boolean} todayBtn default false
21245 * @cfg {Boolean} calendarWeeks default false
21246 * @cfg {Object} daysOfWeekDisabled default empty
21247 * @cfg {Boolean} singleMode default false (true | false)
21249 * @cfg {Boolean} keyboardNavigation default true
21250 * @cfg {String} language default en
21253 * Create a new DateField
21254 * @param {Object} config The config object
21257 Roo.bootstrap.DateField = function(config){
21258 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21262 * Fires when this field show.
21263 * @param {Roo.bootstrap.DateField} this
21264 * @param {Mixed} date The date value
21269 * Fires when this field hide.
21270 * @param {Roo.bootstrap.DateField} this
21271 * @param {Mixed} date The date value
21276 * Fires when select a date.
21277 * @param {Roo.bootstrap.DateField} this
21278 * @param {Mixed} date The date value
21282 * @event beforeselect
21283 * Fires when before select a date.
21284 * @param {Roo.bootstrap.DateField} this
21285 * @param {Mixed} date The date value
21287 beforeselect : true
21291 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21294 * @cfg {String} format
21295 * The default date format string which can be overriden for localization support. The format must be
21296 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21300 * @cfg {String} altFormats
21301 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21302 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21304 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21312 todayHighlight : false,
21318 keyboardNavigation: true,
21320 calendarWeeks: false,
21322 startDate: -Infinity,
21326 daysOfWeekDisabled: [],
21330 singleMode : false,
21332 UTCDate: function()
21334 return new Date(Date.UTC.apply(Date, arguments));
21337 UTCToday: function()
21339 var today = new Date();
21340 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21343 getDate: function() {
21344 var d = this.getUTCDate();
21345 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21348 getUTCDate: function() {
21352 setDate: function(d) {
21353 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21356 setUTCDate: function(d) {
21358 this.setValue(this.formatDate(this.date));
21361 onRender: function(ct, position)
21364 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21366 this.language = this.language || 'en';
21367 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21368 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21370 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21371 this.format = this.format || 'm/d/y';
21372 this.isInline = false;
21373 this.isInput = true;
21374 this.component = this.el.select('.add-on', true).first() || false;
21375 this.component = (this.component && this.component.length === 0) ? false : this.component;
21376 this.hasInput = this.component && this.inputEl().length;
21378 if (typeof(this.minViewMode === 'string')) {
21379 switch (this.minViewMode) {
21381 this.minViewMode = 1;
21384 this.minViewMode = 2;
21387 this.minViewMode = 0;
21392 if (typeof(this.viewMode === 'string')) {
21393 switch (this.viewMode) {
21406 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21408 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21410 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21412 this.picker().on('mousedown', this.onMousedown, this);
21413 this.picker().on('click', this.onClick, this);
21415 this.picker().addClass('datepicker-dropdown');
21417 this.startViewMode = this.viewMode;
21419 if(this.singleMode){
21420 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21421 v.setVisibilityMode(Roo.Element.DISPLAY);
21425 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21426 v.setStyle('width', '189px');
21430 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21431 if(!this.calendarWeeks){
21436 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21437 v.attr('colspan', function(i, val){
21438 return parseInt(val) + 1;
21443 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21445 this.setStartDate(this.startDate);
21446 this.setEndDate(this.endDate);
21448 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21455 if(this.isInline) {
21460 picker : function()
21462 return this.pickerEl;
21463 // return this.el.select('.datepicker', true).first();
21466 fillDow: function()
21468 var dowCnt = this.weekStart;
21477 if(this.calendarWeeks){
21485 while (dowCnt < this.weekStart + 7) {
21489 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21493 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21496 fillMonths: function()
21499 var months = this.picker().select('>.datepicker-months td', true).first();
21501 months.dom.innerHTML = '';
21507 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21510 months.createChild(month);
21517 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;
21519 if (this.date < this.startDate) {
21520 this.viewDate = new Date(this.startDate);
21521 } else if (this.date > this.endDate) {
21522 this.viewDate = new Date(this.endDate);
21524 this.viewDate = new Date(this.date);
21532 var d = new Date(this.viewDate),
21533 year = d.getUTCFullYear(),
21534 month = d.getUTCMonth(),
21535 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21536 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21537 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21538 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21539 currentDate = this.date && this.date.valueOf(),
21540 today = this.UTCToday();
21542 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21544 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21546 // this.picker.select('>tfoot th.today').
21547 // .text(dates[this.language].today)
21548 // .toggle(this.todayBtn !== false);
21550 this.updateNavArrows();
21553 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21555 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21557 prevMonth.setUTCDate(day);
21559 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21561 var nextMonth = new Date(prevMonth);
21563 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21565 nextMonth = nextMonth.valueOf();
21567 var fillMonths = false;
21569 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21571 while(prevMonth.valueOf() <= nextMonth) {
21574 if (prevMonth.getUTCDay() === this.weekStart) {
21576 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21584 if(this.calendarWeeks){
21585 // ISO 8601: First week contains first thursday.
21586 // ISO also states week starts on Monday, but we can be more abstract here.
21588 // Start of current week: based on weekstart/current date
21589 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21590 // Thursday of this week
21591 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21592 // First Thursday of year, year from thursday
21593 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21594 // Calendar week: ms between thursdays, div ms per day, div 7 days
21595 calWeek = (th - yth) / 864e5 / 7 + 1;
21597 fillMonths.cn.push({
21605 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21607 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21610 if (this.todayHighlight &&
21611 prevMonth.getUTCFullYear() == today.getFullYear() &&
21612 prevMonth.getUTCMonth() == today.getMonth() &&
21613 prevMonth.getUTCDate() == today.getDate()) {
21614 clsName += ' today';
21617 if (currentDate && prevMonth.valueOf() === currentDate) {
21618 clsName += ' active';
21621 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21622 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21623 clsName += ' disabled';
21626 fillMonths.cn.push({
21628 cls: 'day ' + clsName,
21629 html: prevMonth.getDate()
21632 prevMonth.setDate(prevMonth.getDate()+1);
21635 var currentYear = this.date && this.date.getUTCFullYear();
21636 var currentMonth = this.date && this.date.getUTCMonth();
21638 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21640 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21641 v.removeClass('active');
21643 if(currentYear === year && k === currentMonth){
21644 v.addClass('active');
21647 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21648 v.addClass('disabled');
21654 year = parseInt(year/10, 10) * 10;
21656 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21658 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21661 for (var i = -1; i < 11; i++) {
21662 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21664 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21672 showMode: function(dir)
21675 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21678 Roo.each(this.picker().select('>div',true).elements, function(v){
21679 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21682 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21687 if(this.isInline) {
21691 this.picker().removeClass(['bottom', 'top']);
21693 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21695 * place to the top of element!
21699 this.picker().addClass('top');
21700 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21705 this.picker().addClass('bottom');
21707 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21710 parseDate : function(value)
21712 if(!value || value instanceof Date){
21715 var v = Date.parseDate(value, this.format);
21716 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21717 v = Date.parseDate(value, 'Y-m-d');
21719 if(!v && this.altFormats){
21720 if(!this.altFormatsArray){
21721 this.altFormatsArray = this.altFormats.split("|");
21723 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21724 v = Date.parseDate(value, this.altFormatsArray[i]);
21730 formatDate : function(date, fmt)
21732 return (!date || !(date instanceof Date)) ?
21733 date : date.dateFormat(fmt || this.format);
21736 onFocus : function()
21738 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21742 onBlur : function()
21744 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21746 var d = this.inputEl().getValue();
21753 showPopup : function()
21755 this.picker().show();
21759 this.fireEvent('showpopup', this, this.date);
21762 hidePopup : function()
21764 if(this.isInline) {
21767 this.picker().hide();
21768 this.viewMode = this.startViewMode;
21771 this.fireEvent('hidepopup', this, this.date);
21775 onMousedown: function(e)
21777 e.stopPropagation();
21778 e.preventDefault();
21783 Roo.bootstrap.DateField.superclass.keyup.call(this);
21787 setValue: function(v)
21789 if(this.fireEvent('beforeselect', this, v) !== false){
21790 var d = new Date(this.parseDate(v) ).clearTime();
21792 if(isNaN(d.getTime())){
21793 this.date = this.viewDate = '';
21794 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21798 v = this.formatDate(d);
21800 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21802 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21806 this.fireEvent('select', this, this.date);
21810 getValue: function()
21812 return this.formatDate(this.date);
21815 fireKey: function(e)
21817 if (!this.picker().isVisible()){
21818 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21824 var dateChanged = false,
21826 newDate, newViewDate;
21831 e.preventDefault();
21835 if (!this.keyboardNavigation) {
21838 dir = e.keyCode == 37 ? -1 : 1;
21841 newDate = this.moveYear(this.date, dir);
21842 newViewDate = this.moveYear(this.viewDate, dir);
21843 } else if (e.shiftKey){
21844 newDate = this.moveMonth(this.date, dir);
21845 newViewDate = this.moveMonth(this.viewDate, dir);
21847 newDate = new Date(this.date);
21848 newDate.setUTCDate(this.date.getUTCDate() + dir);
21849 newViewDate = new Date(this.viewDate);
21850 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21852 if (this.dateWithinRange(newDate)){
21853 this.date = newDate;
21854 this.viewDate = newViewDate;
21855 this.setValue(this.formatDate(this.date));
21857 e.preventDefault();
21858 dateChanged = true;
21863 if (!this.keyboardNavigation) {
21866 dir = e.keyCode == 38 ? -1 : 1;
21868 newDate = this.moveYear(this.date, dir);
21869 newViewDate = this.moveYear(this.viewDate, dir);
21870 } else if (e.shiftKey){
21871 newDate = this.moveMonth(this.date, dir);
21872 newViewDate = this.moveMonth(this.viewDate, dir);
21874 newDate = new Date(this.date);
21875 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21876 newViewDate = new Date(this.viewDate);
21877 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21879 if (this.dateWithinRange(newDate)){
21880 this.date = newDate;
21881 this.viewDate = newViewDate;
21882 this.setValue(this.formatDate(this.date));
21884 e.preventDefault();
21885 dateChanged = true;
21889 this.setValue(this.formatDate(this.date));
21891 e.preventDefault();
21894 this.setValue(this.formatDate(this.date));
21908 onClick: function(e)
21910 e.stopPropagation();
21911 e.preventDefault();
21913 var target = e.getTarget();
21915 if(target.nodeName.toLowerCase() === 'i'){
21916 target = Roo.get(target).dom.parentNode;
21919 var nodeName = target.nodeName;
21920 var className = target.className;
21921 var html = target.innerHTML;
21922 //Roo.log(nodeName);
21924 switch(nodeName.toLowerCase()) {
21926 switch(className) {
21932 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21933 switch(this.viewMode){
21935 this.viewDate = this.moveMonth(this.viewDate, dir);
21939 this.viewDate = this.moveYear(this.viewDate, dir);
21945 var date = new Date();
21946 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21948 this.setValue(this.formatDate(this.date));
21955 if (className.indexOf('disabled') < 0) {
21956 if (!this.viewDate) {
21957 this.viewDate = new Date();
21959 this.viewDate.setUTCDate(1);
21960 if (className.indexOf('month') > -1) {
21961 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21963 var year = parseInt(html, 10) || 0;
21964 this.viewDate.setUTCFullYear(year);
21968 if(this.singleMode){
21969 this.setValue(this.formatDate(this.viewDate));
21980 //Roo.log(className);
21981 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21982 var day = parseInt(html, 10) || 1;
21983 var year = (this.viewDate || new Date()).getUTCFullYear(),
21984 month = (this.viewDate || new Date()).getUTCMonth();
21986 if (className.indexOf('old') > -1) {
21993 } else if (className.indexOf('new') > -1) {
22001 //Roo.log([year,month,day]);
22002 this.date = this.UTCDate(year, month, day,0,0,0,0);
22003 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22005 //Roo.log(this.formatDate(this.date));
22006 this.setValue(this.formatDate(this.date));
22013 setStartDate: function(startDate)
22015 this.startDate = startDate || -Infinity;
22016 if (this.startDate !== -Infinity) {
22017 this.startDate = this.parseDate(this.startDate);
22020 this.updateNavArrows();
22023 setEndDate: function(endDate)
22025 this.endDate = endDate || Infinity;
22026 if (this.endDate !== Infinity) {
22027 this.endDate = this.parseDate(this.endDate);
22030 this.updateNavArrows();
22033 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22035 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22036 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22037 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22039 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22040 return parseInt(d, 10);
22043 this.updateNavArrows();
22046 updateNavArrows: function()
22048 if(this.singleMode){
22052 var d = new Date(this.viewDate),
22053 year = d.getUTCFullYear(),
22054 month = d.getUTCMonth();
22056 Roo.each(this.picker().select('.prev', true).elements, function(v){
22058 switch (this.viewMode) {
22061 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22067 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22074 Roo.each(this.picker().select('.next', true).elements, function(v){
22076 switch (this.viewMode) {
22079 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22085 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22093 moveMonth: function(date, dir)
22098 var new_date = new Date(date.valueOf()),
22099 day = new_date.getUTCDate(),
22100 month = new_date.getUTCMonth(),
22101 mag = Math.abs(dir),
22103 dir = dir > 0 ? 1 : -1;
22106 // If going back one month, make sure month is not current month
22107 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22109 return new_date.getUTCMonth() == month;
22111 // If going forward one month, make sure month is as expected
22112 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22114 return new_date.getUTCMonth() != new_month;
22116 new_month = month + dir;
22117 new_date.setUTCMonth(new_month);
22118 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22119 if (new_month < 0 || new_month > 11) {
22120 new_month = (new_month + 12) % 12;
22123 // For magnitudes >1, move one month at a time...
22124 for (var i=0; i<mag; i++) {
22125 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22126 new_date = this.moveMonth(new_date, dir);
22128 // ...then reset the day, keeping it in the new month
22129 new_month = new_date.getUTCMonth();
22130 new_date.setUTCDate(day);
22132 return new_month != new_date.getUTCMonth();
22135 // Common date-resetting loop -- if date is beyond end of month, make it
22138 new_date.setUTCDate(--day);
22139 new_date.setUTCMonth(new_month);
22144 moveYear: function(date, dir)
22146 return this.moveMonth(date, dir*12);
22149 dateWithinRange: function(date)
22151 return date >= this.startDate && date <= this.endDate;
22157 this.picker().remove();
22160 validateValue : function(value)
22162 if(this.getVisibilityEl().hasClass('hidden')){
22166 if(value.length < 1) {
22167 if(this.allowBlank){
22173 if(value.length < this.minLength){
22176 if(value.length > this.maxLength){
22180 var vt = Roo.form.VTypes;
22181 if(!vt[this.vtype](value, this)){
22185 if(typeof this.validator == "function"){
22186 var msg = this.validator(value);
22192 if(this.regex && !this.regex.test(value)){
22196 if(typeof(this.parseDate(value)) == 'undefined'){
22200 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22204 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22214 this.date = this.viewDate = '';
22216 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22221 Roo.apply(Roo.bootstrap.DateField, {
22232 html: '<i class="fa fa-arrow-left"/>'
22242 html: '<i class="fa fa-arrow-right"/>'
22284 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22285 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22286 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22287 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22288 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22301 navFnc: 'FullYear',
22306 navFnc: 'FullYear',
22311 Roo.apply(Roo.bootstrap.DateField, {
22315 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22319 cls: 'datepicker-days',
22323 cls: 'table-condensed',
22325 Roo.bootstrap.DateField.head,
22329 Roo.bootstrap.DateField.footer
22336 cls: 'datepicker-months',
22340 cls: 'table-condensed',
22342 Roo.bootstrap.DateField.head,
22343 Roo.bootstrap.DateField.content,
22344 Roo.bootstrap.DateField.footer
22351 cls: 'datepicker-years',
22355 cls: 'table-condensed',
22357 Roo.bootstrap.DateField.head,
22358 Roo.bootstrap.DateField.content,
22359 Roo.bootstrap.DateField.footer
22378 * @class Roo.bootstrap.TimeField
22379 * @extends Roo.bootstrap.Input
22380 * Bootstrap DateField class
22384 * Create a new TimeField
22385 * @param {Object} config The config object
22388 Roo.bootstrap.TimeField = function(config){
22389 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22393 * Fires when this field show.
22394 * @param {Roo.bootstrap.DateField} thisthis
22395 * @param {Mixed} date The date value
22400 * Fires when this field hide.
22401 * @param {Roo.bootstrap.DateField} this
22402 * @param {Mixed} date The date value
22407 * Fires when select a date.
22408 * @param {Roo.bootstrap.DateField} this
22409 * @param {Mixed} date The date value
22415 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22418 * @cfg {String} format
22419 * The default time format string which can be overriden for localization support. The format must be
22420 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22424 getAutoCreate : function()
22426 this.after = '<i class="fa far fa-clock"></i>';
22427 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22431 onRender: function(ct, position)
22434 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22436 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22438 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22440 this.pop = this.picker().select('>.datepicker-time',true).first();
22441 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22443 this.picker().on('mousedown', this.onMousedown, this);
22444 this.picker().on('click', this.onClick, this);
22446 this.picker().addClass('datepicker-dropdown');
22451 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22452 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22453 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22454 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22455 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22456 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22460 fireKey: function(e){
22461 if (!this.picker().isVisible()){
22462 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22468 e.preventDefault();
22476 this.onTogglePeriod();
22479 this.onIncrementMinutes();
22482 this.onDecrementMinutes();
22491 onClick: function(e) {
22492 e.stopPropagation();
22493 e.preventDefault();
22496 picker : function()
22498 return this.pickerEl;
22501 fillTime: function()
22503 var time = this.pop.select('tbody', true).first();
22505 time.dom.innerHTML = '';
22520 cls: 'hours-up fa fas fa-chevron-up'
22540 cls: 'minutes-up fa fas fa-chevron-up'
22561 cls: 'timepicker-hour',
22576 cls: 'timepicker-minute',
22591 cls: 'btn btn-primary period',
22613 cls: 'hours-down fa fas fa-chevron-down'
22633 cls: 'minutes-down fa fas fa-chevron-down'
22651 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22658 var hours = this.time.getHours();
22659 var minutes = this.time.getMinutes();
22672 hours = hours - 12;
22676 hours = '0' + hours;
22680 minutes = '0' + minutes;
22683 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22684 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22685 this.pop.select('button', true).first().dom.innerHTML = period;
22691 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22693 var cls = ['bottom'];
22695 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22702 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22706 //this.picker().setXY(20000,20000);
22707 this.picker().addClass(cls.join('-'));
22711 Roo.each(cls, function(c){
22716 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22717 //_this.picker().setTop(_this.inputEl().getHeight());
22721 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22723 //_this.picker().setTop(0 - _this.picker().getHeight());
22728 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22732 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22740 onFocus : function()
22742 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22746 onBlur : function()
22748 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22754 this.picker().show();
22759 this.fireEvent('show', this, this.date);
22764 this.picker().hide();
22767 this.fireEvent('hide', this, this.date);
22770 setTime : function()
22773 this.setValue(this.time.format(this.format));
22775 this.fireEvent('select', this, this.date);
22780 onMousedown: function(e){
22781 e.stopPropagation();
22782 e.preventDefault();
22785 onIncrementHours: function()
22787 Roo.log('onIncrementHours');
22788 this.time = this.time.add(Date.HOUR, 1);
22793 onDecrementHours: function()
22795 Roo.log('onDecrementHours');
22796 this.time = this.time.add(Date.HOUR, -1);
22800 onIncrementMinutes: function()
22802 Roo.log('onIncrementMinutes');
22803 this.time = this.time.add(Date.MINUTE, 1);
22807 onDecrementMinutes: function()
22809 Roo.log('onDecrementMinutes');
22810 this.time = this.time.add(Date.MINUTE, -1);
22814 onTogglePeriod: function()
22816 Roo.log('onTogglePeriod');
22817 this.time = this.time.add(Date.HOUR, 12);
22825 Roo.apply(Roo.bootstrap.TimeField, {
22829 cls: 'datepicker dropdown-menu',
22833 cls: 'datepicker-time',
22837 cls: 'table-condensed',
22866 cls: 'btn btn-info ok',
22894 * @class Roo.bootstrap.MonthField
22895 * @extends Roo.bootstrap.Input
22896 * Bootstrap MonthField class
22898 * @cfg {String} language default en
22901 * Create a new MonthField
22902 * @param {Object} config The config object
22905 Roo.bootstrap.MonthField = function(config){
22906 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22911 * Fires when this field show.
22912 * @param {Roo.bootstrap.MonthField} this
22913 * @param {Mixed} date The date value
22918 * Fires when this field hide.
22919 * @param {Roo.bootstrap.MonthField} this
22920 * @param {Mixed} date The date value
22925 * Fires when select a date.
22926 * @param {Roo.bootstrap.MonthField} this
22927 * @param {String} oldvalue The old value
22928 * @param {String} newvalue The new value
22934 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22936 onRender: function(ct, position)
22939 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22941 this.language = this.language || 'en';
22942 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22943 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22945 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22946 this.isInline = false;
22947 this.isInput = true;
22948 this.component = this.el.select('.add-on', true).first() || false;
22949 this.component = (this.component && this.component.length === 0) ? false : this.component;
22950 this.hasInput = this.component && this.inputEL().length;
22952 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22954 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22956 this.picker().on('mousedown', this.onMousedown, this);
22957 this.picker().on('click', this.onClick, this);
22959 this.picker().addClass('datepicker-dropdown');
22961 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22962 v.setStyle('width', '189px');
22969 if(this.isInline) {
22975 setValue: function(v, suppressEvent)
22977 var o = this.getValue();
22979 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22983 if(suppressEvent !== true){
22984 this.fireEvent('select', this, o, v);
22989 getValue: function()
22994 onClick: function(e)
22996 e.stopPropagation();
22997 e.preventDefault();
22999 var target = e.getTarget();
23001 if(target.nodeName.toLowerCase() === 'i'){
23002 target = Roo.get(target).dom.parentNode;
23005 var nodeName = target.nodeName;
23006 var className = target.className;
23007 var html = target.innerHTML;
23009 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23013 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23015 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23021 picker : function()
23023 return this.pickerEl;
23026 fillMonths: function()
23029 var months = this.picker().select('>.datepicker-months td', true).first();
23031 months.dom.innerHTML = '';
23037 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23040 months.createChild(month);
23049 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23050 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23053 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23054 e.removeClass('active');
23056 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23057 e.addClass('active');
23064 if(this.isInline) {
23068 this.picker().removeClass(['bottom', 'top']);
23070 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23072 * place to the top of element!
23076 this.picker().addClass('top');
23077 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23082 this.picker().addClass('bottom');
23084 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23087 onFocus : function()
23089 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23093 onBlur : function()
23095 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23097 var d = this.inputEl().getValue();
23106 this.picker().show();
23107 this.picker().select('>.datepicker-months', true).first().show();
23111 this.fireEvent('show', this, this.date);
23116 if(this.isInline) {
23119 this.picker().hide();
23120 this.fireEvent('hide', this, this.date);
23124 onMousedown: function(e)
23126 e.stopPropagation();
23127 e.preventDefault();
23132 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23136 fireKey: function(e)
23138 if (!this.picker().isVisible()){
23139 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23150 e.preventDefault();
23154 dir = e.keyCode == 37 ? -1 : 1;
23156 this.vIndex = this.vIndex + dir;
23158 if(this.vIndex < 0){
23162 if(this.vIndex > 11){
23166 if(isNaN(this.vIndex)){
23170 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23176 dir = e.keyCode == 38 ? -1 : 1;
23178 this.vIndex = this.vIndex + dir * 4;
23180 if(this.vIndex < 0){
23184 if(this.vIndex > 11){
23188 if(isNaN(this.vIndex)){
23192 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23197 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23198 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23202 e.preventDefault();
23205 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23206 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23222 this.picker().remove();
23227 Roo.apply(Roo.bootstrap.MonthField, {
23246 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23247 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23252 Roo.apply(Roo.bootstrap.MonthField, {
23256 cls: 'datepicker dropdown-menu roo-dynamic',
23260 cls: 'datepicker-months',
23264 cls: 'table-condensed',
23266 Roo.bootstrap.DateField.content
23286 * @class Roo.bootstrap.CheckBox
23287 * @extends Roo.bootstrap.Input
23288 * Bootstrap CheckBox class
23290 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23291 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23292 * @cfg {String} boxLabel The text that appears beside the checkbox
23293 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23294 * @cfg {Boolean} checked initnal the element
23295 * @cfg {Boolean} inline inline the element (default false)
23296 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23297 * @cfg {String} tooltip label tooltip
23300 * Create a new CheckBox
23301 * @param {Object} config The config object
23304 Roo.bootstrap.CheckBox = function(config){
23305 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23310 * Fires when the element is checked or unchecked.
23311 * @param {Roo.bootstrap.CheckBox} this This input
23312 * @param {Boolean} checked The new checked value
23317 * Fires when the element is click.
23318 * @param {Roo.bootstrap.CheckBox} this This input
23325 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23327 inputType: 'checkbox',
23336 // checkbox success does not make any sense really..
23341 getAutoCreate : function()
23343 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23349 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23352 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23358 type : this.inputType,
23359 value : this.inputValue,
23360 cls : 'roo-' + this.inputType, //'form-box',
23361 placeholder : this.placeholder || ''
23365 if(this.inputType != 'radio'){
23369 cls : 'roo-hidden-value',
23370 value : this.checked ? this.inputValue : this.valueOff
23375 if (this.weight) { // Validity check?
23376 cfg.cls += " " + this.inputType + "-" + this.weight;
23379 if (this.disabled) {
23380 input.disabled=true;
23384 input.checked = this.checked;
23389 input.name = this.name;
23391 if(this.inputType != 'radio'){
23392 hidden.name = this.name;
23393 input.name = '_hidden_' + this.name;
23398 input.cls += ' input-' + this.size;
23403 ['xs','sm','md','lg'].map(function(size){
23404 if (settings[size]) {
23405 cfg.cls += ' col-' + size + '-' + settings[size];
23409 var inputblock = input;
23411 if (this.before || this.after) {
23414 cls : 'input-group',
23419 inputblock.cn.push({
23421 cls : 'input-group-addon',
23426 inputblock.cn.push(input);
23428 if(this.inputType != 'radio'){
23429 inputblock.cn.push(hidden);
23433 inputblock.cn.push({
23435 cls : 'input-group-addon',
23441 var boxLabelCfg = false;
23447 //'for': id, // box label is handled by onclick - so no for...
23449 html: this.boxLabel
23452 boxLabelCfg.tooltip = this.tooltip;
23458 if (align ==='left' && this.fieldLabel.length) {
23459 // Roo.log("left and has label");
23464 cls : 'control-label',
23465 html : this.fieldLabel
23476 cfg.cn[1].cn.push(boxLabelCfg);
23479 if(this.labelWidth > 12){
23480 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23483 if(this.labelWidth < 13 && this.labelmd == 0){
23484 this.labelmd = this.labelWidth;
23487 if(this.labellg > 0){
23488 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23489 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23492 if(this.labelmd > 0){
23493 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23494 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23497 if(this.labelsm > 0){
23498 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23499 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23502 if(this.labelxs > 0){
23503 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23504 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23507 } else if ( this.fieldLabel.length) {
23508 // Roo.log(" label");
23512 tag: this.boxLabel ? 'span' : 'label',
23514 cls: 'control-label box-input-label',
23515 //cls : 'input-group-addon',
23516 html : this.fieldLabel
23523 cfg.cn.push(boxLabelCfg);
23528 // Roo.log(" no label && no align");
23529 cfg.cn = [ inputblock ] ;
23531 cfg.cn.push(boxLabelCfg);
23539 if(this.inputType != 'radio'){
23540 cfg.cn.push(hidden);
23548 * return the real input element.
23550 inputEl: function ()
23552 return this.el.select('input.roo-' + this.inputType,true).first();
23554 hiddenEl: function ()
23556 return this.el.select('input.roo-hidden-value',true).first();
23559 labelEl: function()
23561 return this.el.select('label.control-label',true).first();
23563 /* depricated... */
23567 return this.labelEl();
23570 boxLabelEl: function()
23572 return this.el.select('label.box-label',true).first();
23575 initEvents : function()
23577 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23579 this.inputEl().on('click', this.onClick, this);
23581 if (this.boxLabel) {
23582 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23585 this.startValue = this.getValue();
23588 Roo.bootstrap.CheckBox.register(this);
23592 onClick : function(e)
23594 if(this.fireEvent('click', this, e) !== false){
23595 this.setChecked(!this.checked);
23600 setChecked : function(state,suppressEvent)
23602 this.startValue = this.getValue();
23604 if(this.inputType == 'radio'){
23606 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23607 e.dom.checked = false;
23610 this.inputEl().dom.checked = true;
23612 this.inputEl().dom.value = this.inputValue;
23614 if(suppressEvent !== true){
23615 this.fireEvent('check', this, true);
23623 this.checked = state;
23625 this.inputEl().dom.checked = state;
23628 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23630 if(suppressEvent !== true){
23631 this.fireEvent('check', this, state);
23637 getValue : function()
23639 if(this.inputType == 'radio'){
23640 return this.getGroupValue();
23643 return this.hiddenEl().dom.value;
23647 getGroupValue : function()
23649 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23653 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23656 setValue : function(v,suppressEvent)
23658 if(this.inputType == 'radio'){
23659 this.setGroupValue(v, suppressEvent);
23663 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23668 setGroupValue : function(v, suppressEvent)
23670 this.startValue = this.getValue();
23672 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23673 e.dom.checked = false;
23675 if(e.dom.value == v){
23676 e.dom.checked = true;
23680 if(suppressEvent !== true){
23681 this.fireEvent('check', this, true);
23689 validate : function()
23691 if(this.getVisibilityEl().hasClass('hidden')){
23697 (this.inputType == 'radio' && this.validateRadio()) ||
23698 (this.inputType == 'checkbox' && this.validateCheckbox())
23704 this.markInvalid();
23708 validateRadio : function()
23710 if(this.getVisibilityEl().hasClass('hidden')){
23714 if(this.allowBlank){
23720 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23721 if(!e.dom.checked){
23733 validateCheckbox : function()
23736 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23737 //return (this.getValue() == this.inputValue) ? true : false;
23740 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23748 for(var i in group){
23749 if(group[i].el.isVisible(true)){
23757 for(var i in group){
23762 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23769 * Mark this field as valid
23771 markValid : function()
23775 this.fireEvent('valid', this);
23777 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23780 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23787 if(this.inputType == 'radio'){
23788 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23789 var fg = e.findParent('.form-group', false, true);
23790 if (Roo.bootstrap.version == 3) {
23791 fg.removeClass([_this.invalidClass, _this.validClass]);
23792 fg.addClass(_this.validClass);
23794 fg.removeClass(['is-valid', 'is-invalid']);
23795 fg.addClass('is-valid');
23803 var fg = this.el.findParent('.form-group', false, true);
23804 if (Roo.bootstrap.version == 3) {
23805 fg.removeClass([this.invalidClass, this.validClass]);
23806 fg.addClass(this.validClass);
23808 fg.removeClass(['is-valid', 'is-invalid']);
23809 fg.addClass('is-valid');
23814 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23820 for(var i in group){
23821 var fg = group[i].el.findParent('.form-group', false, true);
23822 if (Roo.bootstrap.version == 3) {
23823 fg.removeClass([this.invalidClass, this.validClass]);
23824 fg.addClass(this.validClass);
23826 fg.removeClass(['is-valid', 'is-invalid']);
23827 fg.addClass('is-valid');
23833 * Mark this field as invalid
23834 * @param {String} msg The validation message
23836 markInvalid : function(msg)
23838 if(this.allowBlank){
23844 this.fireEvent('invalid', this, msg);
23846 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23849 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23853 label.markInvalid();
23856 if(this.inputType == 'radio'){
23858 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23859 var fg = e.findParent('.form-group', false, true);
23860 if (Roo.bootstrap.version == 3) {
23861 fg.removeClass([_this.invalidClass, _this.validClass]);
23862 fg.addClass(_this.invalidClass);
23864 fg.removeClass(['is-invalid', 'is-valid']);
23865 fg.addClass('is-invalid');
23873 var fg = this.el.findParent('.form-group', false, true);
23874 if (Roo.bootstrap.version == 3) {
23875 fg.removeClass([_this.invalidClass, _this.validClass]);
23876 fg.addClass(_this.invalidClass);
23878 fg.removeClass(['is-invalid', 'is-valid']);
23879 fg.addClass('is-invalid');
23884 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23890 for(var i in group){
23891 var fg = group[i].el.findParent('.form-group', false, true);
23892 if (Roo.bootstrap.version == 3) {
23893 fg.removeClass([_this.invalidClass, _this.validClass]);
23894 fg.addClass(_this.invalidClass);
23896 fg.removeClass(['is-invalid', 'is-valid']);
23897 fg.addClass('is-invalid');
23903 clearInvalid : function()
23905 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23907 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23909 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23911 if (label && label.iconEl) {
23912 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23913 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23917 disable : function()
23919 if(this.inputType != 'radio'){
23920 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23927 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23928 _this.getActionEl().addClass(this.disabledClass);
23929 e.dom.disabled = true;
23933 this.disabled = true;
23934 this.fireEvent("disable", this);
23938 enable : function()
23940 if(this.inputType != 'radio'){
23941 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23948 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23949 _this.getActionEl().removeClass(this.disabledClass);
23950 e.dom.disabled = false;
23954 this.disabled = false;
23955 this.fireEvent("enable", this);
23959 setBoxLabel : function(v)
23964 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23970 Roo.apply(Roo.bootstrap.CheckBox, {
23975 * register a CheckBox Group
23976 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23978 register : function(checkbox)
23980 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23981 this.groups[checkbox.groupId] = {};
23984 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23988 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23992 * fetch a CheckBox Group based on the group ID
23993 * @param {string} the group ID
23994 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23996 get: function(groupId) {
23997 if (typeof(this.groups[groupId]) == 'undefined') {
24001 return this.groups[groupId] ;
24014 * @class Roo.bootstrap.Radio
24015 * @extends Roo.bootstrap.Component
24016 * Bootstrap Radio class
24017 * @cfg {String} boxLabel - the label associated
24018 * @cfg {String} value - the value of radio
24021 * Create a new Radio
24022 * @param {Object} config The config object
24024 Roo.bootstrap.Radio = function(config){
24025 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24029 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24035 getAutoCreate : function()
24039 cls : 'form-group radio',
24044 html : this.boxLabel
24052 initEvents : function()
24054 this.parent().register(this);
24056 this.el.on('click', this.onClick, this);
24060 onClick : function(e)
24062 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24063 this.setChecked(true);
24067 setChecked : function(state, suppressEvent)
24069 this.parent().setValue(this.value, suppressEvent);
24073 setBoxLabel : function(v)
24078 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24093 * @class Roo.bootstrap.SecurePass
24094 * @extends Roo.bootstrap.Input
24095 * Bootstrap SecurePass class
24099 * Create a new SecurePass
24100 * @param {Object} config The config object
24103 Roo.bootstrap.SecurePass = function (config) {
24104 // these go here, so the translation tool can replace them..
24106 PwdEmpty: "Please type a password, and then retype it to confirm.",
24107 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24108 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24109 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24110 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24111 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24112 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24113 TooWeak: "Your password is Too Weak."
24115 this.meterLabel = "Password strength:";
24116 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24117 this.meterClass = [
24118 "roo-password-meter-tooweak",
24119 "roo-password-meter-weak",
24120 "roo-password-meter-medium",
24121 "roo-password-meter-strong",
24122 "roo-password-meter-grey"
24127 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24130 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24132 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24134 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24135 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24136 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24137 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24138 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24139 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24140 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24150 * @cfg {String/Object} Label for the strength meter (defaults to
24151 * 'Password strength:')
24156 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24157 * ['Weak', 'Medium', 'Strong'])
24160 pwdStrengths: false,
24173 initEvents: function ()
24175 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24177 if (this.el.is('input[type=password]') && Roo.isSafari) {
24178 this.el.on('keydown', this.SafariOnKeyDown, this);
24181 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24184 onRender: function (ct, position)
24186 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24187 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24188 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24190 this.trigger.createChild({
24195 cls: 'roo-password-meter-grey col-xs-12',
24198 //width: this.meterWidth + 'px'
24202 cls: 'roo-password-meter-text'
24208 if (this.hideTrigger) {
24209 this.trigger.setDisplayed(false);
24211 this.setSize(this.width || '', this.height || '');
24214 onDestroy: function ()
24216 if (this.trigger) {
24217 this.trigger.removeAllListeners();
24218 this.trigger.remove();
24221 this.wrap.remove();
24223 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24226 checkStrength: function ()
24228 var pwd = this.inputEl().getValue();
24229 if (pwd == this._lastPwd) {
24234 if (this.ClientSideStrongPassword(pwd)) {
24236 } else if (this.ClientSideMediumPassword(pwd)) {
24238 } else if (this.ClientSideWeakPassword(pwd)) {
24244 Roo.log('strength1: ' + strength);
24246 //var pm = this.trigger.child('div/div/div').dom;
24247 var pm = this.trigger.child('div/div');
24248 pm.removeClass(this.meterClass);
24249 pm.addClass(this.meterClass[strength]);
24252 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24254 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24256 this._lastPwd = pwd;
24260 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24262 this._lastPwd = '';
24264 var pm = this.trigger.child('div/div');
24265 pm.removeClass(this.meterClass);
24266 pm.addClass('roo-password-meter-grey');
24269 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24272 this.inputEl().dom.type='password';
24275 validateValue: function (value)
24277 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24280 if (value.length == 0) {
24281 if (this.allowBlank) {
24282 this.clearInvalid();
24286 this.markInvalid(this.errors.PwdEmpty);
24287 this.errorMsg = this.errors.PwdEmpty;
24295 if (!value.match(/[\x21-\x7e]+/)) {
24296 this.markInvalid(this.errors.PwdBadChar);
24297 this.errorMsg = this.errors.PwdBadChar;
24300 if (value.length < 6) {
24301 this.markInvalid(this.errors.PwdShort);
24302 this.errorMsg = this.errors.PwdShort;
24305 if (value.length > 16) {
24306 this.markInvalid(this.errors.PwdLong);
24307 this.errorMsg = this.errors.PwdLong;
24311 if (this.ClientSideStrongPassword(value)) {
24313 } else if (this.ClientSideMediumPassword(value)) {
24315 } else if (this.ClientSideWeakPassword(value)) {
24322 if (strength < 2) {
24323 //this.markInvalid(this.errors.TooWeak);
24324 this.errorMsg = this.errors.TooWeak;
24329 console.log('strength2: ' + strength);
24331 //var pm = this.trigger.child('div/div/div').dom;
24333 var pm = this.trigger.child('div/div');
24334 pm.removeClass(this.meterClass);
24335 pm.addClass(this.meterClass[strength]);
24337 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24339 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24341 this.errorMsg = '';
24345 CharacterSetChecks: function (type)
24348 this.fResult = false;
24351 isctype: function (character, type)
24354 case this.kCapitalLetter:
24355 if (character >= 'A' && character <= 'Z') {
24360 case this.kSmallLetter:
24361 if (character >= 'a' && character <= 'z') {
24367 if (character >= '0' && character <= '9') {
24372 case this.kPunctuation:
24373 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24384 IsLongEnough: function (pwd, size)
24386 return !(pwd == null || isNaN(size) || pwd.length < size);
24389 SpansEnoughCharacterSets: function (word, nb)
24391 if (!this.IsLongEnough(word, nb))
24396 var characterSetChecks = new Array(
24397 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24398 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24401 for (var index = 0; index < word.length; ++index) {
24402 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24403 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24404 characterSetChecks[nCharSet].fResult = true;
24411 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24412 if (characterSetChecks[nCharSet].fResult) {
24417 if (nCharSets < nb) {
24423 ClientSideStrongPassword: function (pwd)
24425 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24428 ClientSideMediumPassword: function (pwd)
24430 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24433 ClientSideWeakPassword: function (pwd)
24435 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24438 })//<script type="text/javascript">
24441 * Based Ext JS Library 1.1.1
24442 * Copyright(c) 2006-2007, Ext JS, LLC.
24448 * @class Roo.HtmlEditorCore
24449 * @extends Roo.Component
24450 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24452 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24455 Roo.HtmlEditorCore = function(config){
24458 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24463 * @event initialize
24464 * Fires when the editor is fully initialized (including the iframe)
24465 * @param {Roo.HtmlEditorCore} this
24470 * Fires when the editor is first receives the focus. Any insertion must wait
24471 * until after this event.
24472 * @param {Roo.HtmlEditorCore} this
24476 * @event beforesync
24477 * Fires before the textarea is updated with content from the editor iframe. Return false
24478 * to cancel the sync.
24479 * @param {Roo.HtmlEditorCore} this
24480 * @param {String} html
24484 * @event beforepush
24485 * Fires before the iframe editor is updated with content from the textarea. Return false
24486 * to cancel the push.
24487 * @param {Roo.HtmlEditorCore} this
24488 * @param {String} html
24493 * Fires when the textarea is updated with content from the editor iframe.
24494 * @param {Roo.HtmlEditorCore} this
24495 * @param {String} html
24500 * Fires when the iframe editor is updated with content from the textarea.
24501 * @param {Roo.HtmlEditorCore} this
24502 * @param {String} html
24507 * @event editorevent
24508 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24509 * @param {Roo.HtmlEditorCore} this
24515 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24517 // defaults : white / black...
24518 this.applyBlacklists();
24525 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24529 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24535 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24540 * @cfg {Number} height (in pixels)
24544 * @cfg {Number} width (in pixels)
24549 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24552 stylesheets: false,
24557 // private properties
24558 validationEvent : false,
24560 initialized : false,
24562 sourceEditMode : false,
24563 onFocus : Roo.emptyFn,
24565 hideMode:'offsets',
24569 // blacklist + whitelisted elements..
24576 * Protected method that will not generally be called directly. It
24577 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24578 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24580 getDocMarkup : function(){
24584 // inherit styels from page...??
24585 if (this.stylesheets === false) {
24587 Roo.get(document.head).select('style').each(function(node) {
24588 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24591 Roo.get(document.head).select('link').each(function(node) {
24592 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24595 } else if (!this.stylesheets.length) {
24597 st = '<style type="text/css">' +
24598 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24601 for (var i in this.stylesheets) {
24602 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24607 st += '<style type="text/css">' +
24608 'IMG { cursor: pointer } ' +
24611 var cls = 'roo-htmleditor-body';
24613 if(this.bodyCls.length){
24614 cls += ' ' + this.bodyCls;
24617 return '<html><head>' + st +
24618 //<style type="text/css">' +
24619 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24621 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24625 onRender : function(ct, position)
24628 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24629 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24632 this.el.dom.style.border = '0 none';
24633 this.el.dom.setAttribute('tabIndex', -1);
24634 this.el.addClass('x-hidden hide');
24638 if(Roo.isIE){ // fix IE 1px bogus margin
24639 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24643 this.frameId = Roo.id();
24647 var iframe = this.owner.wrap.createChild({
24649 cls: 'form-control', // bootstrap..
24651 name: this.frameId,
24652 frameBorder : 'no',
24653 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24658 this.iframe = iframe.dom;
24660 this.assignDocWin();
24662 this.doc.designMode = 'on';
24665 this.doc.write(this.getDocMarkup());
24669 var task = { // must defer to wait for browser to be ready
24671 //console.log("run task?" + this.doc.readyState);
24672 this.assignDocWin();
24673 if(this.doc.body || this.doc.readyState == 'complete'){
24675 this.doc.designMode="on";
24679 Roo.TaskMgr.stop(task);
24680 this.initEditor.defer(10, this);
24687 Roo.TaskMgr.start(task);
24692 onResize : function(w, h)
24694 Roo.log('resize: ' +w + ',' + h );
24695 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24699 if(typeof w == 'number'){
24701 this.iframe.style.width = w + 'px';
24703 if(typeof h == 'number'){
24705 this.iframe.style.height = h + 'px';
24707 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24714 * Toggles the editor between standard and source edit mode.
24715 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24717 toggleSourceEdit : function(sourceEditMode){
24719 this.sourceEditMode = sourceEditMode === true;
24721 if(this.sourceEditMode){
24723 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24726 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24727 //this.iframe.className = '';
24730 //this.setSize(this.owner.wrap.getSize());
24731 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24738 * Protected method that will not generally be called directly. If you need/want
24739 * custom HTML cleanup, this is the method you should override.
24740 * @param {String} html The HTML to be cleaned
24741 * return {String} The cleaned HTML
24743 cleanHtml : function(html){
24744 html = String(html);
24745 if(html.length > 5){
24746 if(Roo.isSafari){ // strip safari nonsense
24747 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24750 if(html == ' '){
24757 * HTML Editor -> Textarea
24758 * Protected method that will not generally be called directly. Syncs the contents
24759 * of the editor iframe with the textarea.
24761 syncValue : function(){
24762 if(this.initialized){
24763 var bd = (this.doc.body || this.doc.documentElement);
24764 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24765 var html = bd.innerHTML;
24767 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24768 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24770 html = '<div style="'+m[0]+'">' + html + '</div>';
24773 html = this.cleanHtml(html);
24774 // fix up the special chars.. normaly like back quotes in word...
24775 // however we do not want to do this with chinese..
24776 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24778 var cc = match.charCodeAt();
24780 // Get the character value, handling surrogate pairs
24781 if (match.length == 2) {
24782 // It's a surrogate pair, calculate the Unicode code point
24783 var high = match.charCodeAt(0) - 0xD800;
24784 var low = match.charCodeAt(1) - 0xDC00;
24785 cc = (high * 0x400) + low + 0x10000;
24787 (cc >= 0x4E00 && cc < 0xA000 ) ||
24788 (cc >= 0x3400 && cc < 0x4E00 ) ||
24789 (cc >= 0xf900 && cc < 0xfb00 )
24794 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24795 return "&#" + cc + ";";
24802 if(this.owner.fireEvent('beforesync', this, html) !== false){
24803 this.el.dom.value = html;
24804 this.owner.fireEvent('sync', this, html);
24810 * Protected method that will not generally be called directly. Pushes the value of the textarea
24811 * into the iframe editor.
24813 pushValue : function(){
24814 if(this.initialized){
24815 var v = this.el.dom.value.trim();
24817 // if(v.length < 1){
24821 if(this.owner.fireEvent('beforepush', this, v) !== false){
24822 var d = (this.doc.body || this.doc.documentElement);
24824 this.cleanUpPaste();
24825 this.el.dom.value = d.innerHTML;
24826 this.owner.fireEvent('push', this, v);
24832 deferFocus : function(){
24833 this.focus.defer(10, this);
24837 focus : function(){
24838 if(this.win && !this.sourceEditMode){
24845 assignDocWin: function()
24847 var iframe = this.iframe;
24850 this.doc = iframe.contentWindow.document;
24851 this.win = iframe.contentWindow;
24853 // if (!Roo.get(this.frameId)) {
24856 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24857 // this.win = Roo.get(this.frameId).dom.contentWindow;
24859 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24863 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24864 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24869 initEditor : function(){
24870 //console.log("INIT EDITOR");
24871 this.assignDocWin();
24875 this.doc.designMode="on";
24877 this.doc.write(this.getDocMarkup());
24880 var dbody = (this.doc.body || this.doc.documentElement);
24881 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24882 // this copies styles from the containing element into thsi one..
24883 // not sure why we need all of this..
24884 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24886 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24887 //ss['background-attachment'] = 'fixed'; // w3c
24888 dbody.bgProperties = 'fixed'; // ie
24889 //Roo.DomHelper.applyStyles(dbody, ss);
24890 Roo.EventManager.on(this.doc, {
24891 //'mousedown': this.onEditorEvent,
24892 'mouseup': this.onEditorEvent,
24893 'dblclick': this.onEditorEvent,
24894 'click': this.onEditorEvent,
24895 'keyup': this.onEditorEvent,
24900 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24902 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24903 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24905 this.initialized = true;
24907 this.owner.fireEvent('initialize', this);
24912 onDestroy : function(){
24918 //for (var i =0; i < this.toolbars.length;i++) {
24919 // // fixme - ask toolbars for heights?
24920 // this.toolbars[i].onDestroy();
24923 //this.wrap.dom.innerHTML = '';
24924 //this.wrap.remove();
24929 onFirstFocus : function(){
24931 this.assignDocWin();
24934 this.activated = true;
24937 if(Roo.isGecko){ // prevent silly gecko errors
24939 var s = this.win.getSelection();
24940 if(!s.focusNode || s.focusNode.nodeType != 3){
24941 var r = s.getRangeAt(0);
24942 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24947 this.execCmd('useCSS', true);
24948 this.execCmd('styleWithCSS', false);
24951 this.owner.fireEvent('activate', this);
24955 adjustFont: function(btn){
24956 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24957 //if(Roo.isSafari){ // safari
24960 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24961 if(Roo.isSafari){ // safari
24962 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24963 v = (v < 10) ? 10 : v;
24964 v = (v > 48) ? 48 : v;
24965 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24970 v = Math.max(1, v+adjust);
24972 this.execCmd('FontSize', v );
24975 onEditorEvent : function(e)
24977 this.owner.fireEvent('editorevent', this, e);
24978 // this.updateToolbar();
24979 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24982 insertTag : function(tg)
24984 // could be a bit smarter... -> wrap the current selected tRoo..
24985 if (tg.toLowerCase() == 'span' ||
24986 tg.toLowerCase() == 'code' ||
24987 tg.toLowerCase() == 'sup' ||
24988 tg.toLowerCase() == 'sub'
24991 range = this.createRange(this.getSelection());
24992 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24993 wrappingNode.appendChild(range.extractContents());
24994 range.insertNode(wrappingNode);
25001 this.execCmd("formatblock", tg);
25005 insertText : function(txt)
25009 var range = this.createRange();
25010 range.deleteContents();
25011 //alert(Sender.getAttribute('label'));
25013 range.insertNode(this.doc.createTextNode(txt));
25019 * Executes a Midas editor command on the editor document and performs necessary focus and
25020 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25021 * @param {String} cmd The Midas command
25022 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25024 relayCmd : function(cmd, value){
25026 this.execCmd(cmd, value);
25027 this.owner.fireEvent('editorevent', this);
25028 //this.updateToolbar();
25029 this.owner.deferFocus();
25033 * Executes a Midas editor command directly on the editor document.
25034 * For visual commands, you should use {@link #relayCmd} instead.
25035 * <b>This should only be called after the editor is initialized.</b>
25036 * @param {String} cmd The Midas command
25037 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25039 execCmd : function(cmd, value){
25040 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25047 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25049 * @param {String} text | dom node..
25051 insertAtCursor : function(text)
25054 if(!this.activated){
25060 var r = this.doc.selection.createRange();
25071 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25075 // from jquery ui (MIT licenced)
25077 var win = this.win;
25079 if (win.getSelection && win.getSelection().getRangeAt) {
25080 range = win.getSelection().getRangeAt(0);
25081 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25082 range.insertNode(node);
25083 } else if (win.document.selection && win.document.selection.createRange) {
25084 // no firefox support
25085 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25086 win.document.selection.createRange().pasteHTML(txt);
25088 // no firefox support
25089 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25090 this.execCmd('InsertHTML', txt);
25099 mozKeyPress : function(e){
25101 var c = e.getCharCode(), cmd;
25104 c = String.fromCharCode(c).toLowerCase();
25118 this.cleanUpPaste.defer(100, this);
25126 e.preventDefault();
25134 fixKeys : function(){ // load time branching for fastest keydown performance
25136 return function(e){
25137 var k = e.getKey(), r;
25140 r = this.doc.selection.createRange();
25143 r.pasteHTML('    ');
25150 r = this.doc.selection.createRange();
25152 var target = r.parentElement();
25153 if(!target || target.tagName.toLowerCase() != 'li'){
25155 r.pasteHTML('<br />');
25161 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25162 this.cleanUpPaste.defer(100, this);
25168 }else if(Roo.isOpera){
25169 return function(e){
25170 var k = e.getKey();
25174 this.execCmd('InsertHTML','    ');
25177 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25178 this.cleanUpPaste.defer(100, this);
25183 }else if(Roo.isSafari){
25184 return function(e){
25185 var k = e.getKey();
25189 this.execCmd('InsertText','\t');
25193 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25194 this.cleanUpPaste.defer(100, this);
25202 getAllAncestors: function()
25204 var p = this.getSelectedNode();
25207 a.push(p); // push blank onto stack..
25208 p = this.getParentElement();
25212 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25216 a.push(this.doc.body);
25220 lastSelNode : false,
25223 getSelection : function()
25225 this.assignDocWin();
25226 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25229 getSelectedNode: function()
25231 // this may only work on Gecko!!!
25233 // should we cache this!!!!
25238 var range = this.createRange(this.getSelection()).cloneRange();
25241 var parent = range.parentElement();
25243 var testRange = range.duplicate();
25244 testRange.moveToElementText(parent);
25245 if (testRange.inRange(range)) {
25248 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25251 parent = parent.parentElement;
25256 // is ancestor a text element.
25257 var ac = range.commonAncestorContainer;
25258 if (ac.nodeType == 3) {
25259 ac = ac.parentNode;
25262 var ar = ac.childNodes;
25265 var other_nodes = [];
25266 var has_other_nodes = false;
25267 for (var i=0;i<ar.length;i++) {
25268 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25271 // fullly contained node.
25273 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25278 // probably selected..
25279 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25280 other_nodes.push(ar[i]);
25284 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25289 has_other_nodes = true;
25291 if (!nodes.length && other_nodes.length) {
25292 nodes= other_nodes;
25294 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25300 createRange: function(sel)
25302 // this has strange effects when using with
25303 // top toolbar - not sure if it's a great idea.
25304 //this.editor.contentWindow.focus();
25305 if (typeof sel != "undefined") {
25307 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25309 return this.doc.createRange();
25312 return this.doc.createRange();
25315 getParentElement: function()
25318 this.assignDocWin();
25319 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25321 var range = this.createRange(sel);
25324 var p = range.commonAncestorContainer;
25325 while (p.nodeType == 3) { // text node
25336 * Range intersection.. the hard stuff...
25340 * [ -- selected range --- ]
25344 * if end is before start or hits it. fail.
25345 * if start is after end or hits it fail.
25347 * if either hits (but other is outside. - then it's not
25353 // @see http://www.thismuchiknow.co.uk/?p=64.
25354 rangeIntersectsNode : function(range, node)
25356 var nodeRange = node.ownerDocument.createRange();
25358 nodeRange.selectNode(node);
25360 nodeRange.selectNodeContents(node);
25363 var rangeStartRange = range.cloneRange();
25364 rangeStartRange.collapse(true);
25366 var rangeEndRange = range.cloneRange();
25367 rangeEndRange.collapse(false);
25369 var nodeStartRange = nodeRange.cloneRange();
25370 nodeStartRange.collapse(true);
25372 var nodeEndRange = nodeRange.cloneRange();
25373 nodeEndRange.collapse(false);
25375 return rangeStartRange.compareBoundaryPoints(
25376 Range.START_TO_START, nodeEndRange) == -1 &&
25377 rangeEndRange.compareBoundaryPoints(
25378 Range.START_TO_START, nodeStartRange) == 1;
25382 rangeCompareNode : function(range, node)
25384 var nodeRange = node.ownerDocument.createRange();
25386 nodeRange.selectNode(node);
25388 nodeRange.selectNodeContents(node);
25392 range.collapse(true);
25394 nodeRange.collapse(true);
25396 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25397 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25399 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25401 var nodeIsBefore = ss == 1;
25402 var nodeIsAfter = ee == -1;
25404 if (nodeIsBefore && nodeIsAfter) {
25407 if (!nodeIsBefore && nodeIsAfter) {
25408 return 1; //right trailed.
25411 if (nodeIsBefore && !nodeIsAfter) {
25412 return 2; // left trailed.
25418 // private? - in a new class?
25419 cleanUpPaste : function()
25421 // cleans up the whole document..
25422 Roo.log('cleanuppaste');
25424 this.cleanUpChildren(this.doc.body);
25425 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25426 if (clean != this.doc.body.innerHTML) {
25427 this.doc.body.innerHTML = clean;
25432 cleanWordChars : function(input) {// change the chars to hex code
25433 var he = Roo.HtmlEditorCore;
25435 var output = input;
25436 Roo.each(he.swapCodes, function(sw) {
25437 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25439 output = output.replace(swapper, sw[1]);
25446 cleanUpChildren : function (n)
25448 if (!n.childNodes.length) {
25451 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25452 this.cleanUpChild(n.childNodes[i]);
25459 cleanUpChild : function (node)
25462 //console.log(node);
25463 if (node.nodeName == "#text") {
25464 // clean up silly Windows -- stuff?
25467 if (node.nodeName == "#comment") {
25468 node.parentNode.removeChild(node);
25469 // clean up silly Windows -- stuff?
25472 var lcname = node.tagName.toLowerCase();
25473 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25474 // whitelist of tags..
25476 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25478 node.parentNode.removeChild(node);
25483 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25485 // spans with no attributes - just remove them..
25486 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25487 remove_keep_children = true;
25490 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25491 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25493 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25494 // remove_keep_children = true;
25497 if (remove_keep_children) {
25498 this.cleanUpChildren(node);
25499 // inserts everything just before this node...
25500 while (node.childNodes.length) {
25501 var cn = node.childNodes[0];
25502 node.removeChild(cn);
25503 node.parentNode.insertBefore(cn, node);
25505 node.parentNode.removeChild(node);
25509 if (!node.attributes || !node.attributes.length) {
25514 this.cleanUpChildren(node);
25518 function cleanAttr(n,v)
25521 if (v.match(/^\./) || v.match(/^\//)) {
25524 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25527 if (v.match(/^#/)) {
25530 if (v.match(/^\{/)) { // allow template editing.
25533 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25534 node.removeAttribute(n);
25538 var cwhite = this.cwhite;
25539 var cblack = this.cblack;
25541 function cleanStyle(n,v)
25543 if (v.match(/expression/)) { //XSS?? should we even bother..
25544 node.removeAttribute(n);
25548 var parts = v.split(/;/);
25551 Roo.each(parts, function(p) {
25552 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25556 var l = p.split(':').shift().replace(/\s+/g,'');
25557 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25559 if ( cwhite.length && cblack.indexOf(l) > -1) {
25560 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25561 //node.removeAttribute(n);
25565 // only allow 'c whitelisted system attributes'
25566 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25567 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25568 //node.removeAttribute(n);
25578 if (clean.length) {
25579 node.setAttribute(n, clean.join(';'));
25581 node.removeAttribute(n);
25587 for (var i = node.attributes.length-1; i > -1 ; i--) {
25588 var a = node.attributes[i];
25591 if (a.name.toLowerCase().substr(0,2)=='on') {
25592 node.removeAttribute(a.name);
25595 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25596 node.removeAttribute(a.name);
25599 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25600 cleanAttr(a.name,a.value); // fixme..
25603 if (a.name == 'style') {
25604 cleanStyle(a.name,a.value);
25607 /// clean up MS crap..
25608 // tecnically this should be a list of valid class'es..
25611 if (a.name == 'class') {
25612 if (a.value.match(/^Mso/)) {
25613 node.removeAttribute('class');
25616 if (a.value.match(/^body$/)) {
25617 node.removeAttribute('class');
25628 this.cleanUpChildren(node);
25634 * Clean up MS wordisms...
25636 cleanWord : function(node)
25639 this.cleanWord(this.doc.body);
25644 node.nodeName == 'SPAN' &&
25645 !node.hasAttributes() &&
25646 node.childNodes.length == 1 &&
25647 node.firstChild.nodeName == "#text"
25649 var textNode = node.firstChild;
25650 node.removeChild(textNode);
25651 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25652 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25654 node.parentNode.insertBefore(textNode, node);
25655 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25656 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25658 node.parentNode.removeChild(node);
25661 if (node.nodeName == "#text") {
25662 // clean up silly Windows -- stuff?
25665 if (node.nodeName == "#comment") {
25666 node.parentNode.removeChild(node);
25667 // clean up silly Windows -- stuff?
25671 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25672 node.parentNode.removeChild(node);
25675 //Roo.log(node.tagName);
25676 // remove - but keep children..
25677 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25678 //Roo.log('-- removed');
25679 while (node.childNodes.length) {
25680 var cn = node.childNodes[0];
25681 node.removeChild(cn);
25682 node.parentNode.insertBefore(cn, node);
25683 // move node to parent - and clean it..
25684 this.cleanWord(cn);
25686 node.parentNode.removeChild(node);
25687 /// no need to iterate chidlren = it's got none..
25688 //this.iterateChildren(node, this.cleanWord);
25692 if (node.className.length) {
25694 var cn = node.className.split(/\W+/);
25696 Roo.each(cn, function(cls) {
25697 if (cls.match(/Mso[a-zA-Z]+/)) {
25702 node.className = cna.length ? cna.join(' ') : '';
25704 node.removeAttribute("class");
25708 if (node.hasAttribute("lang")) {
25709 node.removeAttribute("lang");
25712 if (node.hasAttribute("style")) {
25714 var styles = node.getAttribute("style").split(";");
25716 Roo.each(styles, function(s) {
25717 if (!s.match(/:/)) {
25720 var kv = s.split(":");
25721 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25724 // what ever is left... we allow.
25727 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25728 if (!nstyle.length) {
25729 node.removeAttribute('style');
25732 this.iterateChildren(node, this.cleanWord);
25738 * iterateChildren of a Node, calling fn each time, using this as the scole..
25739 * @param {DomNode} node node to iterate children of.
25740 * @param {Function} fn method of this class to call on each item.
25742 iterateChildren : function(node, fn)
25744 if (!node.childNodes.length) {
25747 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25748 fn.call(this, node.childNodes[i])
25754 * cleanTableWidths.
25756 * Quite often pasting from word etc.. results in tables with column and widths.
25757 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25760 cleanTableWidths : function(node)
25765 this.cleanTableWidths(this.doc.body);
25770 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25773 Roo.log(node.tagName);
25774 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25775 this.iterateChildren(node, this.cleanTableWidths);
25778 if (node.hasAttribute('width')) {
25779 node.removeAttribute('width');
25783 if (node.hasAttribute("style")) {
25786 var styles = node.getAttribute("style").split(";");
25788 Roo.each(styles, function(s) {
25789 if (!s.match(/:/)) {
25792 var kv = s.split(":");
25793 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25796 // what ever is left... we allow.
25799 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25800 if (!nstyle.length) {
25801 node.removeAttribute('style');
25805 this.iterateChildren(node, this.cleanTableWidths);
25813 domToHTML : function(currentElement, depth, nopadtext) {
25815 depth = depth || 0;
25816 nopadtext = nopadtext || false;
25818 if (!currentElement) {
25819 return this.domToHTML(this.doc.body);
25822 //Roo.log(currentElement);
25824 var allText = false;
25825 var nodeName = currentElement.nodeName;
25826 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25828 if (nodeName == '#text') {
25830 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25835 if (nodeName != 'BODY') {
25838 // Prints the node tagName, such as <A>, <IMG>, etc
25841 for(i = 0; i < currentElement.attributes.length;i++) {
25843 var aname = currentElement.attributes.item(i).name;
25844 if (!currentElement.attributes.item(i).value.length) {
25847 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25850 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25859 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25862 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25867 // Traverse the tree
25869 var currentElementChild = currentElement.childNodes.item(i);
25870 var allText = true;
25871 var innerHTML = '';
25873 while (currentElementChild) {
25874 // Formatting code (indent the tree so it looks nice on the screen)
25875 var nopad = nopadtext;
25876 if (lastnode == 'SPAN') {
25880 if (currentElementChild.nodeName == '#text') {
25881 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25882 toadd = nopadtext ? toadd : toadd.trim();
25883 if (!nopad && toadd.length > 80) {
25884 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25886 innerHTML += toadd;
25889 currentElementChild = currentElement.childNodes.item(i);
25895 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25897 // Recursively traverse the tree structure of the child node
25898 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25899 lastnode = currentElementChild.nodeName;
25901 currentElementChild=currentElement.childNodes.item(i);
25907 // The remaining code is mostly for formatting the tree
25908 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25913 ret+= "</"+tagName+">";
25919 applyBlacklists : function()
25921 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25922 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25926 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25927 if (b.indexOf(tag) > -1) {
25930 this.white.push(tag);
25934 Roo.each(w, function(tag) {
25935 if (b.indexOf(tag) > -1) {
25938 if (this.white.indexOf(tag) > -1) {
25941 this.white.push(tag);
25946 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25947 if (w.indexOf(tag) > -1) {
25950 this.black.push(tag);
25954 Roo.each(b, function(tag) {
25955 if (w.indexOf(tag) > -1) {
25958 if (this.black.indexOf(tag) > -1) {
25961 this.black.push(tag);
25966 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25967 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25971 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25972 if (b.indexOf(tag) > -1) {
25975 this.cwhite.push(tag);
25979 Roo.each(w, function(tag) {
25980 if (b.indexOf(tag) > -1) {
25983 if (this.cwhite.indexOf(tag) > -1) {
25986 this.cwhite.push(tag);
25991 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25992 if (w.indexOf(tag) > -1) {
25995 this.cblack.push(tag);
25999 Roo.each(b, function(tag) {
26000 if (w.indexOf(tag) > -1) {
26003 if (this.cblack.indexOf(tag) > -1) {
26006 this.cblack.push(tag);
26011 setStylesheets : function(stylesheets)
26013 if(typeof(stylesheets) == 'string'){
26014 Roo.get(this.iframe.contentDocument.head).createChild({
26016 rel : 'stylesheet',
26025 Roo.each(stylesheets, function(s) {
26030 Roo.get(_this.iframe.contentDocument.head).createChild({
26032 rel : 'stylesheet',
26041 removeStylesheets : function()
26045 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26050 setStyle : function(style)
26052 Roo.get(this.iframe.contentDocument.head).createChild({
26061 // hide stuff that is not compatible
26075 * @event specialkey
26079 * @cfg {String} fieldClass @hide
26082 * @cfg {String} focusClass @hide
26085 * @cfg {String} autoCreate @hide
26088 * @cfg {String} inputType @hide
26091 * @cfg {String} invalidClass @hide
26094 * @cfg {String} invalidText @hide
26097 * @cfg {String} msgFx @hide
26100 * @cfg {String} validateOnBlur @hide
26104 Roo.HtmlEditorCore.white = [
26105 'area', 'br', 'img', 'input', 'hr', 'wbr',
26107 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26108 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26109 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26110 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26111 'table', 'ul', 'xmp',
26113 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26116 'dir', 'menu', 'ol', 'ul', 'dl',
26122 Roo.HtmlEditorCore.black = [
26123 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26125 'base', 'basefont', 'bgsound', 'blink', 'body',
26126 'frame', 'frameset', 'head', 'html', 'ilayer',
26127 'iframe', 'layer', 'link', 'meta', 'object',
26128 'script', 'style' ,'title', 'xml' // clean later..
26130 Roo.HtmlEditorCore.clean = [
26131 'script', 'style', 'title', 'xml'
26133 Roo.HtmlEditorCore.remove = [
26138 Roo.HtmlEditorCore.ablack = [
26142 Roo.HtmlEditorCore.aclean = [
26143 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26147 Roo.HtmlEditorCore.pwhite= [
26148 'http', 'https', 'mailto'
26151 // white listed style attributes.
26152 Roo.HtmlEditorCore.cwhite= [
26153 // 'text-align', /// default is to allow most things..
26159 // black listed style attributes.
26160 Roo.HtmlEditorCore.cblack= [
26161 // 'font-size' -- this can be set by the project
26165 Roo.HtmlEditorCore.swapCodes =[
26166 [ 8211, "–" ],
26167 [ 8212, "—" ],
26184 * @class Roo.bootstrap.HtmlEditor
26185 * @extends Roo.bootstrap.TextArea
26186 * Bootstrap HtmlEditor class
26189 * Create a new HtmlEditor
26190 * @param {Object} config The config object
26193 Roo.bootstrap.HtmlEditor = function(config){
26194 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26195 if (!this.toolbars) {
26196 this.toolbars = [];
26199 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26202 * @event initialize
26203 * Fires when the editor is fully initialized (including the iframe)
26204 * @param {HtmlEditor} this
26209 * Fires when the editor is first receives the focus. Any insertion must wait
26210 * until after this event.
26211 * @param {HtmlEditor} this
26215 * @event beforesync
26216 * Fires before the textarea is updated with content from the editor iframe. Return false
26217 * to cancel the sync.
26218 * @param {HtmlEditor} this
26219 * @param {String} html
26223 * @event beforepush
26224 * Fires before the iframe editor is updated with content from the textarea. Return false
26225 * to cancel the push.
26226 * @param {HtmlEditor} this
26227 * @param {String} html
26232 * Fires when the textarea is updated with content from the editor iframe.
26233 * @param {HtmlEditor} this
26234 * @param {String} html
26239 * Fires when the iframe editor is updated with content from the textarea.
26240 * @param {HtmlEditor} this
26241 * @param {String} html
26245 * @event editmodechange
26246 * Fires when the editor switches edit modes
26247 * @param {HtmlEditor} this
26248 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26250 editmodechange: true,
26252 * @event editorevent
26253 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26254 * @param {HtmlEditor} this
26258 * @event firstfocus
26259 * Fires when on first focus - needed by toolbars..
26260 * @param {HtmlEditor} this
26265 * Auto save the htmlEditor value as a file into Events
26266 * @param {HtmlEditor} this
26270 * @event savedpreview
26271 * preview the saved version of htmlEditor
26272 * @param {HtmlEditor} this
26279 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26283 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26288 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26293 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26298 * @cfg {Number} height (in pixels)
26302 * @cfg {Number} width (in pixels)
26307 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26310 stylesheets: false,
26315 // private properties
26316 validationEvent : false,
26318 initialized : false,
26321 onFocus : Roo.emptyFn,
26323 hideMode:'offsets',
26325 tbContainer : false,
26329 toolbarContainer :function() {
26330 return this.wrap.select('.x-html-editor-tb',true).first();
26334 * Protected method that will not generally be called directly. It
26335 * is called when the editor creates its toolbar. Override this method if you need to
26336 * add custom toolbar buttons.
26337 * @param {HtmlEditor} editor
26339 createToolbar : function(){
26340 Roo.log('renewing');
26341 Roo.log("create toolbars");
26343 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26344 this.toolbars[0].render(this.toolbarContainer());
26348 // if (!editor.toolbars || !editor.toolbars.length) {
26349 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26352 // for (var i =0 ; i < editor.toolbars.length;i++) {
26353 // editor.toolbars[i] = Roo.factory(
26354 // typeof(editor.toolbars[i]) == 'string' ?
26355 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26356 // Roo.bootstrap.HtmlEditor);
26357 // editor.toolbars[i].init(editor);
26363 onRender : function(ct, position)
26365 // Roo.log("Call onRender: " + this.xtype);
26367 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26369 this.wrap = this.inputEl().wrap({
26370 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26373 this.editorcore.onRender(ct, position);
26375 if (this.resizable) {
26376 this.resizeEl = new Roo.Resizable(this.wrap, {
26380 minHeight : this.height,
26381 height: this.height,
26382 handles : this.resizable,
26385 resize : function(r, w, h) {
26386 _t.onResize(w,h); // -something
26392 this.createToolbar(this);
26395 if(!this.width && this.resizable){
26396 this.setSize(this.wrap.getSize());
26398 if (this.resizeEl) {
26399 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26400 // should trigger onReize..
26406 onResize : function(w, h)
26408 Roo.log('resize: ' +w + ',' + h );
26409 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26413 if(this.inputEl() ){
26414 if(typeof w == 'number'){
26415 var aw = w - this.wrap.getFrameWidth('lr');
26416 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26419 if(typeof h == 'number'){
26420 var tbh = -11; // fixme it needs to tool bar size!
26421 for (var i =0; i < this.toolbars.length;i++) {
26422 // fixme - ask toolbars for heights?
26423 tbh += this.toolbars[i].el.getHeight();
26424 //if (this.toolbars[i].footer) {
26425 // tbh += this.toolbars[i].footer.el.getHeight();
26433 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26434 ah -= 5; // knock a few pixes off for look..
26435 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26439 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26440 this.editorcore.onResize(ew,eh);
26445 * Toggles the editor between standard and source edit mode.
26446 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26448 toggleSourceEdit : function(sourceEditMode)
26450 this.editorcore.toggleSourceEdit(sourceEditMode);
26452 if(this.editorcore.sourceEditMode){
26453 Roo.log('editor - showing textarea');
26456 // Roo.log(this.syncValue());
26458 this.inputEl().removeClass(['hide', 'x-hidden']);
26459 this.inputEl().dom.removeAttribute('tabIndex');
26460 this.inputEl().focus();
26462 Roo.log('editor - hiding textarea');
26464 // Roo.log(this.pushValue());
26467 this.inputEl().addClass(['hide', 'x-hidden']);
26468 this.inputEl().dom.setAttribute('tabIndex', -1);
26469 //this.deferFocus();
26472 if(this.resizable){
26473 this.setSize(this.wrap.getSize());
26476 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26479 // private (for BoxComponent)
26480 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26482 // private (for BoxComponent)
26483 getResizeEl : function(){
26487 // private (for BoxComponent)
26488 getPositionEl : function(){
26493 initEvents : function(){
26494 this.originalValue = this.getValue();
26498 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26501 // markInvalid : Roo.emptyFn,
26503 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26506 // clearInvalid : Roo.emptyFn,
26508 setValue : function(v){
26509 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26510 this.editorcore.pushValue();
26515 deferFocus : function(){
26516 this.focus.defer(10, this);
26520 focus : function(){
26521 this.editorcore.focus();
26527 onDestroy : function(){
26533 for (var i =0; i < this.toolbars.length;i++) {
26534 // fixme - ask toolbars for heights?
26535 this.toolbars[i].onDestroy();
26538 this.wrap.dom.innerHTML = '';
26539 this.wrap.remove();
26544 onFirstFocus : function(){
26545 //Roo.log("onFirstFocus");
26546 this.editorcore.onFirstFocus();
26547 for (var i =0; i < this.toolbars.length;i++) {
26548 this.toolbars[i].onFirstFocus();
26554 syncValue : function()
26556 this.editorcore.syncValue();
26559 pushValue : function()
26561 this.editorcore.pushValue();
26565 // hide stuff that is not compatible
26579 * @event specialkey
26583 * @cfg {String} fieldClass @hide
26586 * @cfg {String} focusClass @hide
26589 * @cfg {String} autoCreate @hide
26592 * @cfg {String} inputType @hide
26596 * @cfg {String} invalidText @hide
26599 * @cfg {String} msgFx @hide
26602 * @cfg {String} validateOnBlur @hide
26611 Roo.namespace('Roo.bootstrap.htmleditor');
26613 * @class Roo.bootstrap.HtmlEditorToolbar1
26619 new Roo.bootstrap.HtmlEditor({
26622 new Roo.bootstrap.HtmlEditorToolbar1({
26623 disable : { fonts: 1 , format: 1, ..., ... , ...],
26629 * @cfg {Object} disable List of elements to disable..
26630 * @cfg {Array} btns List of additional buttons.
26634 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26637 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26640 Roo.apply(this, config);
26642 // default disabled, based on 'good practice'..
26643 this.disable = this.disable || {};
26644 Roo.applyIf(this.disable, {
26647 specialElements : true
26649 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26651 this.editor = config.editor;
26652 this.editorcore = config.editor.editorcore;
26654 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26656 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26657 // dont call parent... till later.
26659 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26664 editorcore : false,
26669 "h1","h2","h3","h4","h5","h6",
26671 "abbr", "acronym", "address", "cite", "samp", "var",
26675 onRender : function(ct, position)
26677 // Roo.log("Call onRender: " + this.xtype);
26679 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26681 this.el.dom.style.marginBottom = '0';
26683 var editorcore = this.editorcore;
26684 var editor= this.editor;
26687 var btn = function(id,cmd , toggle, handler, html){
26689 var event = toggle ? 'toggle' : 'click';
26694 xns: Roo.bootstrap,
26698 enableToggle:toggle !== false,
26700 pressed : toggle ? false : null,
26703 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26704 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26710 // var cb_box = function...
26715 xns: Roo.bootstrap,
26720 xns: Roo.bootstrap,
26724 Roo.each(this.formats, function(f) {
26725 style.menu.items.push({
26727 xns: Roo.bootstrap,
26728 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26733 editorcore.insertTag(this.tagname);
26740 children.push(style);
26742 btn('bold',false,true);
26743 btn('italic',false,true);
26744 btn('align-left', 'justifyleft',true);
26745 btn('align-center', 'justifycenter',true);
26746 btn('align-right' , 'justifyright',true);
26747 btn('link', false, false, function(btn) {
26748 //Roo.log("create link?");
26749 var url = prompt(this.createLinkText, this.defaultLinkValue);
26750 if(url && url != 'http:/'+'/'){
26751 this.editorcore.relayCmd('createlink', url);
26754 btn('list','insertunorderedlist',true);
26755 btn('pencil', false,true, function(btn){
26757 this.toggleSourceEdit(btn.pressed);
26760 if (this.editor.btns.length > 0) {
26761 for (var i = 0; i<this.editor.btns.length; i++) {
26762 children.push(this.editor.btns[i]);
26770 xns: Roo.bootstrap,
26775 xns: Roo.bootstrap,
26780 cog.menu.items.push({
26782 xns: Roo.bootstrap,
26783 html : Clean styles,
26788 editorcore.insertTag(this.tagname);
26797 this.xtype = 'NavSimplebar';
26799 for(var i=0;i< children.length;i++) {
26801 this.buttons.add(this.addxtypeChild(children[i]));
26805 editor.on('editorevent', this.updateToolbar, this);
26807 onBtnClick : function(id)
26809 this.editorcore.relayCmd(id);
26810 this.editorcore.focus();
26814 * Protected method that will not generally be called directly. It triggers
26815 * a toolbar update by reading the markup state of the current selection in the editor.
26817 updateToolbar: function(){
26819 if(!this.editorcore.activated){
26820 this.editor.onFirstFocus(); // is this neeed?
26824 var btns = this.buttons;
26825 var doc = this.editorcore.doc;
26826 btns.get('bold').setActive(doc.queryCommandState('bold'));
26827 btns.get('italic').setActive(doc.queryCommandState('italic'));
26828 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26830 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26831 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26832 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26834 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26835 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26838 var ans = this.editorcore.getAllAncestors();
26839 if (this.formatCombo) {
26842 var store = this.formatCombo.store;
26843 this.formatCombo.setValue("");
26844 for (var i =0; i < ans.length;i++) {
26845 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26847 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26855 // hides menus... - so this cant be on a menu...
26856 Roo.bootstrap.MenuMgr.hideAll();
26858 Roo.bootstrap.MenuMgr.hideAll();
26859 //this.editorsyncValue();
26861 onFirstFocus: function() {
26862 this.buttons.each(function(item){
26866 toggleSourceEdit : function(sourceEditMode){
26869 if(sourceEditMode){
26870 Roo.log("disabling buttons");
26871 this.buttons.each( function(item){
26872 if(item.cmd != 'pencil'){
26878 Roo.log("enabling buttons");
26879 if(this.editorcore.initialized){
26880 this.buttons.each( function(item){
26886 Roo.log("calling toggole on editor");
26887 // tell the editor that it's been pressed..
26888 this.editor.toggleSourceEdit(sourceEditMode);
26902 * @class Roo.bootstrap.Markdown
26903 * @extends Roo.bootstrap.TextArea
26904 * Bootstrap Showdown editable area
26905 * @cfg {string} content
26908 * Create a new Showdown
26911 Roo.bootstrap.Markdown = function(config){
26912 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26916 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26920 initEvents : function()
26923 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26924 this.markdownEl = this.el.createChild({
26925 cls : 'roo-markdown-area'
26927 this.inputEl().addClass('d-none');
26928 if (this.getValue() == '') {
26929 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()));
26934 this.markdownEl.on('click', this.toggleTextEdit, this);
26935 this.on('blur', this.toggleTextEdit, this);
26936 this.on('specialkey', this.resizeTextArea, this);
26939 toggleTextEdit : function()
26941 var sh = this.markdownEl.getHeight();
26942 this.inputEl().addClass('d-none');
26943 this.markdownEl.addClass('d-none');
26944 if (!this.editing) {
26946 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26947 this.inputEl().removeClass('d-none');
26948 this.inputEl().focus();
26949 this.editing = true;
26952 // show showdown...
26953 this.updateMarkdown();
26954 this.markdownEl.removeClass('d-none');
26955 this.editing = false;
26958 updateMarkdown : function()
26960 if (this.getValue() == '') {
26961 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26965 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26968 resizeTextArea: function () {
26971 Roo.log([sh, this.getValue().split("\n").length * 30]);
26972 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26974 setValue : function(val)
26976 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26977 if (!this.editing) {
26978 this.updateMarkdown();
26984 if (!this.editing) {
26985 this.toggleTextEdit();
26993 * @class Roo.bootstrap.Table.AbstractSelectionModel
26994 * @extends Roo.util.Observable
26995 * Abstract base class for grid SelectionModels. It provides the interface that should be
26996 * implemented by descendant classes. This class should not be directly instantiated.
26999 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27000 this.locked = false;
27001 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27005 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27006 /** @ignore Called by the grid automatically. Do not call directly. */
27007 init : function(grid){
27013 * Locks the selections.
27016 this.locked = true;
27020 * Unlocks the selections.
27022 unlock : function(){
27023 this.locked = false;
27027 * Returns true if the selections are locked.
27028 * @return {Boolean}
27030 isLocked : function(){
27031 return this.locked;
27035 initEvents : function ()
27041 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27042 * @class Roo.bootstrap.Table.RowSelectionModel
27043 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27044 * It supports multiple selections and keyboard selection/navigation.
27046 * @param {Object} config
27049 Roo.bootstrap.Table.RowSelectionModel = function(config){
27050 Roo.apply(this, config);
27051 this.selections = new Roo.util.MixedCollection(false, function(o){
27056 this.lastActive = false;
27060 * @event selectionchange
27061 * Fires when the selection changes
27062 * @param {SelectionModel} this
27064 "selectionchange" : true,
27066 * @event afterselectionchange
27067 * Fires after the selection changes (eg. by key press or clicking)
27068 * @param {SelectionModel} this
27070 "afterselectionchange" : true,
27072 * @event beforerowselect
27073 * Fires when a row is selected being selected, return false to cancel.
27074 * @param {SelectionModel} this
27075 * @param {Number} rowIndex The selected index
27076 * @param {Boolean} keepExisting False if other selections will be cleared
27078 "beforerowselect" : true,
27081 * Fires when a row is selected.
27082 * @param {SelectionModel} this
27083 * @param {Number} rowIndex The selected index
27084 * @param {Roo.data.Record} r The record
27086 "rowselect" : true,
27088 * @event rowdeselect
27089 * Fires when a row is deselected.
27090 * @param {SelectionModel} this
27091 * @param {Number} rowIndex The selected index
27093 "rowdeselect" : true
27095 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27096 this.locked = false;
27099 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27101 * @cfg {Boolean} singleSelect
27102 * True to allow selection of only one row at a time (defaults to false)
27104 singleSelect : false,
27107 initEvents : function()
27110 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27111 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27112 //}else{ // allow click to work like normal
27113 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27115 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27116 this.grid.on("rowclick", this.handleMouseDown, this);
27118 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27119 "up" : function(e){
27121 this.selectPrevious(e.shiftKey);
27122 }else if(this.last !== false && this.lastActive !== false){
27123 var last = this.last;
27124 this.selectRange(this.last, this.lastActive-1);
27125 this.grid.getView().focusRow(this.lastActive);
27126 if(last !== false){
27130 this.selectFirstRow();
27132 this.fireEvent("afterselectionchange", this);
27134 "down" : function(e){
27136 this.selectNext(e.shiftKey);
27137 }else if(this.last !== false && this.lastActive !== false){
27138 var last = this.last;
27139 this.selectRange(this.last, this.lastActive+1);
27140 this.grid.getView().focusRow(this.lastActive);
27141 if(last !== false){
27145 this.selectFirstRow();
27147 this.fireEvent("afterselectionchange", this);
27151 this.grid.store.on('load', function(){
27152 this.selections.clear();
27155 var view = this.grid.view;
27156 view.on("refresh", this.onRefresh, this);
27157 view.on("rowupdated", this.onRowUpdated, this);
27158 view.on("rowremoved", this.onRemove, this);
27163 onRefresh : function()
27165 var ds = this.grid.store, i, v = this.grid.view;
27166 var s = this.selections;
27167 s.each(function(r){
27168 if((i = ds.indexOfId(r.id)) != -1){
27177 onRemove : function(v, index, r){
27178 this.selections.remove(r);
27182 onRowUpdated : function(v, index, r){
27183 if(this.isSelected(r)){
27184 v.onRowSelect(index);
27190 * @param {Array} records The records to select
27191 * @param {Boolean} keepExisting (optional) True to keep existing selections
27193 selectRecords : function(records, keepExisting)
27196 this.clearSelections();
27198 var ds = this.grid.store;
27199 for(var i = 0, len = records.length; i < len; i++){
27200 this.selectRow(ds.indexOf(records[i]), true);
27205 * Gets the number of selected rows.
27208 getCount : function(){
27209 return this.selections.length;
27213 * Selects the first row in the grid.
27215 selectFirstRow : function(){
27220 * Select the last row.
27221 * @param {Boolean} keepExisting (optional) True to keep existing selections
27223 selectLastRow : function(keepExisting){
27224 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27225 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27229 * Selects the row immediately following the last selected row.
27230 * @param {Boolean} keepExisting (optional) True to keep existing selections
27232 selectNext : function(keepExisting)
27234 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27235 this.selectRow(this.last+1, keepExisting);
27236 this.grid.getView().focusRow(this.last);
27241 * Selects the row that precedes the last selected row.
27242 * @param {Boolean} keepExisting (optional) True to keep existing selections
27244 selectPrevious : function(keepExisting){
27246 this.selectRow(this.last-1, keepExisting);
27247 this.grid.getView().focusRow(this.last);
27252 * Returns the selected records
27253 * @return {Array} Array of selected records
27255 getSelections : function(){
27256 return [].concat(this.selections.items);
27260 * Returns the first selected record.
27263 getSelected : function(){
27264 return this.selections.itemAt(0);
27269 * Clears all selections.
27271 clearSelections : function(fast)
27277 var ds = this.grid.store;
27278 var s = this.selections;
27279 s.each(function(r){
27280 this.deselectRow(ds.indexOfId(r.id));
27284 this.selections.clear();
27291 * Selects all rows.
27293 selectAll : function(){
27297 this.selections.clear();
27298 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27299 this.selectRow(i, true);
27304 * Returns True if there is a selection.
27305 * @return {Boolean}
27307 hasSelection : function(){
27308 return this.selections.length > 0;
27312 * Returns True if the specified row is selected.
27313 * @param {Number/Record} record The record or index of the record to check
27314 * @return {Boolean}
27316 isSelected : function(index){
27317 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27318 return (r && this.selections.key(r.id) ? true : false);
27322 * Returns True if the specified record id is selected.
27323 * @param {String} id The id of record to check
27324 * @return {Boolean}
27326 isIdSelected : function(id){
27327 return (this.selections.key(id) ? true : false);
27332 handleMouseDBClick : function(e, t){
27336 handleMouseDown : function(e, t)
27338 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27339 if(this.isLocked() || rowIndex < 0 ){
27342 if(e.shiftKey && this.last !== false){
27343 var last = this.last;
27344 this.selectRange(last, rowIndex, e.ctrlKey);
27345 this.last = last; // reset the last
27349 var isSelected = this.isSelected(rowIndex);
27350 //Roo.log("select row:" + rowIndex);
27352 this.deselectRow(rowIndex);
27354 this.selectRow(rowIndex, true);
27358 if(e.button !== 0 && isSelected){
27359 alert('rowIndex 2: ' + rowIndex);
27360 view.focusRow(rowIndex);
27361 }else if(e.ctrlKey && isSelected){
27362 this.deselectRow(rowIndex);
27363 }else if(!isSelected){
27364 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27365 view.focusRow(rowIndex);
27369 this.fireEvent("afterselectionchange", this);
27372 handleDragableRowClick : function(grid, rowIndex, e)
27374 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27375 this.selectRow(rowIndex, false);
27376 grid.view.focusRow(rowIndex);
27377 this.fireEvent("afterselectionchange", this);
27382 * Selects multiple rows.
27383 * @param {Array} rows Array of the indexes of the row to select
27384 * @param {Boolean} keepExisting (optional) True to keep existing selections
27386 selectRows : function(rows, keepExisting){
27388 this.clearSelections();
27390 for(var i = 0, len = rows.length; i < len; i++){
27391 this.selectRow(rows[i], true);
27396 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27397 * @param {Number} startRow The index of the first row in the range
27398 * @param {Number} endRow The index of the last row in the range
27399 * @param {Boolean} keepExisting (optional) True to retain existing selections
27401 selectRange : function(startRow, endRow, keepExisting){
27406 this.clearSelections();
27408 if(startRow <= endRow){
27409 for(var i = startRow; i <= endRow; i++){
27410 this.selectRow(i, true);
27413 for(var i = startRow; i >= endRow; i--){
27414 this.selectRow(i, true);
27420 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27421 * @param {Number} startRow The index of the first row in the range
27422 * @param {Number} endRow The index of the last row in the range
27424 deselectRange : function(startRow, endRow, preventViewNotify){
27428 for(var i = startRow; i <= endRow; i++){
27429 this.deselectRow(i, preventViewNotify);
27435 * @param {Number} row The index of the row to select
27436 * @param {Boolean} keepExisting (optional) True to keep existing selections
27438 selectRow : function(index, keepExisting, preventViewNotify)
27440 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27443 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27444 if(!keepExisting || this.singleSelect){
27445 this.clearSelections();
27448 var r = this.grid.store.getAt(index);
27449 //console.log('selectRow - record id :' + r.id);
27451 this.selections.add(r);
27452 this.last = this.lastActive = index;
27453 if(!preventViewNotify){
27454 var proxy = new Roo.Element(
27455 this.grid.getRowDom(index)
27457 proxy.addClass('bg-info info');
27459 this.fireEvent("rowselect", this, index, r);
27460 this.fireEvent("selectionchange", this);
27466 * @param {Number} row The index of the row to deselect
27468 deselectRow : function(index, preventViewNotify)
27473 if(this.last == index){
27476 if(this.lastActive == index){
27477 this.lastActive = false;
27480 var r = this.grid.store.getAt(index);
27485 this.selections.remove(r);
27486 //.console.log('deselectRow - record id :' + r.id);
27487 if(!preventViewNotify){
27489 var proxy = new Roo.Element(
27490 this.grid.getRowDom(index)
27492 proxy.removeClass('bg-info info');
27494 this.fireEvent("rowdeselect", this, index);
27495 this.fireEvent("selectionchange", this);
27499 restoreLast : function(){
27501 this.last = this._last;
27506 acceptsNav : function(row, col, cm){
27507 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27511 onEditorKey : function(field, e){
27512 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27517 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27519 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27521 }else if(k == e.ENTER && !e.ctrlKey){
27525 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27527 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27529 }else if(k == e.ESC){
27533 g.startEditing(newCell[0], newCell[1]);
27539 * Ext JS Library 1.1.1
27540 * Copyright(c) 2006-2007, Ext JS, LLC.
27542 * Originally Released Under LGPL - original licence link has changed is not relivant.
27545 * <script type="text/javascript">
27549 * @class Roo.bootstrap.PagingToolbar
27550 * @extends Roo.bootstrap.NavSimplebar
27551 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27553 * Create a new PagingToolbar
27554 * @param {Object} config The config object
27555 * @param {Roo.data.Store} store
27557 Roo.bootstrap.PagingToolbar = function(config)
27559 // old args format still supported... - xtype is prefered..
27560 // created from xtype...
27562 this.ds = config.dataSource;
27564 if (config.store && !this.ds) {
27565 this.store= Roo.factory(config.store, Roo.data);
27566 this.ds = this.store;
27567 this.ds.xmodule = this.xmodule || false;
27570 this.toolbarItems = [];
27571 if (config.items) {
27572 this.toolbarItems = config.items;
27575 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27580 this.bind(this.ds);
27583 if (Roo.bootstrap.version == 4) {
27584 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27586 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27591 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27593 * @cfg {Roo.data.Store} dataSource
27594 * The underlying data store providing the paged data
27597 * @cfg {String/HTMLElement/Element} container
27598 * container The id or element that will contain the toolbar
27601 * @cfg {Boolean} displayInfo
27602 * True to display the displayMsg (defaults to false)
27605 * @cfg {Number} pageSize
27606 * The number of records to display per page (defaults to 20)
27610 * @cfg {String} displayMsg
27611 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27613 displayMsg : 'Displaying {0} - {1} of {2}',
27615 * @cfg {String} emptyMsg
27616 * The message to display when no records are found (defaults to "No data to display")
27618 emptyMsg : 'No data to display',
27620 * Customizable piece of the default paging text (defaults to "Page")
27623 beforePageText : "Page",
27625 * Customizable piece of the default paging text (defaults to "of %0")
27628 afterPageText : "of {0}",
27630 * Customizable piece of the default paging text (defaults to "First Page")
27633 firstText : "First Page",
27635 * Customizable piece of the default paging text (defaults to "Previous Page")
27638 prevText : "Previous Page",
27640 * Customizable piece of the default paging text (defaults to "Next Page")
27643 nextText : "Next Page",
27645 * Customizable piece of the default paging text (defaults to "Last Page")
27648 lastText : "Last Page",
27650 * Customizable piece of the default paging text (defaults to "Refresh")
27653 refreshText : "Refresh",
27657 onRender : function(ct, position)
27659 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27660 this.navgroup.parentId = this.id;
27661 this.navgroup.onRender(this.el, null);
27662 // add the buttons to the navgroup
27664 if(this.displayInfo){
27665 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27666 this.displayEl = this.el.select('.x-paging-info', true).first();
27667 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27668 // this.displayEl = navel.el.select('span',true).first();
27674 Roo.each(_this.buttons, function(e){ // this might need to use render????
27675 Roo.factory(e).render(_this.el);
27679 Roo.each(_this.toolbarItems, function(e) {
27680 _this.navgroup.addItem(e);
27684 this.first = this.navgroup.addItem({
27685 tooltip: this.firstText,
27686 cls: "prev btn-outline-secondary",
27687 html : ' <i class="fa fa-step-backward"></i>',
27689 preventDefault: true,
27690 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27693 this.prev = this.navgroup.addItem({
27694 tooltip: this.prevText,
27695 cls: "prev btn-outline-secondary",
27696 html : ' <i class="fa fa-backward"></i>',
27698 preventDefault: true,
27699 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27701 //this.addSeparator();
27704 var field = this.navgroup.addItem( {
27706 cls : 'x-paging-position btn-outline-secondary',
27708 html : this.beforePageText +
27709 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27710 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27713 this.field = field.el.select('input', true).first();
27714 this.field.on("keydown", this.onPagingKeydown, this);
27715 this.field.on("focus", function(){this.dom.select();});
27718 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27719 //this.field.setHeight(18);
27720 //this.addSeparator();
27721 this.next = this.navgroup.addItem({
27722 tooltip: this.nextText,
27723 cls: "next btn-outline-secondary",
27724 html : ' <i class="fa fa-forward"></i>',
27726 preventDefault: true,
27727 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27729 this.last = this.navgroup.addItem({
27730 tooltip: this.lastText,
27731 html : ' <i class="fa fa-step-forward"></i>',
27732 cls: "next btn-outline-secondary",
27734 preventDefault: true,
27735 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27737 //this.addSeparator();
27738 this.loading = this.navgroup.addItem({
27739 tooltip: this.refreshText,
27740 cls: "btn-outline-secondary",
27741 html : ' <i class="fa fa-refresh"></i>',
27742 preventDefault: true,
27743 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27749 updateInfo : function(){
27750 if(this.displayEl){
27751 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27752 var msg = count == 0 ?
27756 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27758 this.displayEl.update(msg);
27763 onLoad : function(ds, r, o)
27765 this.cursor = o.params && o.params.start ? o.params.start : 0;
27767 var d = this.getPageData(),
27772 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27773 this.field.dom.value = ap;
27774 this.first.setDisabled(ap == 1);
27775 this.prev.setDisabled(ap == 1);
27776 this.next.setDisabled(ap == ps);
27777 this.last.setDisabled(ap == ps);
27778 this.loading.enable();
27783 getPageData : function(){
27784 var total = this.ds.getTotalCount();
27787 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27788 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27793 onLoadError : function(){
27794 this.loading.enable();
27798 onPagingKeydown : function(e){
27799 var k = e.getKey();
27800 var d = this.getPageData();
27802 var v = this.field.dom.value, pageNum;
27803 if(!v || isNaN(pageNum = parseInt(v, 10))){
27804 this.field.dom.value = d.activePage;
27807 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27808 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27811 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))
27813 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27814 this.field.dom.value = pageNum;
27815 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27818 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27820 var v = this.field.dom.value, pageNum;
27821 var increment = (e.shiftKey) ? 10 : 1;
27822 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27825 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27826 this.field.dom.value = d.activePage;
27829 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27831 this.field.dom.value = parseInt(v, 10) + increment;
27832 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27833 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27840 beforeLoad : function(){
27842 this.loading.disable();
27847 onClick : function(which){
27856 ds.load({params:{start: 0, limit: this.pageSize}});
27859 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27862 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27865 var total = ds.getTotalCount();
27866 var extra = total % this.pageSize;
27867 var lastStart = extra ? (total - extra) : total-this.pageSize;
27868 ds.load({params:{start: lastStart, limit: this.pageSize}});
27871 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27877 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27878 * @param {Roo.data.Store} store The data store to unbind
27880 unbind : function(ds){
27881 ds.un("beforeload", this.beforeLoad, this);
27882 ds.un("load", this.onLoad, this);
27883 ds.un("loadexception", this.onLoadError, this);
27884 ds.un("remove", this.updateInfo, this);
27885 ds.un("add", this.updateInfo, this);
27886 this.ds = undefined;
27890 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27891 * @param {Roo.data.Store} store The data store to bind
27893 bind : function(ds){
27894 ds.on("beforeload", this.beforeLoad, this);
27895 ds.on("load", this.onLoad, this);
27896 ds.on("loadexception", this.onLoadError, this);
27897 ds.on("remove", this.updateInfo, this);
27898 ds.on("add", this.updateInfo, this);
27909 * @class Roo.bootstrap.MessageBar
27910 * @extends Roo.bootstrap.Component
27911 * Bootstrap MessageBar class
27912 * @cfg {String} html contents of the MessageBar
27913 * @cfg {String} weight (info | success | warning | danger) default info
27914 * @cfg {String} beforeClass insert the bar before the given class
27915 * @cfg {Boolean} closable (true | false) default false
27916 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27919 * Create a new Element
27920 * @param {Object} config The config object
27923 Roo.bootstrap.MessageBar = function(config){
27924 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27927 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27933 beforeClass: 'bootstrap-sticky-wrap',
27935 getAutoCreate : function(){
27939 cls: 'alert alert-dismissable alert-' + this.weight,
27944 html: this.html || ''
27950 cfg.cls += ' alert-messages-fixed';
27964 onRender : function(ct, position)
27966 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27969 var cfg = Roo.apply({}, this.getAutoCreate());
27973 cfg.cls += ' ' + this.cls;
27976 cfg.style = this.style;
27978 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27980 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27983 this.el.select('>button.close').on('click', this.hide, this);
27989 if (!this.rendered) {
27995 this.fireEvent('show', this);
28001 if (!this.rendered) {
28007 this.fireEvent('hide', this);
28010 update : function()
28012 // var e = this.el.dom.firstChild;
28014 // if(this.closable){
28015 // e = e.nextSibling;
28018 // e.data = this.html || '';
28020 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28036 * @class Roo.bootstrap.Graph
28037 * @extends Roo.bootstrap.Component
28038 * Bootstrap Graph class
28042 @cfg {String} graphtype bar | vbar | pie
28043 @cfg {number} g_x coodinator | centre x (pie)
28044 @cfg {number} g_y coodinator | centre y (pie)
28045 @cfg {number} g_r radius (pie)
28046 @cfg {number} g_height height of the chart (respected by all elements in the set)
28047 @cfg {number} g_width width of the chart (respected by all elements in the set)
28048 @cfg {Object} title The title of the chart
28051 -opts (object) options for the chart
28053 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28054 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28056 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.
28057 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28059 o stretch (boolean)
28061 -opts (object) options for the pie
28064 o startAngle (number)
28065 o endAngle (number)
28069 * Create a new Input
28070 * @param {Object} config The config object
28073 Roo.bootstrap.Graph = function(config){
28074 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28080 * The img click event for the img.
28081 * @param {Roo.EventObject} e
28087 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28098 //g_colors: this.colors,
28105 getAutoCreate : function(){
28116 onRender : function(ct,position){
28119 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28121 if (typeof(Raphael) == 'undefined') {
28122 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28126 this.raphael = Raphael(this.el.dom);
28128 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28129 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28130 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28131 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28133 r.text(160, 10, "Single Series Chart").attr(txtattr);
28134 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28135 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28136 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28138 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28139 r.barchart(330, 10, 300, 220, data1);
28140 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28141 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28144 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28145 // r.barchart(30, 30, 560, 250, xdata, {
28146 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28147 // axis : "0 0 1 1",
28148 // axisxlabels : xdata
28149 // //yvalues : cols,
28152 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28154 // this.load(null,xdata,{
28155 // axis : "0 0 1 1",
28156 // axisxlabels : xdata
28161 load : function(graphtype,xdata,opts)
28163 this.raphael.clear();
28165 graphtype = this.graphtype;
28170 var r = this.raphael,
28171 fin = function () {
28172 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28174 fout = function () {
28175 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28177 pfin = function() {
28178 this.sector.stop();
28179 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28182 this.label[0].stop();
28183 this.label[0].attr({ r: 7.5 });
28184 this.label[1].attr({ "font-weight": 800 });
28187 pfout = function() {
28188 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28191 this.label[0].animate({ r: 5 }, 500, "bounce");
28192 this.label[1].attr({ "font-weight": 400 });
28198 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28201 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28204 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28205 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28207 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28214 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28219 setTitle: function(o)
28224 initEvents: function() {
28227 this.el.on('click', this.onClick, this);
28231 onClick : function(e)
28233 Roo.log('img onclick');
28234 this.fireEvent('click', this, e);
28246 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28249 * @class Roo.bootstrap.dash.NumberBox
28250 * @extends Roo.bootstrap.Component
28251 * Bootstrap NumberBox class
28252 * @cfg {String} headline Box headline
28253 * @cfg {String} content Box content
28254 * @cfg {String} icon Box icon
28255 * @cfg {String} footer Footer text
28256 * @cfg {String} fhref Footer href
28259 * Create a new NumberBox
28260 * @param {Object} config The config object
28264 Roo.bootstrap.dash.NumberBox = function(config){
28265 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28269 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28278 getAutoCreate : function(){
28282 cls : 'small-box ',
28290 cls : 'roo-headline',
28291 html : this.headline
28295 cls : 'roo-content',
28296 html : this.content
28310 cls : 'ion ' + this.icon
28319 cls : 'small-box-footer',
28320 href : this.fhref || '#',
28324 cfg.cn.push(footer);
28331 onRender : function(ct,position){
28332 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28339 setHeadline: function (value)
28341 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28344 setFooter: function (value, href)
28346 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28349 this.el.select('a.small-box-footer',true).first().attr('href', href);
28354 setContent: function (value)
28356 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28359 initEvents: function()
28373 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28376 * @class Roo.bootstrap.dash.TabBox
28377 * @extends Roo.bootstrap.Component
28378 * Bootstrap TabBox class
28379 * @cfg {String} title Title of the TabBox
28380 * @cfg {String} icon Icon of the TabBox
28381 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28382 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28385 * Create a new TabBox
28386 * @param {Object} config The config object
28390 Roo.bootstrap.dash.TabBox = function(config){
28391 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28396 * When a pane is added
28397 * @param {Roo.bootstrap.dash.TabPane} pane
28401 * @event activatepane
28402 * When a pane is activated
28403 * @param {Roo.bootstrap.dash.TabPane} pane
28405 "activatepane" : true
28413 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28418 tabScrollable : false,
28420 getChildContainer : function()
28422 return this.el.select('.tab-content', true).first();
28425 getAutoCreate : function(){
28429 cls: 'pull-left header',
28437 cls: 'fa ' + this.icon
28443 cls: 'nav nav-tabs pull-right',
28449 if(this.tabScrollable){
28456 cls: 'nav nav-tabs pull-right',
28467 cls: 'nav-tabs-custom',
28472 cls: 'tab-content no-padding',
28480 initEvents : function()
28482 //Roo.log('add add pane handler');
28483 this.on('addpane', this.onAddPane, this);
28486 * Updates the box title
28487 * @param {String} html to set the title to.
28489 setTitle : function(value)
28491 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28493 onAddPane : function(pane)
28495 this.panes.push(pane);
28496 //Roo.log('addpane');
28498 // tabs are rendere left to right..
28499 if(!this.showtabs){
28503 var ctr = this.el.select('.nav-tabs', true).first();
28506 var existing = ctr.select('.nav-tab',true);
28507 var qty = existing.getCount();;
28510 var tab = ctr.createChild({
28512 cls : 'nav-tab' + (qty ? '' : ' active'),
28520 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28523 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28525 pane.el.addClass('active');
28530 onTabClick : function(ev,un,ob,pane)
28532 //Roo.log('tab - prev default');
28533 ev.preventDefault();
28536 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28537 pane.tab.addClass('active');
28538 //Roo.log(pane.title);
28539 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28540 // technically we should have a deactivate event.. but maybe add later.
28541 // and it should not de-activate the selected tab...
28542 this.fireEvent('activatepane', pane);
28543 pane.el.addClass('active');
28544 pane.fireEvent('activate');
28549 getActivePane : function()
28552 Roo.each(this.panes, function(p) {
28553 if(p.el.hasClass('active')){
28574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28576 * @class Roo.bootstrap.TabPane
28577 * @extends Roo.bootstrap.Component
28578 * Bootstrap TabPane class
28579 * @cfg {Boolean} active (false | true) Default false
28580 * @cfg {String} title title of panel
28584 * Create a new TabPane
28585 * @param {Object} config The config object
28588 Roo.bootstrap.dash.TabPane = function(config){
28589 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28595 * When a pane is activated
28596 * @param {Roo.bootstrap.dash.TabPane} pane
28603 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28608 // the tabBox that this is attached to.
28611 getAutoCreate : function()
28619 cfg.cls += ' active';
28624 initEvents : function()
28626 //Roo.log('trigger add pane handler');
28627 this.parent().fireEvent('addpane', this)
28631 * Updates the tab title
28632 * @param {String} html to set the title to.
28634 setTitle: function(str)
28640 this.tab.select('a', true).first().dom.innerHTML = str;
28657 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28660 * @class Roo.bootstrap.menu.Menu
28661 * @extends Roo.bootstrap.Component
28662 * Bootstrap Menu class - container for Menu
28663 * @cfg {String} html Text of the menu
28664 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28665 * @cfg {String} icon Font awesome icon
28666 * @cfg {String} pos Menu align to (top | bottom) default bottom
28670 * Create a new Menu
28671 * @param {Object} config The config object
28675 Roo.bootstrap.menu.Menu = function(config){
28676 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28680 * @event beforeshow
28681 * Fires before this menu is displayed
28682 * @param {Roo.bootstrap.menu.Menu} this
28686 * @event beforehide
28687 * Fires before this menu is hidden
28688 * @param {Roo.bootstrap.menu.Menu} this
28693 * Fires after this menu is displayed
28694 * @param {Roo.bootstrap.menu.Menu} this
28699 * Fires after this menu is hidden
28700 * @param {Roo.bootstrap.menu.Menu} this
28705 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28706 * @param {Roo.bootstrap.menu.Menu} this
28707 * @param {Roo.EventObject} e
28714 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28718 weight : 'default',
28723 getChildContainer : function() {
28724 if(this.isSubMenu){
28728 return this.el.select('ul.dropdown-menu', true).first();
28731 getAutoCreate : function()
28736 cls : 'roo-menu-text',
28744 cls : 'fa ' + this.icon
28755 cls : 'dropdown-button btn btn-' + this.weight,
28760 cls : 'dropdown-toggle btn btn-' + this.weight,
28770 cls : 'dropdown-menu'
28776 if(this.pos == 'top'){
28777 cfg.cls += ' dropup';
28780 if(this.isSubMenu){
28783 cls : 'dropdown-menu'
28790 onRender : function(ct, position)
28792 this.isSubMenu = ct.hasClass('dropdown-submenu');
28794 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28797 initEvents : function()
28799 if(this.isSubMenu){
28803 this.hidden = true;
28805 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28806 this.triggerEl.on('click', this.onTriggerPress, this);
28808 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28809 this.buttonEl.on('click', this.onClick, this);
28815 if(this.isSubMenu){
28819 return this.el.select('ul.dropdown-menu', true).first();
28822 onClick : function(e)
28824 this.fireEvent("click", this, e);
28827 onTriggerPress : function(e)
28829 if (this.isVisible()) {
28836 isVisible : function(){
28837 return !this.hidden;
28842 this.fireEvent("beforeshow", this);
28844 this.hidden = false;
28845 this.el.addClass('open');
28847 Roo.get(document).on("mouseup", this.onMouseUp, this);
28849 this.fireEvent("show", this);
28856 this.fireEvent("beforehide", this);
28858 this.hidden = true;
28859 this.el.removeClass('open');
28861 Roo.get(document).un("mouseup", this.onMouseUp);
28863 this.fireEvent("hide", this);
28866 onMouseUp : function()
28880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28883 * @class Roo.bootstrap.menu.Item
28884 * @extends Roo.bootstrap.Component
28885 * Bootstrap MenuItem class
28886 * @cfg {Boolean} submenu (true | false) default false
28887 * @cfg {String} html text of the item
28888 * @cfg {String} href the link
28889 * @cfg {Boolean} disable (true | false) default false
28890 * @cfg {Boolean} preventDefault (true | false) default true
28891 * @cfg {String} icon Font awesome icon
28892 * @cfg {String} pos Submenu align to (left | right) default right
28896 * Create a new Item
28897 * @param {Object} config The config object
28901 Roo.bootstrap.menu.Item = function(config){
28902 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28906 * Fires when the mouse is hovering over this menu
28907 * @param {Roo.bootstrap.menu.Item} this
28908 * @param {Roo.EventObject} e
28913 * Fires when the mouse exits this menu
28914 * @param {Roo.bootstrap.menu.Item} this
28915 * @param {Roo.EventObject} e
28921 * The raw click event for the entire grid.
28922 * @param {Roo.EventObject} e
28928 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28933 preventDefault: true,
28938 getAutoCreate : function()
28943 cls : 'roo-menu-item-text',
28951 cls : 'fa ' + this.icon
28960 href : this.href || '#',
28967 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28971 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28973 if(this.pos == 'left'){
28974 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28981 initEvents : function()
28983 this.el.on('mouseover', this.onMouseOver, this);
28984 this.el.on('mouseout', this.onMouseOut, this);
28986 this.el.select('a', true).first().on('click', this.onClick, this);
28990 onClick : function(e)
28992 if(this.preventDefault){
28993 e.preventDefault();
28996 this.fireEvent("click", this, e);
28999 onMouseOver : function(e)
29001 if(this.submenu && this.pos == 'left'){
29002 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29005 this.fireEvent("mouseover", this, e);
29008 onMouseOut : function(e)
29010 this.fireEvent("mouseout", this, e);
29022 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29025 * @class Roo.bootstrap.menu.Separator
29026 * @extends Roo.bootstrap.Component
29027 * Bootstrap Separator class
29030 * Create a new Separator
29031 * @param {Object} config The config object
29035 Roo.bootstrap.menu.Separator = function(config){
29036 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29039 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29041 getAutoCreate : function(){
29044 cls: 'dropdown-divider divider'
29062 * @class Roo.bootstrap.Tooltip
29063 * Bootstrap Tooltip class
29064 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29065 * to determine which dom element triggers the tooltip.
29067 * It needs to add support for additional attributes like tooltip-position
29070 * Create a new Toolti
29071 * @param {Object} config The config object
29074 Roo.bootstrap.Tooltip = function(config){
29075 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29077 this.alignment = Roo.bootstrap.Tooltip.alignment;
29079 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29080 this.alignment = config.alignment;
29085 Roo.apply(Roo.bootstrap.Tooltip, {
29087 * @function init initialize tooltip monitoring.
29091 currentTip : false,
29092 currentRegion : false,
29098 Roo.get(document).on('mouseover', this.enter ,this);
29099 Roo.get(document).on('mouseout', this.leave, this);
29102 this.currentTip = new Roo.bootstrap.Tooltip();
29105 enter : function(ev)
29107 var dom = ev.getTarget();
29109 //Roo.log(['enter',dom]);
29110 var el = Roo.fly(dom);
29111 if (this.currentEl) {
29113 //Roo.log(this.currentEl);
29114 //Roo.log(this.currentEl.contains(dom));
29115 if (this.currentEl == el) {
29118 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29124 if (this.currentTip.el) {
29125 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29129 if(!el || el.dom == document){
29135 if (!el.attr('tooltip')) {
29136 pel = el.findParent("[tooltip]");
29138 bindEl = Roo.get(pel);
29144 // you can not look for children, as if el is the body.. then everythign is the child..
29145 if (!pel && !el.attr('tooltip')) { //
29146 if (!el.select("[tooltip]").elements.length) {
29149 // is the mouse over this child...?
29150 bindEl = el.select("[tooltip]").first();
29151 var xy = ev.getXY();
29152 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29153 //Roo.log("not in region.");
29156 //Roo.log("child element over..");
29159 this.currentEl = el;
29160 this.currentTip.bind(bindEl);
29161 this.currentRegion = Roo.lib.Region.getRegion(dom);
29162 this.currentTip.enter();
29165 leave : function(ev)
29167 var dom = ev.getTarget();
29168 //Roo.log(['leave',dom]);
29169 if (!this.currentEl) {
29174 if (dom != this.currentEl.dom) {
29177 var xy = ev.getXY();
29178 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29181 // only activate leave if mouse cursor is outside... bounding box..
29186 if (this.currentTip) {
29187 this.currentTip.leave();
29189 //Roo.log('clear currentEl');
29190 this.currentEl = false;
29195 'left' : ['r-l', [-2,0], 'right'],
29196 'right' : ['l-r', [2,0], 'left'],
29197 'bottom' : ['t-b', [0,2], 'top'],
29198 'top' : [ 'b-t', [0,-2], 'bottom']
29204 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29209 delay : null, // can be { show : 300 , hide: 500}
29213 hoverState : null, //???
29215 placement : 'bottom',
29219 getAutoCreate : function(){
29226 cls : 'tooltip-arrow arrow'
29229 cls : 'tooltip-inner'
29236 bind : function(el)
29241 initEvents : function()
29243 this.arrowEl = this.el.select('.arrow', true).first();
29244 this.innerEl = this.el.select('.tooltip-inner', true).first();
29247 enter : function () {
29249 if (this.timeout != null) {
29250 clearTimeout(this.timeout);
29253 this.hoverState = 'in';
29254 //Roo.log("enter - show");
29255 if (!this.delay || !this.delay.show) {
29260 this.timeout = setTimeout(function () {
29261 if (_t.hoverState == 'in') {
29264 }, this.delay.show);
29268 clearTimeout(this.timeout);
29270 this.hoverState = 'out';
29271 if (!this.delay || !this.delay.hide) {
29277 this.timeout = setTimeout(function () {
29278 //Roo.log("leave - timeout");
29280 if (_t.hoverState == 'out') {
29282 Roo.bootstrap.Tooltip.currentEl = false;
29287 show : function (msg)
29290 this.render(document.body);
29293 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29295 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29297 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29299 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29300 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29302 var placement = typeof this.placement == 'function' ?
29303 this.placement.call(this, this.el, on_el) :
29306 var autoToken = /\s?auto?\s?/i;
29307 var autoPlace = autoToken.test(placement);
29309 placement = placement.replace(autoToken, '') || 'top';
29313 //this.el.setXY([0,0]);
29315 //this.el.dom.style.display='block';
29317 //this.el.appendTo(on_el);
29319 var p = this.getPosition();
29320 var box = this.el.getBox();
29326 var align = this.alignment[placement];
29328 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29330 if(placement == 'top' || placement == 'bottom'){
29332 placement = 'right';
29335 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29336 placement = 'left';
29339 var scroll = Roo.select('body', true).first().getScroll();
29341 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29345 align = this.alignment[placement];
29347 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29351 var elems = document.getElementsByTagName('div');
29352 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29353 for (var i = 0; i < elems.length; i++) {
29354 var zindex = Number.parseInt(
29355 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29358 if (zindex > highest) {
29365 this.el.dom.style.zIndex = highest;
29367 this.el.alignTo(this.bindEl, align[0],align[1]);
29368 //var arrow = this.el.select('.arrow',true).first();
29369 //arrow.set(align[2],
29371 this.el.addClass(placement);
29372 this.el.addClass("bs-tooltip-"+ placement);
29374 this.el.addClass('in fade show');
29376 this.hoverState = null;
29378 if (this.el.hasClass('fade')) {
29393 //this.el.setXY([0,0]);
29394 this.el.removeClass(['show', 'in']);
29410 * @class Roo.bootstrap.LocationPicker
29411 * @extends Roo.bootstrap.Component
29412 * Bootstrap LocationPicker class
29413 * @cfg {Number} latitude Position when init default 0
29414 * @cfg {Number} longitude Position when init default 0
29415 * @cfg {Number} zoom default 15
29416 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29417 * @cfg {Boolean} mapTypeControl default false
29418 * @cfg {Boolean} disableDoubleClickZoom default false
29419 * @cfg {Boolean} scrollwheel default true
29420 * @cfg {Boolean} streetViewControl default false
29421 * @cfg {Number} radius default 0
29422 * @cfg {String} locationName
29423 * @cfg {Boolean} draggable default true
29424 * @cfg {Boolean} enableAutocomplete default false
29425 * @cfg {Boolean} enableReverseGeocode default true
29426 * @cfg {String} markerTitle
29429 * Create a new LocationPicker
29430 * @param {Object} config The config object
29434 Roo.bootstrap.LocationPicker = function(config){
29436 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29441 * Fires when the picker initialized.
29442 * @param {Roo.bootstrap.LocationPicker} this
29443 * @param {Google Location} location
29447 * @event positionchanged
29448 * Fires when the picker position changed.
29449 * @param {Roo.bootstrap.LocationPicker} this
29450 * @param {Google Location} location
29452 positionchanged : true,
29455 * Fires when the map resize.
29456 * @param {Roo.bootstrap.LocationPicker} this
29461 * Fires when the map show.
29462 * @param {Roo.bootstrap.LocationPicker} this
29467 * Fires when the map hide.
29468 * @param {Roo.bootstrap.LocationPicker} this
29473 * Fires when click the map.
29474 * @param {Roo.bootstrap.LocationPicker} this
29475 * @param {Map event} e
29479 * @event mapRightClick
29480 * Fires when right click the map.
29481 * @param {Roo.bootstrap.LocationPicker} this
29482 * @param {Map event} e
29484 mapRightClick : true,
29486 * @event markerClick
29487 * Fires when click the marker.
29488 * @param {Roo.bootstrap.LocationPicker} this
29489 * @param {Map event} e
29491 markerClick : true,
29493 * @event markerRightClick
29494 * Fires when right click the marker.
29495 * @param {Roo.bootstrap.LocationPicker} this
29496 * @param {Map event} e
29498 markerRightClick : true,
29500 * @event OverlayViewDraw
29501 * Fires when OverlayView Draw
29502 * @param {Roo.bootstrap.LocationPicker} this
29504 OverlayViewDraw : true,
29506 * @event OverlayViewOnAdd
29507 * Fires when OverlayView Draw
29508 * @param {Roo.bootstrap.LocationPicker} this
29510 OverlayViewOnAdd : true,
29512 * @event OverlayViewOnRemove
29513 * Fires when OverlayView Draw
29514 * @param {Roo.bootstrap.LocationPicker} this
29516 OverlayViewOnRemove : true,
29518 * @event OverlayViewShow
29519 * Fires when OverlayView Draw
29520 * @param {Roo.bootstrap.LocationPicker} this
29521 * @param {Pixel} cpx
29523 OverlayViewShow : true,
29525 * @event OverlayViewHide
29526 * Fires when OverlayView Draw
29527 * @param {Roo.bootstrap.LocationPicker} this
29529 OverlayViewHide : true,
29531 * @event loadexception
29532 * Fires when load google lib failed.
29533 * @param {Roo.bootstrap.LocationPicker} this
29535 loadexception : true
29540 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29542 gMapContext: false,
29548 mapTypeControl: false,
29549 disableDoubleClickZoom: false,
29551 streetViewControl: false,
29555 enableAutocomplete: false,
29556 enableReverseGeocode: true,
29559 getAutoCreate: function()
29564 cls: 'roo-location-picker'
29570 initEvents: function(ct, position)
29572 if(!this.el.getWidth() || this.isApplied()){
29576 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29581 initial: function()
29583 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29584 this.fireEvent('loadexception', this);
29588 if(!this.mapTypeId){
29589 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29592 this.gMapContext = this.GMapContext();
29594 this.initOverlayView();
29596 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29600 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29601 _this.setPosition(_this.gMapContext.marker.position);
29604 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29605 _this.fireEvent('mapClick', this, event);
29609 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29610 _this.fireEvent('mapRightClick', this, event);
29614 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29615 _this.fireEvent('markerClick', this, event);
29619 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29620 _this.fireEvent('markerRightClick', this, event);
29624 this.setPosition(this.gMapContext.location);
29626 this.fireEvent('initial', this, this.gMapContext.location);
29629 initOverlayView: function()
29633 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29637 _this.fireEvent('OverlayViewDraw', _this);
29642 _this.fireEvent('OverlayViewOnAdd', _this);
29645 onRemove: function()
29647 _this.fireEvent('OverlayViewOnRemove', _this);
29650 show: function(cpx)
29652 _this.fireEvent('OverlayViewShow', _this, cpx);
29657 _this.fireEvent('OverlayViewHide', _this);
29663 fromLatLngToContainerPixel: function(event)
29665 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29668 isApplied: function()
29670 return this.getGmapContext() == false ? false : true;
29673 getGmapContext: function()
29675 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29678 GMapContext: function()
29680 var position = new google.maps.LatLng(this.latitude, this.longitude);
29682 var _map = new google.maps.Map(this.el.dom, {
29685 mapTypeId: this.mapTypeId,
29686 mapTypeControl: this.mapTypeControl,
29687 disableDoubleClickZoom: this.disableDoubleClickZoom,
29688 scrollwheel: this.scrollwheel,
29689 streetViewControl: this.streetViewControl,
29690 locationName: this.locationName,
29691 draggable: this.draggable,
29692 enableAutocomplete: this.enableAutocomplete,
29693 enableReverseGeocode: this.enableReverseGeocode
29696 var _marker = new google.maps.Marker({
29697 position: position,
29699 title: this.markerTitle,
29700 draggable: this.draggable
29707 location: position,
29708 radius: this.radius,
29709 locationName: this.locationName,
29710 addressComponents: {
29711 formatted_address: null,
29712 addressLine1: null,
29713 addressLine2: null,
29715 streetNumber: null,
29719 stateOrProvince: null
29722 domContainer: this.el.dom,
29723 geodecoder: new google.maps.Geocoder()
29727 drawCircle: function(center, radius, options)
29729 if (this.gMapContext.circle != null) {
29730 this.gMapContext.circle.setMap(null);
29734 options = Roo.apply({}, options, {
29735 strokeColor: "#0000FF",
29736 strokeOpacity: .35,
29738 fillColor: "#0000FF",
29742 options.map = this.gMapContext.map;
29743 options.radius = radius;
29744 options.center = center;
29745 this.gMapContext.circle = new google.maps.Circle(options);
29746 return this.gMapContext.circle;
29752 setPosition: function(location)
29754 this.gMapContext.location = location;
29755 this.gMapContext.marker.setPosition(location);
29756 this.gMapContext.map.panTo(location);
29757 this.drawCircle(location, this.gMapContext.radius, {});
29761 if (this.gMapContext.settings.enableReverseGeocode) {
29762 this.gMapContext.geodecoder.geocode({
29763 latLng: this.gMapContext.location
29764 }, function(results, status) {
29766 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29767 _this.gMapContext.locationName = results[0].formatted_address;
29768 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29770 _this.fireEvent('positionchanged', this, location);
29777 this.fireEvent('positionchanged', this, location);
29782 google.maps.event.trigger(this.gMapContext.map, "resize");
29784 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29786 this.fireEvent('resize', this);
29789 setPositionByLatLng: function(latitude, longitude)
29791 this.setPosition(new google.maps.LatLng(latitude, longitude));
29794 getCurrentPosition: function()
29797 latitude: this.gMapContext.location.lat(),
29798 longitude: this.gMapContext.location.lng()
29802 getAddressName: function()
29804 return this.gMapContext.locationName;
29807 getAddressComponents: function()
29809 return this.gMapContext.addressComponents;
29812 address_component_from_google_geocode: function(address_components)
29816 for (var i = 0; i < address_components.length; i++) {
29817 var component = address_components[i];
29818 if (component.types.indexOf("postal_code") >= 0) {
29819 result.postalCode = component.short_name;
29820 } else if (component.types.indexOf("street_number") >= 0) {
29821 result.streetNumber = component.short_name;
29822 } else if (component.types.indexOf("route") >= 0) {
29823 result.streetName = component.short_name;
29824 } else if (component.types.indexOf("neighborhood") >= 0) {
29825 result.city = component.short_name;
29826 } else if (component.types.indexOf("locality") >= 0) {
29827 result.city = component.short_name;
29828 } else if (component.types.indexOf("sublocality") >= 0) {
29829 result.district = component.short_name;
29830 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29831 result.stateOrProvince = component.short_name;
29832 } else if (component.types.indexOf("country") >= 0) {
29833 result.country = component.short_name;
29837 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29838 result.addressLine2 = "";
29842 setZoomLevel: function(zoom)
29844 this.gMapContext.map.setZoom(zoom);
29857 this.fireEvent('show', this);
29868 this.fireEvent('hide', this);
29873 Roo.apply(Roo.bootstrap.LocationPicker, {
29875 OverlayView : function(map, options)
29877 options = options || {};
29884 * @class Roo.bootstrap.Alert
29885 * @extends Roo.bootstrap.Component
29886 * Bootstrap Alert class - shows an alert area box
29888 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29889 Enter a valid email address
29892 * @cfg {String} title The title of alert
29893 * @cfg {String} html The content of alert
29894 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29895 * @cfg {String} fa font-awesomeicon
29896 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29897 * @cfg {Boolean} close true to show a x closer
29901 * Create a new alert
29902 * @param {Object} config The config object
29906 Roo.bootstrap.Alert = function(config){
29907 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29911 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29917 faicon: false, // BC
29921 getAutoCreate : function()
29933 style : this.close ? '' : 'display:none'
29937 cls : 'roo-alert-icon'
29942 cls : 'roo-alert-title',
29947 cls : 'roo-alert-text',
29954 cfg.cn[0].cls += ' fa ' + this.faicon;
29957 cfg.cn[0].cls += ' fa ' + this.fa;
29961 cfg.cls += ' alert-' + this.weight;
29967 initEvents: function()
29969 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29970 this.titleEl = this.el.select('.roo-alert-title',true).first();
29971 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29972 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29973 if (this.seconds > 0) {
29974 this.hide.defer(this.seconds, this);
29978 * Set the Title Message HTML
29979 * @param {String} html
29981 setTitle : function(str)
29983 this.titleEl.dom.innerHTML = str;
29987 * Set the Body Message HTML
29988 * @param {String} html
29990 setHtml : function(str)
29992 this.htmlEl.dom.innerHTML = str;
29995 * Set the Weight of the alert
29996 * @param {String} (success|info|warning|danger) weight
29999 setWeight : function(weight)
30002 this.el.removeClass('alert-' + this.weight);
30005 this.weight = weight;
30007 this.el.addClass('alert-' + this.weight);
30010 * Set the Icon of the alert
30011 * @param {String} see fontawsome names (name without the 'fa-' bit)
30013 setIcon : function(icon)
30016 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30019 this.faicon = icon;
30021 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30046 * @class Roo.bootstrap.UploadCropbox
30047 * @extends Roo.bootstrap.Component
30048 * Bootstrap UploadCropbox class
30049 * @cfg {String} emptyText show when image has been loaded
30050 * @cfg {String} rotateNotify show when image too small to rotate
30051 * @cfg {Number} errorTimeout default 3000
30052 * @cfg {Number} minWidth default 300
30053 * @cfg {Number} minHeight default 300
30054 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30055 * @cfg {Boolean} isDocument (true|false) default false
30056 * @cfg {String} url action url
30057 * @cfg {String} paramName default 'imageUpload'
30058 * @cfg {String} method default POST
30059 * @cfg {Boolean} loadMask (true|false) default true
30060 * @cfg {Boolean} loadingText default 'Loading...'
30063 * Create a new UploadCropbox
30064 * @param {Object} config The config object
30067 Roo.bootstrap.UploadCropbox = function(config){
30068 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30072 * @event beforeselectfile
30073 * Fire before select file
30074 * @param {Roo.bootstrap.UploadCropbox} this
30076 "beforeselectfile" : true,
30079 * Fire after initEvent
30080 * @param {Roo.bootstrap.UploadCropbox} this
30085 * Fire after initEvent
30086 * @param {Roo.bootstrap.UploadCropbox} this
30087 * @param {String} data
30092 * Fire when preparing the file data
30093 * @param {Roo.bootstrap.UploadCropbox} this
30094 * @param {Object} file
30099 * Fire when get exception
30100 * @param {Roo.bootstrap.UploadCropbox} this
30101 * @param {XMLHttpRequest} xhr
30103 "exception" : true,
30105 * @event beforeloadcanvas
30106 * Fire before load the canvas
30107 * @param {Roo.bootstrap.UploadCropbox} this
30108 * @param {String} src
30110 "beforeloadcanvas" : true,
30113 * Fire when trash image
30114 * @param {Roo.bootstrap.UploadCropbox} this
30119 * Fire when download the image
30120 * @param {Roo.bootstrap.UploadCropbox} this
30124 * @event footerbuttonclick
30125 * Fire when footerbuttonclick
30126 * @param {Roo.bootstrap.UploadCropbox} this
30127 * @param {String} type
30129 "footerbuttonclick" : true,
30133 * @param {Roo.bootstrap.UploadCropbox} this
30138 * Fire when rotate the image
30139 * @param {Roo.bootstrap.UploadCropbox} this
30140 * @param {String} pos
30145 * Fire when inspect the file
30146 * @param {Roo.bootstrap.UploadCropbox} this
30147 * @param {Object} file
30152 * Fire when xhr upload the file
30153 * @param {Roo.bootstrap.UploadCropbox} this
30154 * @param {Object} data
30159 * Fire when arrange the file data
30160 * @param {Roo.bootstrap.UploadCropbox} this
30161 * @param {Object} formData
30166 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30169 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30171 emptyText : 'Click to upload image',
30172 rotateNotify : 'Image is too small to rotate',
30173 errorTimeout : 3000,
30187 cropType : 'image/jpeg',
30189 canvasLoaded : false,
30190 isDocument : false,
30192 paramName : 'imageUpload',
30194 loadingText : 'Loading...',
30197 getAutoCreate : function()
30201 cls : 'roo-upload-cropbox',
30205 cls : 'roo-upload-cropbox-selector',
30210 cls : 'roo-upload-cropbox-body',
30211 style : 'cursor:pointer',
30215 cls : 'roo-upload-cropbox-preview'
30219 cls : 'roo-upload-cropbox-thumb'
30223 cls : 'roo-upload-cropbox-empty-notify',
30224 html : this.emptyText
30228 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30229 html : this.rotateNotify
30235 cls : 'roo-upload-cropbox-footer',
30238 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30248 onRender : function(ct, position)
30250 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30252 if (this.buttons.length) {
30254 Roo.each(this.buttons, function(bb) {
30256 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30258 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30264 this.maskEl = this.el;
30268 initEvents : function()
30270 this.urlAPI = (window.createObjectURL && window) ||
30271 (window.URL && URL.revokeObjectURL && URL) ||
30272 (window.webkitURL && webkitURL);
30274 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30275 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30277 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30278 this.selectorEl.hide();
30280 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30281 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30283 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30284 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30285 this.thumbEl.hide();
30287 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30288 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30290 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30291 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30292 this.errorEl.hide();
30294 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30295 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30296 this.footerEl.hide();
30298 this.setThumbBoxSize();
30304 this.fireEvent('initial', this);
30311 window.addEventListener("resize", function() { _this.resize(); } );
30313 this.bodyEl.on('click', this.beforeSelectFile, this);
30316 this.bodyEl.on('touchstart', this.onTouchStart, this);
30317 this.bodyEl.on('touchmove', this.onTouchMove, this);
30318 this.bodyEl.on('touchend', this.onTouchEnd, this);
30322 this.bodyEl.on('mousedown', this.onMouseDown, this);
30323 this.bodyEl.on('mousemove', this.onMouseMove, this);
30324 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30325 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30326 Roo.get(document).on('mouseup', this.onMouseUp, this);
30329 this.selectorEl.on('change', this.onFileSelected, this);
30335 this.baseScale = 1;
30337 this.baseRotate = 1;
30338 this.dragable = false;
30339 this.pinching = false;
30342 this.cropData = false;
30343 this.notifyEl.dom.innerHTML = this.emptyText;
30345 this.selectorEl.dom.value = '';
30349 resize : function()
30351 if(this.fireEvent('resize', this) != false){
30352 this.setThumbBoxPosition();
30353 this.setCanvasPosition();
30357 onFooterButtonClick : function(e, el, o, type)
30360 case 'rotate-left' :
30361 this.onRotateLeft(e);
30363 case 'rotate-right' :
30364 this.onRotateRight(e);
30367 this.beforeSelectFile(e);
30382 this.fireEvent('footerbuttonclick', this, type);
30385 beforeSelectFile : function(e)
30387 e.preventDefault();
30389 if(this.fireEvent('beforeselectfile', this) != false){
30390 this.selectorEl.dom.click();
30394 onFileSelected : function(e)
30396 e.preventDefault();
30398 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30402 var file = this.selectorEl.dom.files[0];
30404 if(this.fireEvent('inspect', this, file) != false){
30405 this.prepare(file);
30410 trash : function(e)
30412 this.fireEvent('trash', this);
30415 download : function(e)
30417 this.fireEvent('download', this);
30420 loadCanvas : function(src)
30422 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30426 this.imageEl = document.createElement('img');
30430 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30432 this.imageEl.src = src;
30436 onLoadCanvas : function()
30438 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30439 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30441 this.bodyEl.un('click', this.beforeSelectFile, this);
30443 this.notifyEl.hide();
30444 this.thumbEl.show();
30445 this.footerEl.show();
30447 this.baseRotateLevel();
30449 if(this.isDocument){
30450 this.setThumbBoxSize();
30453 this.setThumbBoxPosition();
30455 this.baseScaleLevel();
30461 this.canvasLoaded = true;
30464 this.maskEl.unmask();
30469 setCanvasPosition : function()
30471 if(!this.canvasEl){
30475 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30476 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30478 this.previewEl.setLeft(pw);
30479 this.previewEl.setTop(ph);
30483 onMouseDown : function(e)
30487 this.dragable = true;
30488 this.pinching = false;
30490 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30491 this.dragable = false;
30495 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30496 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30500 onMouseMove : function(e)
30504 if(!this.canvasLoaded){
30508 if (!this.dragable){
30512 var minX = Math.ceil(this.thumbEl.getLeft(true));
30513 var minY = Math.ceil(this.thumbEl.getTop(true));
30515 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30516 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30518 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30519 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30521 x = x - this.mouseX;
30522 y = y - this.mouseY;
30524 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30525 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30527 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30528 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30530 this.previewEl.setLeft(bgX);
30531 this.previewEl.setTop(bgY);
30533 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30534 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30537 onMouseUp : function(e)
30541 this.dragable = false;
30544 onMouseWheel : function(e)
30548 this.startScale = this.scale;
30550 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30552 if(!this.zoomable()){
30553 this.scale = this.startScale;
30562 zoomable : function()
30564 var minScale = this.thumbEl.getWidth() / this.minWidth;
30566 if(this.minWidth < this.minHeight){
30567 minScale = this.thumbEl.getHeight() / this.minHeight;
30570 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30571 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30575 (this.rotate == 0 || this.rotate == 180) &&
30577 width > this.imageEl.OriginWidth ||
30578 height > this.imageEl.OriginHeight ||
30579 (width < this.minWidth && height < this.minHeight)
30587 (this.rotate == 90 || this.rotate == 270) &&
30589 width > this.imageEl.OriginWidth ||
30590 height > this.imageEl.OriginHeight ||
30591 (width < this.minHeight && height < this.minWidth)
30598 !this.isDocument &&
30599 (this.rotate == 0 || this.rotate == 180) &&
30601 width < this.minWidth ||
30602 width > this.imageEl.OriginWidth ||
30603 height < this.minHeight ||
30604 height > this.imageEl.OriginHeight
30611 !this.isDocument &&
30612 (this.rotate == 90 || this.rotate == 270) &&
30614 width < this.minHeight ||
30615 width > this.imageEl.OriginWidth ||
30616 height < this.minWidth ||
30617 height > this.imageEl.OriginHeight
30627 onRotateLeft : function(e)
30629 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30631 var minScale = this.thumbEl.getWidth() / this.minWidth;
30633 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30634 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30636 this.startScale = this.scale;
30638 while (this.getScaleLevel() < minScale){
30640 this.scale = this.scale + 1;
30642 if(!this.zoomable()){
30647 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30648 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30653 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30660 this.scale = this.startScale;
30662 this.onRotateFail();
30667 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30669 if(this.isDocument){
30670 this.setThumbBoxSize();
30671 this.setThumbBoxPosition();
30672 this.setCanvasPosition();
30677 this.fireEvent('rotate', this, 'left');
30681 onRotateRight : function(e)
30683 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30685 var minScale = this.thumbEl.getWidth() / this.minWidth;
30687 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30688 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30690 this.startScale = this.scale;
30692 while (this.getScaleLevel() < minScale){
30694 this.scale = this.scale + 1;
30696 if(!this.zoomable()){
30701 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30702 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30707 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30714 this.scale = this.startScale;
30716 this.onRotateFail();
30721 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30723 if(this.isDocument){
30724 this.setThumbBoxSize();
30725 this.setThumbBoxPosition();
30726 this.setCanvasPosition();
30731 this.fireEvent('rotate', this, 'right');
30734 onRotateFail : function()
30736 this.errorEl.show(true);
30740 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30745 this.previewEl.dom.innerHTML = '';
30747 var canvasEl = document.createElement("canvas");
30749 var contextEl = canvasEl.getContext("2d");
30751 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30752 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30753 var center = this.imageEl.OriginWidth / 2;
30755 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30756 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30758 center = this.imageEl.OriginHeight / 2;
30761 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30763 contextEl.translate(center, center);
30764 contextEl.rotate(this.rotate * Math.PI / 180);
30766 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30768 this.canvasEl = document.createElement("canvas");
30770 this.contextEl = this.canvasEl.getContext("2d");
30772 switch (this.rotate) {
30775 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30776 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30778 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30783 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30784 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30786 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30787 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);
30791 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30796 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30797 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30799 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30800 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);
30804 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);
30809 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30810 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30812 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30813 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30817 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);
30824 this.previewEl.appendChild(this.canvasEl);
30826 this.setCanvasPosition();
30831 if(!this.canvasLoaded){
30835 var imageCanvas = document.createElement("canvas");
30837 var imageContext = imageCanvas.getContext("2d");
30839 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30840 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30842 var center = imageCanvas.width / 2;
30844 imageContext.translate(center, center);
30846 imageContext.rotate(this.rotate * Math.PI / 180);
30848 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30850 var canvas = document.createElement("canvas");
30852 var context = canvas.getContext("2d");
30854 canvas.width = this.minWidth;
30855 canvas.height = this.minHeight;
30857 switch (this.rotate) {
30860 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30861 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30863 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30864 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30866 var targetWidth = this.minWidth - 2 * x;
30867 var targetHeight = this.minHeight - 2 * y;
30871 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30872 scale = targetWidth / width;
30875 if(x > 0 && y == 0){
30876 scale = targetHeight / height;
30879 if(x > 0 && y > 0){
30880 scale = targetWidth / width;
30882 if(width < height){
30883 scale = targetHeight / height;
30887 context.scale(scale, scale);
30889 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30890 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30892 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30893 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30895 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30900 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30901 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30903 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30904 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30906 var targetWidth = this.minWidth - 2 * x;
30907 var targetHeight = this.minHeight - 2 * y;
30911 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30912 scale = targetWidth / width;
30915 if(x > 0 && y == 0){
30916 scale = targetHeight / height;
30919 if(x > 0 && y > 0){
30920 scale = targetWidth / width;
30922 if(width < height){
30923 scale = targetHeight / height;
30927 context.scale(scale, scale);
30929 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30930 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30932 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30933 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30935 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30937 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30942 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30943 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30945 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30946 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30948 var targetWidth = this.minWidth - 2 * x;
30949 var targetHeight = this.minHeight - 2 * y;
30953 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30954 scale = targetWidth / width;
30957 if(x > 0 && y == 0){
30958 scale = targetHeight / height;
30961 if(x > 0 && y > 0){
30962 scale = targetWidth / width;
30964 if(width < height){
30965 scale = targetHeight / height;
30969 context.scale(scale, scale);
30971 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30972 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30974 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30975 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30977 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30978 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30980 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30985 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30986 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30988 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30989 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30991 var targetWidth = this.minWidth - 2 * x;
30992 var targetHeight = this.minHeight - 2 * y;
30996 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30997 scale = targetWidth / width;
31000 if(x > 0 && y == 0){
31001 scale = targetHeight / height;
31004 if(x > 0 && y > 0){
31005 scale = targetWidth / width;
31007 if(width < height){
31008 scale = targetHeight / height;
31012 context.scale(scale, scale);
31014 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31015 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31017 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31018 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31020 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31022 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31029 this.cropData = canvas.toDataURL(this.cropType);
31031 if(this.fireEvent('crop', this, this.cropData) !== false){
31032 this.process(this.file, this.cropData);
31039 setThumbBoxSize : function()
31043 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31044 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31045 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31047 this.minWidth = width;
31048 this.minHeight = height;
31050 if(this.rotate == 90 || this.rotate == 270){
31051 this.minWidth = height;
31052 this.minHeight = width;
31057 width = Math.ceil(this.minWidth * height / this.minHeight);
31059 if(this.minWidth > this.minHeight){
31061 height = Math.ceil(this.minHeight * width / this.minWidth);
31064 this.thumbEl.setStyle({
31065 width : width + 'px',
31066 height : height + 'px'
31073 setThumbBoxPosition : function()
31075 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31076 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31078 this.thumbEl.setLeft(x);
31079 this.thumbEl.setTop(y);
31083 baseRotateLevel : function()
31085 this.baseRotate = 1;
31088 typeof(this.exif) != 'undefined' &&
31089 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31090 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31092 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31095 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31099 baseScaleLevel : function()
31103 if(this.isDocument){
31105 if(this.baseRotate == 6 || this.baseRotate == 8){
31107 height = this.thumbEl.getHeight();
31108 this.baseScale = height / this.imageEl.OriginWidth;
31110 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31111 width = this.thumbEl.getWidth();
31112 this.baseScale = width / this.imageEl.OriginHeight;
31118 height = this.thumbEl.getHeight();
31119 this.baseScale = height / this.imageEl.OriginHeight;
31121 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31122 width = this.thumbEl.getWidth();
31123 this.baseScale = width / this.imageEl.OriginWidth;
31129 if(this.baseRotate == 6 || this.baseRotate == 8){
31131 width = this.thumbEl.getHeight();
31132 this.baseScale = width / this.imageEl.OriginHeight;
31134 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31135 height = this.thumbEl.getWidth();
31136 this.baseScale = height / this.imageEl.OriginHeight;
31139 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31140 height = this.thumbEl.getWidth();
31141 this.baseScale = height / this.imageEl.OriginHeight;
31143 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31144 width = this.thumbEl.getHeight();
31145 this.baseScale = width / this.imageEl.OriginWidth;
31152 width = this.thumbEl.getWidth();
31153 this.baseScale = width / this.imageEl.OriginWidth;
31155 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31156 height = this.thumbEl.getHeight();
31157 this.baseScale = height / this.imageEl.OriginHeight;
31160 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31162 height = this.thumbEl.getHeight();
31163 this.baseScale = height / this.imageEl.OriginHeight;
31165 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31166 width = this.thumbEl.getWidth();
31167 this.baseScale = width / this.imageEl.OriginWidth;
31175 getScaleLevel : function()
31177 return this.baseScale * Math.pow(1.1, this.scale);
31180 onTouchStart : function(e)
31182 if(!this.canvasLoaded){
31183 this.beforeSelectFile(e);
31187 var touches = e.browserEvent.touches;
31193 if(touches.length == 1){
31194 this.onMouseDown(e);
31198 if(touches.length != 2){
31204 for(var i = 0, finger; finger = touches[i]; i++){
31205 coords.push(finger.pageX, finger.pageY);
31208 var x = Math.pow(coords[0] - coords[2], 2);
31209 var y = Math.pow(coords[1] - coords[3], 2);
31211 this.startDistance = Math.sqrt(x + y);
31213 this.startScale = this.scale;
31215 this.pinching = true;
31216 this.dragable = false;
31220 onTouchMove : function(e)
31222 if(!this.pinching && !this.dragable){
31226 var touches = e.browserEvent.touches;
31233 this.onMouseMove(e);
31239 for(var i = 0, finger; finger = touches[i]; i++){
31240 coords.push(finger.pageX, finger.pageY);
31243 var x = Math.pow(coords[0] - coords[2], 2);
31244 var y = Math.pow(coords[1] - coords[3], 2);
31246 this.endDistance = Math.sqrt(x + y);
31248 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31250 if(!this.zoomable()){
31251 this.scale = this.startScale;
31259 onTouchEnd : function(e)
31261 this.pinching = false;
31262 this.dragable = false;
31266 process : function(file, crop)
31269 this.maskEl.mask(this.loadingText);
31272 this.xhr = new XMLHttpRequest();
31274 file.xhr = this.xhr;
31276 this.xhr.open(this.method, this.url, true);
31279 "Accept": "application/json",
31280 "Cache-Control": "no-cache",
31281 "X-Requested-With": "XMLHttpRequest"
31284 for (var headerName in headers) {
31285 var headerValue = headers[headerName];
31287 this.xhr.setRequestHeader(headerName, headerValue);
31293 this.xhr.onload = function()
31295 _this.xhrOnLoad(_this.xhr);
31298 this.xhr.onerror = function()
31300 _this.xhrOnError(_this.xhr);
31303 var formData = new FormData();
31305 formData.append('returnHTML', 'NO');
31308 formData.append('crop', crop);
31311 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31312 formData.append(this.paramName, file, file.name);
31315 if(typeof(file.filename) != 'undefined'){
31316 formData.append('filename', file.filename);
31319 if(typeof(file.mimetype) != 'undefined'){
31320 formData.append('mimetype', file.mimetype);
31323 if(this.fireEvent('arrange', this, formData) != false){
31324 this.xhr.send(formData);
31328 xhrOnLoad : function(xhr)
31331 this.maskEl.unmask();
31334 if (xhr.readyState !== 4) {
31335 this.fireEvent('exception', this, xhr);
31339 var response = Roo.decode(xhr.responseText);
31341 if(!response.success){
31342 this.fireEvent('exception', this, xhr);
31346 var response = Roo.decode(xhr.responseText);
31348 this.fireEvent('upload', this, response);
31352 xhrOnError : function()
31355 this.maskEl.unmask();
31358 Roo.log('xhr on error');
31360 var response = Roo.decode(xhr.responseText);
31366 prepare : function(file)
31369 this.maskEl.mask(this.loadingText);
31375 if(typeof(file) === 'string'){
31376 this.loadCanvas(file);
31380 if(!file || !this.urlAPI){
31385 this.cropType = file.type;
31389 if(this.fireEvent('prepare', this, this.file) != false){
31391 var reader = new FileReader();
31393 reader.onload = function (e) {
31394 if (e.target.error) {
31395 Roo.log(e.target.error);
31399 var buffer = e.target.result,
31400 dataView = new DataView(buffer),
31402 maxOffset = dataView.byteLength - 4,
31406 if (dataView.getUint16(0) === 0xffd8) {
31407 while (offset < maxOffset) {
31408 markerBytes = dataView.getUint16(offset);
31410 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31411 markerLength = dataView.getUint16(offset + 2) + 2;
31412 if (offset + markerLength > dataView.byteLength) {
31413 Roo.log('Invalid meta data: Invalid segment size.');
31417 if(markerBytes == 0xffe1){
31418 _this.parseExifData(
31425 offset += markerLength;
31435 var url = _this.urlAPI.createObjectURL(_this.file);
31437 _this.loadCanvas(url);
31442 reader.readAsArrayBuffer(this.file);
31448 parseExifData : function(dataView, offset, length)
31450 var tiffOffset = offset + 10,
31454 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31455 // No Exif data, might be XMP data instead
31459 // Check for the ASCII code for "Exif" (0x45786966):
31460 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31461 // No Exif data, might be XMP data instead
31464 if (tiffOffset + 8 > dataView.byteLength) {
31465 Roo.log('Invalid Exif data: Invalid segment size.');
31468 // Check for the two null bytes:
31469 if (dataView.getUint16(offset + 8) !== 0x0000) {
31470 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31473 // Check the byte alignment:
31474 switch (dataView.getUint16(tiffOffset)) {
31476 littleEndian = true;
31479 littleEndian = false;
31482 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31485 // Check for the TIFF tag marker (0x002A):
31486 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31487 Roo.log('Invalid Exif data: Missing TIFF marker.');
31490 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31491 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31493 this.parseExifTags(
31496 tiffOffset + dirOffset,
31501 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31506 if (dirOffset + 6 > dataView.byteLength) {
31507 Roo.log('Invalid Exif data: Invalid directory offset.');
31510 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31511 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31512 if (dirEndOffset + 4 > dataView.byteLength) {
31513 Roo.log('Invalid Exif data: Invalid directory size.');
31516 for (i = 0; i < tagsNumber; i += 1) {
31520 dirOffset + 2 + 12 * i, // tag offset
31524 // Return the offset to the next directory:
31525 return dataView.getUint32(dirEndOffset, littleEndian);
31528 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31530 var tag = dataView.getUint16(offset, littleEndian);
31532 this.exif[tag] = this.getExifValue(
31536 dataView.getUint16(offset + 2, littleEndian), // tag type
31537 dataView.getUint32(offset + 4, littleEndian), // tag length
31542 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31544 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31553 Roo.log('Invalid Exif data: Invalid tag type.');
31557 tagSize = tagType.size * length;
31558 // Determine if the value is contained in the dataOffset bytes,
31559 // or if the value at the dataOffset is a pointer to the actual data:
31560 dataOffset = tagSize > 4 ?
31561 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31562 if (dataOffset + tagSize > dataView.byteLength) {
31563 Roo.log('Invalid Exif data: Invalid data offset.');
31566 if (length === 1) {
31567 return tagType.getValue(dataView, dataOffset, littleEndian);
31570 for (i = 0; i < length; i += 1) {
31571 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31574 if (tagType.ascii) {
31576 // Concatenate the chars:
31577 for (i = 0; i < values.length; i += 1) {
31579 // Ignore the terminating NULL byte(s):
31580 if (c === '\u0000') {
31592 Roo.apply(Roo.bootstrap.UploadCropbox, {
31594 'Orientation': 0x0112
31598 1: 0, //'top-left',
31600 3: 180, //'bottom-right',
31601 // 4: 'bottom-left',
31603 6: 90, //'right-top',
31604 // 7: 'right-bottom',
31605 8: 270 //'left-bottom'
31609 // byte, 8-bit unsigned int:
31611 getValue: function (dataView, dataOffset) {
31612 return dataView.getUint8(dataOffset);
31616 // ascii, 8-bit byte:
31618 getValue: function (dataView, dataOffset) {
31619 return String.fromCharCode(dataView.getUint8(dataOffset));
31624 // short, 16 bit int:
31626 getValue: function (dataView, dataOffset, littleEndian) {
31627 return dataView.getUint16(dataOffset, littleEndian);
31631 // long, 32 bit int:
31633 getValue: function (dataView, dataOffset, littleEndian) {
31634 return dataView.getUint32(dataOffset, littleEndian);
31638 // rational = two long values, first is numerator, second is denominator:
31640 getValue: function (dataView, dataOffset, littleEndian) {
31641 return dataView.getUint32(dataOffset, littleEndian) /
31642 dataView.getUint32(dataOffset + 4, littleEndian);
31646 // slong, 32 bit signed int:
31648 getValue: function (dataView, dataOffset, littleEndian) {
31649 return dataView.getInt32(dataOffset, littleEndian);
31653 // srational, two slongs, first is numerator, second is denominator:
31655 getValue: function (dataView, dataOffset, littleEndian) {
31656 return dataView.getInt32(dataOffset, littleEndian) /
31657 dataView.getInt32(dataOffset + 4, littleEndian);
31667 cls : 'btn-group roo-upload-cropbox-rotate-left',
31668 action : 'rotate-left',
31672 cls : 'btn btn-default',
31673 html : '<i class="fa fa-undo"></i>'
31679 cls : 'btn-group roo-upload-cropbox-picture',
31680 action : 'picture',
31684 cls : 'btn btn-default',
31685 html : '<i class="fa fa-picture-o"></i>'
31691 cls : 'btn-group roo-upload-cropbox-rotate-right',
31692 action : 'rotate-right',
31696 cls : 'btn btn-default',
31697 html : '<i class="fa fa-repeat"></i>'
31705 cls : 'btn-group roo-upload-cropbox-rotate-left',
31706 action : 'rotate-left',
31710 cls : 'btn btn-default',
31711 html : '<i class="fa fa-undo"></i>'
31717 cls : 'btn-group roo-upload-cropbox-download',
31718 action : 'download',
31722 cls : 'btn btn-default',
31723 html : '<i class="fa fa-download"></i>'
31729 cls : 'btn-group roo-upload-cropbox-crop',
31734 cls : 'btn btn-default',
31735 html : '<i class="fa fa-crop"></i>'
31741 cls : 'btn-group roo-upload-cropbox-trash',
31746 cls : 'btn btn-default',
31747 html : '<i class="fa fa-trash"></i>'
31753 cls : 'btn-group roo-upload-cropbox-rotate-right',
31754 action : 'rotate-right',
31758 cls : 'btn btn-default',
31759 html : '<i class="fa fa-repeat"></i>'
31767 cls : 'btn-group roo-upload-cropbox-rotate-left',
31768 action : 'rotate-left',
31772 cls : 'btn btn-default',
31773 html : '<i class="fa fa-undo"></i>'
31779 cls : 'btn-group roo-upload-cropbox-rotate-right',
31780 action : 'rotate-right',
31784 cls : 'btn btn-default',
31785 html : '<i class="fa fa-repeat"></i>'
31798 * @class Roo.bootstrap.DocumentManager
31799 * @extends Roo.bootstrap.Component
31800 * Bootstrap DocumentManager class
31801 * @cfg {String} paramName default 'imageUpload'
31802 * @cfg {String} toolTipName default 'filename'
31803 * @cfg {String} method default POST
31804 * @cfg {String} url action url
31805 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31806 * @cfg {Boolean} multiple multiple upload default true
31807 * @cfg {Number} thumbSize default 300
31808 * @cfg {String} fieldLabel
31809 * @cfg {Number} labelWidth default 4
31810 * @cfg {String} labelAlign (left|top) default left
31811 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31812 * @cfg {Number} labellg set the width of label (1-12)
31813 * @cfg {Number} labelmd set the width of label (1-12)
31814 * @cfg {Number} labelsm set the width of label (1-12)
31815 * @cfg {Number} labelxs set the width of label (1-12)
31818 * Create a new DocumentManager
31819 * @param {Object} config The config object
31822 Roo.bootstrap.DocumentManager = function(config){
31823 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31826 this.delegates = [];
31831 * Fire when initial the DocumentManager
31832 * @param {Roo.bootstrap.DocumentManager} this
31837 * inspect selected file
31838 * @param {Roo.bootstrap.DocumentManager} this
31839 * @param {File} file
31844 * Fire when xhr load exception
31845 * @param {Roo.bootstrap.DocumentManager} this
31846 * @param {XMLHttpRequest} xhr
31848 "exception" : true,
31850 * @event afterupload
31851 * Fire when xhr load exception
31852 * @param {Roo.bootstrap.DocumentManager} this
31853 * @param {XMLHttpRequest} xhr
31855 "afterupload" : true,
31858 * prepare the form data
31859 * @param {Roo.bootstrap.DocumentManager} this
31860 * @param {Object} formData
31865 * Fire when remove the file
31866 * @param {Roo.bootstrap.DocumentManager} this
31867 * @param {Object} file
31872 * Fire after refresh the file
31873 * @param {Roo.bootstrap.DocumentManager} this
31878 * Fire after click the image
31879 * @param {Roo.bootstrap.DocumentManager} this
31880 * @param {Object} file
31885 * Fire when upload a image and editable set to true
31886 * @param {Roo.bootstrap.DocumentManager} this
31887 * @param {Object} file
31891 * @event beforeselectfile
31892 * Fire before select file
31893 * @param {Roo.bootstrap.DocumentManager} this
31895 "beforeselectfile" : true,
31898 * Fire before process file
31899 * @param {Roo.bootstrap.DocumentManager} this
31900 * @param {Object} file
31904 * @event previewrendered
31905 * Fire when preview rendered
31906 * @param {Roo.bootstrap.DocumentManager} this
31907 * @param {Object} file
31909 "previewrendered" : true,
31912 "previewResize" : true
31917 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31926 paramName : 'imageUpload',
31927 toolTipName : 'filename',
31930 labelAlign : 'left',
31940 getAutoCreate : function()
31942 var managerWidget = {
31944 cls : 'roo-document-manager',
31948 cls : 'roo-document-manager-selector',
31953 cls : 'roo-document-manager-uploader',
31957 cls : 'roo-document-manager-upload-btn',
31958 html : '<i class="fa fa-plus"></i>'
31969 cls : 'column col-md-12',
31974 if(this.fieldLabel.length){
31979 cls : 'column col-md-12',
31980 html : this.fieldLabel
31984 cls : 'column col-md-12',
31989 if(this.labelAlign == 'left'){
31994 html : this.fieldLabel
32003 if(this.labelWidth > 12){
32004 content[0].style = "width: " + this.labelWidth + 'px';
32007 if(this.labelWidth < 13 && this.labelmd == 0){
32008 this.labelmd = this.labelWidth;
32011 if(this.labellg > 0){
32012 content[0].cls += ' col-lg-' + this.labellg;
32013 content[1].cls += ' col-lg-' + (12 - this.labellg);
32016 if(this.labelmd > 0){
32017 content[0].cls += ' col-md-' + this.labelmd;
32018 content[1].cls += ' col-md-' + (12 - this.labelmd);
32021 if(this.labelsm > 0){
32022 content[0].cls += ' col-sm-' + this.labelsm;
32023 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32026 if(this.labelxs > 0){
32027 content[0].cls += ' col-xs-' + this.labelxs;
32028 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32036 cls : 'row clearfix',
32044 initEvents : function()
32046 this.managerEl = this.el.select('.roo-document-manager', true).first();
32047 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32049 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32050 this.selectorEl.hide();
32053 this.selectorEl.attr('multiple', 'multiple');
32056 this.selectorEl.on('change', this.onFileSelected, this);
32058 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32059 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32061 this.uploader.on('click', this.onUploaderClick, this);
32063 this.renderProgressDialog();
32067 window.addEventListener("resize", function() { _this.refresh(); } );
32069 this.fireEvent('initial', this);
32072 renderProgressDialog : function()
32076 this.progressDialog = new Roo.bootstrap.Modal({
32077 cls : 'roo-document-manager-progress-dialog',
32078 allow_close : false,
32089 btnclick : function() {
32090 _this.uploadCancel();
32096 this.progressDialog.render(Roo.get(document.body));
32098 this.progress = new Roo.bootstrap.Progress({
32099 cls : 'roo-document-manager-progress',
32104 this.progress.render(this.progressDialog.getChildContainer());
32106 this.progressBar = new Roo.bootstrap.ProgressBar({
32107 cls : 'roo-document-manager-progress-bar',
32110 aria_valuemax : 12,
32114 this.progressBar.render(this.progress.getChildContainer());
32117 onUploaderClick : function(e)
32119 e.preventDefault();
32121 if(this.fireEvent('beforeselectfile', this) != false){
32122 this.selectorEl.dom.click();
32127 onFileSelected : function(e)
32129 e.preventDefault();
32131 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32135 Roo.each(this.selectorEl.dom.files, function(file){
32136 if(this.fireEvent('inspect', this, file) != false){
32137 this.files.push(file);
32147 this.selectorEl.dom.value = '';
32149 if(!this.files || !this.files.length){
32153 if(this.boxes > 0 && this.files.length > this.boxes){
32154 this.files = this.files.slice(0, this.boxes);
32157 this.uploader.show();
32159 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32160 this.uploader.hide();
32169 Roo.each(this.files, function(file){
32171 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32172 var f = this.renderPreview(file);
32177 if(file.type.indexOf('image') != -1){
32178 this.delegates.push(
32180 _this.process(file);
32181 }).createDelegate(this)
32189 _this.process(file);
32190 }).createDelegate(this)
32195 this.files = files;
32197 this.delegates = this.delegates.concat(docs);
32199 if(!this.delegates.length){
32204 this.progressBar.aria_valuemax = this.delegates.length;
32211 arrange : function()
32213 if(!this.delegates.length){
32214 this.progressDialog.hide();
32219 var delegate = this.delegates.shift();
32221 this.progressDialog.show();
32223 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32225 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32230 refresh : function()
32232 this.uploader.show();
32234 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32235 this.uploader.hide();
32238 Roo.isTouch ? this.closable(false) : this.closable(true);
32240 this.fireEvent('refresh', this);
32243 onRemove : function(e, el, o)
32245 e.preventDefault();
32247 this.fireEvent('remove', this, o);
32251 remove : function(o)
32255 Roo.each(this.files, function(file){
32256 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32265 this.files = files;
32272 Roo.each(this.files, function(file){
32277 file.target.remove();
32286 onClick : function(e, el, o)
32288 e.preventDefault();
32290 this.fireEvent('click', this, o);
32294 closable : function(closable)
32296 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32298 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32310 xhrOnLoad : function(xhr)
32312 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32316 if (xhr.readyState !== 4) {
32318 this.fireEvent('exception', this, xhr);
32322 var response = Roo.decode(xhr.responseText);
32324 if(!response.success){
32326 this.fireEvent('exception', this, xhr);
32330 var file = this.renderPreview(response.data);
32332 this.files.push(file);
32336 this.fireEvent('afterupload', this, xhr);
32340 xhrOnError : function(xhr)
32342 Roo.log('xhr on error');
32344 var response = Roo.decode(xhr.responseText);
32351 process : function(file)
32353 if(this.fireEvent('process', this, file) !== false){
32354 if(this.editable && file.type.indexOf('image') != -1){
32355 this.fireEvent('edit', this, file);
32359 this.uploadStart(file, false);
32366 uploadStart : function(file, crop)
32368 this.xhr = new XMLHttpRequest();
32370 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32375 file.xhr = this.xhr;
32377 this.managerEl.createChild({
32379 cls : 'roo-document-manager-loading',
32383 tooltip : file.name,
32384 cls : 'roo-document-manager-thumb',
32385 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32391 this.xhr.open(this.method, this.url, true);
32394 "Accept": "application/json",
32395 "Cache-Control": "no-cache",
32396 "X-Requested-With": "XMLHttpRequest"
32399 for (var headerName in headers) {
32400 var headerValue = headers[headerName];
32402 this.xhr.setRequestHeader(headerName, headerValue);
32408 this.xhr.onload = function()
32410 _this.xhrOnLoad(_this.xhr);
32413 this.xhr.onerror = function()
32415 _this.xhrOnError(_this.xhr);
32418 var formData = new FormData();
32420 formData.append('returnHTML', 'NO');
32423 formData.append('crop', crop);
32426 formData.append(this.paramName, file, file.name);
32433 if(this.fireEvent('prepare', this, formData, options) != false){
32435 if(options.manually){
32439 this.xhr.send(formData);
32443 this.uploadCancel();
32446 uploadCancel : function()
32452 this.delegates = [];
32454 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32461 renderPreview : function(file)
32463 if(typeof(file.target) != 'undefined' && file.target){
32467 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32469 var previewEl = this.managerEl.createChild({
32471 cls : 'roo-document-manager-preview',
32475 tooltip : file[this.toolTipName],
32476 cls : 'roo-document-manager-thumb',
32477 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32482 html : '<i class="fa fa-times-circle"></i>'
32487 var close = previewEl.select('button.close', true).first();
32489 close.on('click', this.onRemove, this, file);
32491 file.target = previewEl;
32493 var image = previewEl.select('img', true).first();
32497 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32499 image.on('click', this.onClick, this, file);
32501 this.fireEvent('previewrendered', this, file);
32507 onPreviewLoad : function(file, image)
32509 if(typeof(file.target) == 'undefined' || !file.target){
32513 var width = image.dom.naturalWidth || image.dom.width;
32514 var height = image.dom.naturalHeight || image.dom.height;
32516 if(!this.previewResize) {
32520 if(width > height){
32521 file.target.addClass('wide');
32525 file.target.addClass('tall');
32530 uploadFromSource : function(file, crop)
32532 this.xhr = new XMLHttpRequest();
32534 this.managerEl.createChild({
32536 cls : 'roo-document-manager-loading',
32540 tooltip : file.name,
32541 cls : 'roo-document-manager-thumb',
32542 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32548 this.xhr.open(this.method, this.url, true);
32551 "Accept": "application/json",
32552 "Cache-Control": "no-cache",
32553 "X-Requested-With": "XMLHttpRequest"
32556 for (var headerName in headers) {
32557 var headerValue = headers[headerName];
32559 this.xhr.setRequestHeader(headerName, headerValue);
32565 this.xhr.onload = function()
32567 _this.xhrOnLoad(_this.xhr);
32570 this.xhr.onerror = function()
32572 _this.xhrOnError(_this.xhr);
32575 var formData = new FormData();
32577 formData.append('returnHTML', 'NO');
32579 formData.append('crop', crop);
32581 if(typeof(file.filename) != 'undefined'){
32582 formData.append('filename', file.filename);
32585 if(typeof(file.mimetype) != 'undefined'){
32586 formData.append('mimetype', file.mimetype);
32591 if(this.fireEvent('prepare', this, formData) != false){
32592 this.xhr.send(formData);
32602 * @class Roo.bootstrap.DocumentViewer
32603 * @extends Roo.bootstrap.Component
32604 * Bootstrap DocumentViewer class
32605 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32606 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32609 * Create a new DocumentViewer
32610 * @param {Object} config The config object
32613 Roo.bootstrap.DocumentViewer = function(config){
32614 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32619 * Fire after initEvent
32620 * @param {Roo.bootstrap.DocumentViewer} this
32626 * @param {Roo.bootstrap.DocumentViewer} this
32631 * Fire after download button
32632 * @param {Roo.bootstrap.DocumentViewer} this
32637 * Fire after trash button
32638 * @param {Roo.bootstrap.DocumentViewer} this
32645 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32647 showDownload : true,
32651 getAutoCreate : function()
32655 cls : 'roo-document-viewer',
32659 cls : 'roo-document-viewer-body',
32663 cls : 'roo-document-viewer-thumb',
32667 cls : 'roo-document-viewer-image'
32675 cls : 'roo-document-viewer-footer',
32678 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32682 cls : 'btn-group roo-document-viewer-download',
32686 cls : 'btn btn-default',
32687 html : '<i class="fa fa-download"></i>'
32693 cls : 'btn-group roo-document-viewer-trash',
32697 cls : 'btn btn-default',
32698 html : '<i class="fa fa-trash"></i>'
32711 initEvents : function()
32713 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32714 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32716 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32717 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32719 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32720 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32722 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32723 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32725 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32726 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32728 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32729 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32731 this.bodyEl.on('click', this.onClick, this);
32732 this.downloadBtn.on('click', this.onDownload, this);
32733 this.trashBtn.on('click', this.onTrash, this);
32735 this.downloadBtn.hide();
32736 this.trashBtn.hide();
32738 if(this.showDownload){
32739 this.downloadBtn.show();
32742 if(this.showTrash){
32743 this.trashBtn.show();
32746 if(!this.showDownload && !this.showTrash) {
32747 this.footerEl.hide();
32752 initial : function()
32754 this.fireEvent('initial', this);
32758 onClick : function(e)
32760 e.preventDefault();
32762 this.fireEvent('click', this);
32765 onDownload : function(e)
32767 e.preventDefault();
32769 this.fireEvent('download', this);
32772 onTrash : function(e)
32774 e.preventDefault();
32776 this.fireEvent('trash', this);
32788 * @class Roo.bootstrap.NavProgressBar
32789 * @extends Roo.bootstrap.Component
32790 * Bootstrap NavProgressBar class
32793 * Create a new nav progress bar
32794 * @param {Object} config The config object
32797 Roo.bootstrap.NavProgressBar = function(config){
32798 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32800 this.bullets = this.bullets || [];
32802 // Roo.bootstrap.NavProgressBar.register(this);
32806 * Fires when the active item changes
32807 * @param {Roo.bootstrap.NavProgressBar} this
32808 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32809 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32816 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32821 getAutoCreate : function()
32823 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32827 cls : 'roo-navigation-bar-group',
32831 cls : 'roo-navigation-top-bar'
32835 cls : 'roo-navigation-bullets-bar',
32839 cls : 'roo-navigation-bar'
32846 cls : 'roo-navigation-bottom-bar'
32856 initEvents: function()
32861 onRender : function(ct, position)
32863 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32865 if(this.bullets.length){
32866 Roo.each(this.bullets, function(b){
32875 addItem : function(cfg)
32877 var item = new Roo.bootstrap.NavProgressItem(cfg);
32879 item.parentId = this.id;
32880 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32883 var top = new Roo.bootstrap.Element({
32885 cls : 'roo-navigation-bar-text'
32888 var bottom = new Roo.bootstrap.Element({
32890 cls : 'roo-navigation-bar-text'
32893 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32894 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32896 var topText = new Roo.bootstrap.Element({
32898 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32901 var bottomText = new Roo.bootstrap.Element({
32903 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32906 topText.onRender(top.el, null);
32907 bottomText.onRender(bottom.el, null);
32910 item.bottomEl = bottom;
32913 this.barItems.push(item);
32918 getActive : function()
32920 var active = false;
32922 Roo.each(this.barItems, function(v){
32924 if (!v.isActive()) {
32936 setActiveItem : function(item)
32940 Roo.each(this.barItems, function(v){
32941 if (v.rid == item.rid) {
32945 if (v.isActive()) {
32946 v.setActive(false);
32951 item.setActive(true);
32953 this.fireEvent('changed', this, item, prev);
32956 getBarItem: function(rid)
32960 Roo.each(this.barItems, function(e) {
32961 if (e.rid != rid) {
32972 indexOfItem : function(item)
32976 Roo.each(this.barItems, function(v, i){
32978 if (v.rid != item.rid) {
32989 setActiveNext : function()
32991 var i = this.indexOfItem(this.getActive());
32993 if (i > this.barItems.length) {
32997 this.setActiveItem(this.barItems[i+1]);
33000 setActivePrev : function()
33002 var i = this.indexOfItem(this.getActive());
33008 this.setActiveItem(this.barItems[i-1]);
33011 format : function()
33013 if(!this.barItems.length){
33017 var width = 100 / this.barItems.length;
33019 Roo.each(this.barItems, function(i){
33020 i.el.setStyle('width', width + '%');
33021 i.topEl.el.setStyle('width', width + '%');
33022 i.bottomEl.el.setStyle('width', width + '%');
33031 * Nav Progress Item
33036 * @class Roo.bootstrap.NavProgressItem
33037 * @extends Roo.bootstrap.Component
33038 * Bootstrap NavProgressItem class
33039 * @cfg {String} rid the reference id
33040 * @cfg {Boolean} active (true|false) Is item active default false
33041 * @cfg {Boolean} disabled (true|false) Is item active default false
33042 * @cfg {String} html
33043 * @cfg {String} position (top|bottom) text position default bottom
33044 * @cfg {String} icon show icon instead of number
33047 * Create a new NavProgressItem
33048 * @param {Object} config The config object
33050 Roo.bootstrap.NavProgressItem = function(config){
33051 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33056 * The raw click event for the entire grid.
33057 * @param {Roo.bootstrap.NavProgressItem} this
33058 * @param {Roo.EventObject} e
33065 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33071 position : 'bottom',
33074 getAutoCreate : function()
33076 var iconCls = 'roo-navigation-bar-item-icon';
33078 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33082 cls: 'roo-navigation-bar-item',
33092 cfg.cls += ' active';
33095 cfg.cls += ' disabled';
33101 disable : function()
33103 this.setDisabled(true);
33106 enable : function()
33108 this.setDisabled(false);
33111 initEvents: function()
33113 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33115 this.iconEl.on('click', this.onClick, this);
33118 onClick : function(e)
33120 e.preventDefault();
33126 if(this.fireEvent('click', this, e) === false){
33130 this.parent().setActiveItem(this);
33133 isActive: function ()
33135 return this.active;
33138 setActive : function(state)
33140 if(this.active == state){
33144 this.active = state;
33147 this.el.addClass('active');
33151 this.el.removeClass('active');
33156 setDisabled : function(state)
33158 if(this.disabled == state){
33162 this.disabled = state;
33165 this.el.addClass('disabled');
33169 this.el.removeClass('disabled');
33172 tooltipEl : function()
33174 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33187 * @class Roo.bootstrap.FieldLabel
33188 * @extends Roo.bootstrap.Component
33189 * Bootstrap FieldLabel class
33190 * @cfg {String} html contents of the element
33191 * @cfg {String} tag tag of the element default label
33192 * @cfg {String} cls class of the element
33193 * @cfg {String} target label target
33194 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33195 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33196 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33197 * @cfg {String} iconTooltip default "This field is required"
33198 * @cfg {String} indicatorpos (left|right) default left
33201 * Create a new FieldLabel
33202 * @param {Object} config The config object
33205 Roo.bootstrap.FieldLabel = function(config){
33206 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33211 * Fires after the field has been marked as invalid.
33212 * @param {Roo.form.FieldLabel} this
33213 * @param {String} msg The validation message
33218 * Fires after the field has been validated with no errors.
33219 * @param {Roo.form.FieldLabel} this
33225 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33232 invalidClass : 'has-warning',
33233 validClass : 'has-success',
33234 iconTooltip : 'This field is required',
33235 indicatorpos : 'left',
33237 getAutoCreate : function(){
33240 if (!this.allowBlank) {
33246 cls : 'roo-bootstrap-field-label ' + this.cls,
33251 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33252 tooltip : this.iconTooltip
33261 if(this.indicatorpos == 'right'){
33264 cls : 'roo-bootstrap-field-label ' + this.cls,
33273 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33274 tooltip : this.iconTooltip
33283 initEvents: function()
33285 Roo.bootstrap.Element.superclass.initEvents.call(this);
33287 this.indicator = this.indicatorEl();
33289 if(this.indicator){
33290 this.indicator.removeClass('visible');
33291 this.indicator.addClass('invisible');
33294 Roo.bootstrap.FieldLabel.register(this);
33297 indicatorEl : function()
33299 var indicator = this.el.select('i.roo-required-indicator',true).first();
33310 * Mark this field as valid
33312 markValid : function()
33314 if(this.indicator){
33315 this.indicator.removeClass('visible');
33316 this.indicator.addClass('invisible');
33318 if (Roo.bootstrap.version == 3) {
33319 this.el.removeClass(this.invalidClass);
33320 this.el.addClass(this.validClass);
33322 this.el.removeClass('is-invalid');
33323 this.el.addClass('is-valid');
33327 this.fireEvent('valid', this);
33331 * Mark this field as invalid
33332 * @param {String} msg The validation message
33334 markInvalid : function(msg)
33336 if(this.indicator){
33337 this.indicator.removeClass('invisible');
33338 this.indicator.addClass('visible');
33340 if (Roo.bootstrap.version == 3) {
33341 this.el.removeClass(this.validClass);
33342 this.el.addClass(this.invalidClass);
33344 this.el.removeClass('is-valid');
33345 this.el.addClass('is-invalid');
33349 this.fireEvent('invalid', this, msg);
33355 Roo.apply(Roo.bootstrap.FieldLabel, {
33360 * register a FieldLabel Group
33361 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33363 register : function(label)
33365 if(this.groups.hasOwnProperty(label.target)){
33369 this.groups[label.target] = label;
33373 * fetch a FieldLabel Group based on the target
33374 * @param {string} target
33375 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33377 get: function(target) {
33378 if (typeof(this.groups[target]) == 'undefined') {
33382 return this.groups[target] ;
33391 * page DateSplitField.
33397 * @class Roo.bootstrap.DateSplitField
33398 * @extends Roo.bootstrap.Component
33399 * Bootstrap DateSplitField class
33400 * @cfg {string} fieldLabel - the label associated
33401 * @cfg {Number} labelWidth set the width of label (0-12)
33402 * @cfg {String} labelAlign (top|left)
33403 * @cfg {Boolean} dayAllowBlank (true|false) default false
33404 * @cfg {Boolean} monthAllowBlank (true|false) default false
33405 * @cfg {Boolean} yearAllowBlank (true|false) default false
33406 * @cfg {string} dayPlaceholder
33407 * @cfg {string} monthPlaceholder
33408 * @cfg {string} yearPlaceholder
33409 * @cfg {string} dayFormat default 'd'
33410 * @cfg {string} monthFormat default 'm'
33411 * @cfg {string} yearFormat default 'Y'
33412 * @cfg {Number} labellg set the width of label (1-12)
33413 * @cfg {Number} labelmd set the width of label (1-12)
33414 * @cfg {Number} labelsm set the width of label (1-12)
33415 * @cfg {Number} labelxs set the width of label (1-12)
33419 * Create a new DateSplitField
33420 * @param {Object} config The config object
33423 Roo.bootstrap.DateSplitField = function(config){
33424 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33430 * getting the data of years
33431 * @param {Roo.bootstrap.DateSplitField} this
33432 * @param {Object} years
33437 * getting the data of days
33438 * @param {Roo.bootstrap.DateSplitField} this
33439 * @param {Object} days
33444 * Fires after the field has been marked as invalid.
33445 * @param {Roo.form.Field} this
33446 * @param {String} msg The validation message
33451 * Fires after the field has been validated with no errors.
33452 * @param {Roo.form.Field} this
33458 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33461 labelAlign : 'top',
33463 dayAllowBlank : false,
33464 monthAllowBlank : false,
33465 yearAllowBlank : false,
33466 dayPlaceholder : '',
33467 monthPlaceholder : '',
33468 yearPlaceholder : '',
33472 isFormField : true,
33478 getAutoCreate : function()
33482 cls : 'row roo-date-split-field-group',
33487 cls : 'form-hidden-field roo-date-split-field-group-value',
33493 var labelCls = 'col-md-12';
33494 var contentCls = 'col-md-4';
33496 if(this.fieldLabel){
33500 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33504 html : this.fieldLabel
33509 if(this.labelAlign == 'left'){
33511 if(this.labelWidth > 12){
33512 label.style = "width: " + this.labelWidth + 'px';
33515 if(this.labelWidth < 13 && this.labelmd == 0){
33516 this.labelmd = this.labelWidth;
33519 if(this.labellg > 0){
33520 labelCls = ' col-lg-' + this.labellg;
33521 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33524 if(this.labelmd > 0){
33525 labelCls = ' col-md-' + this.labelmd;
33526 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33529 if(this.labelsm > 0){
33530 labelCls = ' col-sm-' + this.labelsm;
33531 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33534 if(this.labelxs > 0){
33535 labelCls = ' col-xs-' + this.labelxs;
33536 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33540 label.cls += ' ' + labelCls;
33542 cfg.cn.push(label);
33545 Roo.each(['day', 'month', 'year'], function(t){
33548 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33555 inputEl: function ()
33557 return this.el.select('.roo-date-split-field-group-value', true).first();
33560 onRender : function(ct, position)
33564 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33566 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33568 this.dayField = new Roo.bootstrap.ComboBox({
33569 allowBlank : this.dayAllowBlank,
33570 alwaysQuery : true,
33571 displayField : 'value',
33574 forceSelection : true,
33576 placeholder : this.dayPlaceholder,
33577 selectOnFocus : true,
33578 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33579 triggerAction : 'all',
33581 valueField : 'value',
33582 store : new Roo.data.SimpleStore({
33583 data : (function() {
33585 _this.fireEvent('days', _this, days);
33588 fields : [ 'value' ]
33591 select : function (_self, record, index)
33593 _this.setValue(_this.getValue());
33598 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33600 this.monthField = new Roo.bootstrap.MonthField({
33601 after : '<i class=\"fa fa-calendar\"></i>',
33602 allowBlank : this.monthAllowBlank,
33603 placeholder : this.monthPlaceholder,
33606 render : function (_self)
33608 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33609 e.preventDefault();
33613 select : function (_self, oldvalue, newvalue)
33615 _this.setValue(_this.getValue());
33620 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33622 this.yearField = new Roo.bootstrap.ComboBox({
33623 allowBlank : this.yearAllowBlank,
33624 alwaysQuery : true,
33625 displayField : 'value',
33628 forceSelection : true,
33630 placeholder : this.yearPlaceholder,
33631 selectOnFocus : true,
33632 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33633 triggerAction : 'all',
33635 valueField : 'value',
33636 store : new Roo.data.SimpleStore({
33637 data : (function() {
33639 _this.fireEvent('years', _this, years);
33642 fields : [ 'value' ]
33645 select : function (_self, record, index)
33647 _this.setValue(_this.getValue());
33652 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33655 setValue : function(v, format)
33657 this.inputEl.dom.value = v;
33659 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33661 var d = Date.parseDate(v, f);
33668 this.setDay(d.format(this.dayFormat));
33669 this.setMonth(d.format(this.monthFormat));
33670 this.setYear(d.format(this.yearFormat));
33677 setDay : function(v)
33679 this.dayField.setValue(v);
33680 this.inputEl.dom.value = this.getValue();
33685 setMonth : function(v)
33687 this.monthField.setValue(v, true);
33688 this.inputEl.dom.value = this.getValue();
33693 setYear : function(v)
33695 this.yearField.setValue(v);
33696 this.inputEl.dom.value = this.getValue();
33701 getDay : function()
33703 return this.dayField.getValue();
33706 getMonth : function()
33708 return this.monthField.getValue();
33711 getYear : function()
33713 return this.yearField.getValue();
33716 getValue : function()
33718 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33720 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33730 this.inputEl.dom.value = '';
33735 validate : function()
33737 var d = this.dayField.validate();
33738 var m = this.monthField.validate();
33739 var y = this.yearField.validate();
33744 (!this.dayAllowBlank && !d) ||
33745 (!this.monthAllowBlank && !m) ||
33746 (!this.yearAllowBlank && !y)
33751 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33760 this.markInvalid();
33765 markValid : function()
33768 var label = this.el.select('label', true).first();
33769 var icon = this.el.select('i.fa-star', true).first();
33775 this.fireEvent('valid', this);
33779 * Mark this field as invalid
33780 * @param {String} msg The validation message
33782 markInvalid : function(msg)
33785 var label = this.el.select('label', true).first();
33786 var icon = this.el.select('i.fa-star', true).first();
33788 if(label && !icon){
33789 this.el.select('.roo-date-split-field-label', true).createChild({
33791 cls : 'text-danger fa fa-lg fa-star',
33792 tooltip : 'This field is required',
33793 style : 'margin-right:5px;'
33797 this.fireEvent('invalid', this, msg);
33800 clearInvalid : function()
33802 var label = this.el.select('label', true).first();
33803 var icon = this.el.select('i.fa-star', true).first();
33809 this.fireEvent('valid', this);
33812 getName: function()
33822 * http://masonry.desandro.com
33824 * The idea is to render all the bricks based on vertical width...
33826 * The original code extends 'outlayer' - we might need to use that....
33832 * @class Roo.bootstrap.LayoutMasonry
33833 * @extends Roo.bootstrap.Component
33834 * Bootstrap Layout Masonry class
33837 * Create a new Element
33838 * @param {Object} config The config object
33841 Roo.bootstrap.LayoutMasonry = function(config){
33843 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33847 Roo.bootstrap.LayoutMasonry.register(this);
33853 * Fire after layout the items
33854 * @param {Roo.bootstrap.LayoutMasonry} this
33855 * @param {Roo.EventObject} e
33862 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33865 * @cfg {Boolean} isLayoutInstant = no animation?
33867 isLayoutInstant : false, // needed?
33870 * @cfg {Number} boxWidth width of the columns
33875 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33880 * @cfg {Number} padWidth padding below box..
33885 * @cfg {Number} gutter gutter width..
33890 * @cfg {Number} maxCols maximum number of columns
33896 * @cfg {Boolean} isAutoInitial defalut true
33898 isAutoInitial : true,
33903 * @cfg {Boolean} isHorizontal defalut false
33905 isHorizontal : false,
33907 currentSize : null,
33913 bricks: null, //CompositeElement
33917 _isLayoutInited : false,
33919 // isAlternative : false, // only use for vertical layout...
33922 * @cfg {Number} alternativePadWidth padding below box..
33924 alternativePadWidth : 50,
33926 selectedBrick : [],
33928 getAutoCreate : function(){
33930 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33934 cls: 'blog-masonary-wrapper ' + this.cls,
33936 cls : 'mas-boxes masonary'
33943 getChildContainer: function( )
33945 if (this.boxesEl) {
33946 return this.boxesEl;
33949 this.boxesEl = this.el.select('.mas-boxes').first();
33951 return this.boxesEl;
33955 initEvents : function()
33959 if(this.isAutoInitial){
33960 Roo.log('hook children rendered');
33961 this.on('childrenrendered', function() {
33962 Roo.log('children rendered');
33968 initial : function()
33970 this.selectedBrick = [];
33972 this.currentSize = this.el.getBox(true);
33974 Roo.EventManager.onWindowResize(this.resize, this);
33976 if(!this.isAutoInitial){
33984 //this.layout.defer(500,this);
33988 resize : function()
33990 var cs = this.el.getBox(true);
33993 this.currentSize.width == cs.width &&
33994 this.currentSize.x == cs.x &&
33995 this.currentSize.height == cs.height &&
33996 this.currentSize.y == cs.y
33998 Roo.log("no change in with or X or Y");
34002 this.currentSize = cs;
34008 layout : function()
34010 this._resetLayout();
34012 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34014 this.layoutItems( isInstant );
34016 this._isLayoutInited = true;
34018 this.fireEvent('layout', this);
34022 _resetLayout : function()
34024 if(this.isHorizontal){
34025 this.horizontalMeasureColumns();
34029 this.verticalMeasureColumns();
34033 verticalMeasureColumns : function()
34035 this.getContainerWidth();
34037 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34038 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34042 var boxWidth = this.boxWidth + this.padWidth;
34044 if(this.containerWidth < this.boxWidth){
34045 boxWidth = this.containerWidth
34048 var containerWidth = this.containerWidth;
34050 var cols = Math.floor(containerWidth / boxWidth);
34052 this.cols = Math.max( cols, 1 );
34054 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34056 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34058 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34060 this.colWidth = boxWidth + avail - this.padWidth;
34062 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34063 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34066 horizontalMeasureColumns : function()
34068 this.getContainerWidth();
34070 var boxWidth = this.boxWidth;
34072 if(this.containerWidth < boxWidth){
34073 boxWidth = this.containerWidth;
34076 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34078 this.el.setHeight(boxWidth);
34082 getContainerWidth : function()
34084 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34087 layoutItems : function( isInstant )
34089 Roo.log(this.bricks);
34091 var items = Roo.apply([], this.bricks);
34093 if(this.isHorizontal){
34094 this._horizontalLayoutItems( items , isInstant );
34098 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34099 // this._verticalAlternativeLayoutItems( items , isInstant );
34103 this._verticalLayoutItems( items , isInstant );
34107 _verticalLayoutItems : function ( items , isInstant)
34109 if ( !items || !items.length ) {
34114 ['xs', 'xs', 'xs', 'tall'],
34115 ['xs', 'xs', 'tall'],
34116 ['xs', 'xs', 'sm'],
34117 ['xs', 'xs', 'xs'],
34123 ['sm', 'xs', 'xs'],
34127 ['tall', 'xs', 'xs', 'xs'],
34128 ['tall', 'xs', 'xs'],
34140 Roo.each(items, function(item, k){
34142 switch (item.size) {
34143 // these layouts take up a full box,
34154 boxes.push([item]);
34177 var filterPattern = function(box, length)
34185 var pattern = box.slice(0, length);
34189 Roo.each(pattern, function(i){
34190 format.push(i.size);
34193 Roo.each(standard, function(s){
34195 if(String(s) != String(format)){
34204 if(!match && length == 1){
34209 filterPattern(box, length - 1);
34213 queue.push(pattern);
34215 box = box.slice(length, box.length);
34217 filterPattern(box, 4);
34223 Roo.each(boxes, function(box, k){
34229 if(box.length == 1){
34234 filterPattern(box, 4);
34238 this._processVerticalLayoutQueue( queue, isInstant );
34242 // _verticalAlternativeLayoutItems : function( items , isInstant )
34244 // if ( !items || !items.length ) {
34248 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34252 _horizontalLayoutItems : function ( items , isInstant)
34254 if ( !items || !items.length || items.length < 3) {
34260 var eItems = items.slice(0, 3);
34262 items = items.slice(3, items.length);
34265 ['xs', 'xs', 'xs', 'wide'],
34266 ['xs', 'xs', 'wide'],
34267 ['xs', 'xs', 'sm'],
34268 ['xs', 'xs', 'xs'],
34274 ['sm', 'xs', 'xs'],
34278 ['wide', 'xs', 'xs', 'xs'],
34279 ['wide', 'xs', 'xs'],
34292 Roo.each(items, function(item, k){
34294 switch (item.size) {
34305 boxes.push([item]);
34329 var filterPattern = function(box, length)
34337 var pattern = box.slice(0, length);
34341 Roo.each(pattern, function(i){
34342 format.push(i.size);
34345 Roo.each(standard, function(s){
34347 if(String(s) != String(format)){
34356 if(!match && length == 1){
34361 filterPattern(box, length - 1);
34365 queue.push(pattern);
34367 box = box.slice(length, box.length);
34369 filterPattern(box, 4);
34375 Roo.each(boxes, function(box, k){
34381 if(box.length == 1){
34386 filterPattern(box, 4);
34393 var pos = this.el.getBox(true);
34397 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34399 var hit_end = false;
34401 Roo.each(queue, function(box){
34405 Roo.each(box, function(b){
34407 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34417 Roo.each(box, function(b){
34419 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34422 mx = Math.max(mx, b.x);
34426 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34430 Roo.each(box, function(b){
34432 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34446 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34449 /** Sets position of item in DOM
34450 * @param {Element} item
34451 * @param {Number} x - horizontal position
34452 * @param {Number} y - vertical position
34453 * @param {Boolean} isInstant - disables transitions
34455 _processVerticalLayoutQueue : function( queue, isInstant )
34457 var pos = this.el.getBox(true);
34462 for (var i = 0; i < this.cols; i++){
34466 Roo.each(queue, function(box, k){
34468 var col = k % this.cols;
34470 Roo.each(box, function(b,kk){
34472 b.el.position('absolute');
34474 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34475 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34477 if(b.size == 'md-left' || b.size == 'md-right'){
34478 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34479 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34482 b.el.setWidth(width);
34483 b.el.setHeight(height);
34485 b.el.select('iframe',true).setSize(width,height);
34489 for (var i = 0; i < this.cols; i++){
34491 if(maxY[i] < maxY[col]){
34496 col = Math.min(col, i);
34500 x = pos.x + col * (this.colWidth + this.padWidth);
34504 var positions = [];
34506 switch (box.length){
34508 positions = this.getVerticalOneBoxColPositions(x, y, box);
34511 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34514 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34517 positions = this.getVerticalFourBoxColPositions(x, y, box);
34523 Roo.each(box, function(b,kk){
34525 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34527 var sz = b.el.getSize();
34529 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34537 for (var i = 0; i < this.cols; i++){
34538 mY = Math.max(mY, maxY[i]);
34541 this.el.setHeight(mY - pos.y);
34545 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34547 // var pos = this.el.getBox(true);
34550 // var maxX = pos.right;
34552 // var maxHeight = 0;
34554 // Roo.each(items, function(item, k){
34558 // item.el.position('absolute');
34560 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34562 // item.el.setWidth(width);
34564 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34566 // item.el.setHeight(height);
34569 // item.el.setXY([x, y], isInstant ? false : true);
34571 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34574 // y = y + height + this.alternativePadWidth;
34576 // maxHeight = maxHeight + height + this.alternativePadWidth;
34580 // this.el.setHeight(maxHeight);
34584 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34586 var pos = this.el.getBox(true);
34591 var maxX = pos.right;
34593 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34595 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34597 Roo.each(queue, function(box, k){
34599 Roo.each(box, function(b, kk){
34601 b.el.position('absolute');
34603 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34604 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34606 if(b.size == 'md-left' || b.size == 'md-right'){
34607 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34608 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34611 b.el.setWidth(width);
34612 b.el.setHeight(height);
34620 var positions = [];
34622 switch (box.length){
34624 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34627 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34630 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34633 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34639 Roo.each(box, function(b,kk){
34641 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34643 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34651 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34653 Roo.each(eItems, function(b,k){
34655 b.size = (k == 0) ? 'sm' : 'xs';
34656 b.x = (k == 0) ? 2 : 1;
34657 b.y = (k == 0) ? 2 : 1;
34659 b.el.position('absolute');
34661 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34663 b.el.setWidth(width);
34665 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34667 b.el.setHeight(height);
34671 var positions = [];
34674 x : maxX - this.unitWidth * 2 - this.gutter,
34679 x : maxX - this.unitWidth,
34680 y : minY + (this.unitWidth + this.gutter) * 2
34684 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34688 Roo.each(eItems, function(b,k){
34690 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34696 getVerticalOneBoxColPositions : function(x, y, box)
34700 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34702 if(box[0].size == 'md-left'){
34706 if(box[0].size == 'md-right'){
34711 x : x + (this.unitWidth + this.gutter) * rand,
34718 getVerticalTwoBoxColPositions : function(x, y, box)
34722 if(box[0].size == 'xs'){
34726 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34730 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34744 x : x + (this.unitWidth + this.gutter) * 2,
34745 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34752 getVerticalThreeBoxColPositions : function(x, y, box)
34756 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34764 x : x + (this.unitWidth + this.gutter) * 1,
34769 x : x + (this.unitWidth + this.gutter) * 2,
34777 if(box[0].size == 'xs' && box[1].size == 'xs'){
34786 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34790 x : x + (this.unitWidth + this.gutter) * 1,
34804 x : x + (this.unitWidth + this.gutter) * 2,
34809 x : x + (this.unitWidth + this.gutter) * 2,
34810 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34817 getVerticalFourBoxColPositions : function(x, y, box)
34821 if(box[0].size == 'xs'){
34830 y : y + (this.unitHeight + this.gutter) * 1
34835 y : y + (this.unitHeight + this.gutter) * 2
34839 x : x + (this.unitWidth + this.gutter) * 1,
34853 x : x + (this.unitWidth + this.gutter) * 2,
34858 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34859 y : y + (this.unitHeight + this.gutter) * 1
34863 x : x + (this.unitWidth + this.gutter) * 2,
34864 y : y + (this.unitWidth + this.gutter) * 2
34871 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34875 if(box[0].size == 'md-left'){
34877 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34884 if(box[0].size == 'md-right'){
34886 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34887 y : minY + (this.unitWidth + this.gutter) * 1
34893 var rand = Math.floor(Math.random() * (4 - box[0].y));
34896 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34897 y : minY + (this.unitWidth + this.gutter) * rand
34904 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34908 if(box[0].size == 'xs'){
34911 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34916 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34917 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34925 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34930 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34931 y : minY + (this.unitWidth + this.gutter) * 2
34938 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34942 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34945 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34950 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34951 y : minY + (this.unitWidth + this.gutter) * 1
34955 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34956 y : minY + (this.unitWidth + this.gutter) * 2
34963 if(box[0].size == 'xs' && box[1].size == 'xs'){
34966 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34971 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34976 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34977 y : minY + (this.unitWidth + this.gutter) * 1
34985 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34990 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34991 y : minY + (this.unitWidth + this.gutter) * 2
34995 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34996 y : minY + (this.unitWidth + this.gutter) * 2
35003 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35007 if(box[0].size == 'xs'){
35010 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35015 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35020 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),
35025 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35026 y : minY + (this.unitWidth + this.gutter) * 1
35034 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35039 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35040 y : minY + (this.unitWidth + this.gutter) * 2
35044 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35045 y : minY + (this.unitWidth + this.gutter) * 2
35049 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),
35050 y : minY + (this.unitWidth + this.gutter) * 2
35058 * remove a Masonry Brick
35059 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35061 removeBrick : function(brick_id)
35067 for (var i = 0; i<this.bricks.length; i++) {
35068 if (this.bricks[i].id == brick_id) {
35069 this.bricks.splice(i,1);
35070 this.el.dom.removeChild(Roo.get(brick_id).dom);
35077 * adds a Masonry Brick
35078 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35080 addBrick : function(cfg)
35082 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35083 //this.register(cn);
35084 cn.parentId = this.id;
35085 cn.render(this.el);
35090 * register a Masonry Brick
35091 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35094 register : function(brick)
35096 this.bricks.push(brick);
35097 brick.masonryId = this.id;
35101 * clear all the Masonry Brick
35103 clearAll : function()
35106 //this.getChildContainer().dom.innerHTML = "";
35107 this.el.dom.innerHTML = '';
35110 getSelected : function()
35112 if (!this.selectedBrick) {
35116 return this.selectedBrick;
35120 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35124 * register a Masonry Layout
35125 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35128 register : function(layout)
35130 this.groups[layout.id] = layout;
35133 * fetch a Masonry Layout based on the masonry layout ID
35134 * @param {string} the masonry layout to add
35135 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35138 get: function(layout_id) {
35139 if (typeof(this.groups[layout_id]) == 'undefined') {
35142 return this.groups[layout_id] ;
35154 * http://masonry.desandro.com
35156 * The idea is to render all the bricks based on vertical width...
35158 * The original code extends 'outlayer' - we might need to use that....
35164 * @class Roo.bootstrap.LayoutMasonryAuto
35165 * @extends Roo.bootstrap.Component
35166 * Bootstrap Layout Masonry class
35169 * Create a new Element
35170 * @param {Object} config The config object
35173 Roo.bootstrap.LayoutMasonryAuto = function(config){
35174 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35177 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35180 * @cfg {Boolean} isFitWidth - resize the width..
35182 isFitWidth : false, // options..
35184 * @cfg {Boolean} isOriginLeft = left align?
35186 isOriginLeft : true,
35188 * @cfg {Boolean} isOriginTop = top align?
35190 isOriginTop : false,
35192 * @cfg {Boolean} isLayoutInstant = no animation?
35194 isLayoutInstant : false, // needed?
35196 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35198 isResizingContainer : true,
35200 * @cfg {Number} columnWidth width of the columns
35206 * @cfg {Number} maxCols maximum number of columns
35211 * @cfg {Number} padHeight padding below box..
35217 * @cfg {Boolean} isAutoInitial defalut true
35220 isAutoInitial : true,
35226 initialColumnWidth : 0,
35227 currentSize : null,
35229 colYs : null, // array.
35236 bricks: null, //CompositeElement
35237 cols : 0, // array?
35238 // element : null, // wrapped now this.el
35239 _isLayoutInited : null,
35242 getAutoCreate : function(){
35246 cls: 'blog-masonary-wrapper ' + this.cls,
35248 cls : 'mas-boxes masonary'
35255 getChildContainer: function( )
35257 if (this.boxesEl) {
35258 return this.boxesEl;
35261 this.boxesEl = this.el.select('.mas-boxes').first();
35263 return this.boxesEl;
35267 initEvents : function()
35271 if(this.isAutoInitial){
35272 Roo.log('hook children rendered');
35273 this.on('childrenrendered', function() {
35274 Roo.log('children rendered');
35281 initial : function()
35283 this.reloadItems();
35285 this.currentSize = this.el.getBox(true);
35287 /// was window resize... - let's see if this works..
35288 Roo.EventManager.onWindowResize(this.resize, this);
35290 if(!this.isAutoInitial){
35295 this.layout.defer(500,this);
35298 reloadItems: function()
35300 this.bricks = this.el.select('.masonry-brick', true);
35302 this.bricks.each(function(b) {
35303 //Roo.log(b.getSize());
35304 if (!b.attr('originalwidth')) {
35305 b.attr('originalwidth', b.getSize().width);
35310 Roo.log(this.bricks.elements.length);
35313 resize : function()
35316 var cs = this.el.getBox(true);
35318 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35319 Roo.log("no change in with or X");
35322 this.currentSize = cs;
35326 layout : function()
35329 this._resetLayout();
35330 //this._manageStamps();
35332 // don't animate first layout
35333 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35334 this.layoutItems( isInstant );
35336 // flag for initalized
35337 this._isLayoutInited = true;
35340 layoutItems : function( isInstant )
35342 //var items = this._getItemsForLayout( this.items );
35343 // original code supports filtering layout items.. we just ignore it..
35345 this._layoutItems( this.bricks , isInstant );
35347 this._postLayout();
35349 _layoutItems : function ( items , isInstant)
35351 //this.fireEvent( 'layout', this, items );
35354 if ( !items || !items.elements.length ) {
35355 // no items, emit event with empty array
35360 items.each(function(item) {
35361 Roo.log("layout item");
35363 // get x/y object from method
35364 var position = this._getItemLayoutPosition( item );
35366 position.item = item;
35367 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35368 queue.push( position );
35371 this._processLayoutQueue( queue );
35373 /** Sets position of item in DOM
35374 * @param {Element} item
35375 * @param {Number} x - horizontal position
35376 * @param {Number} y - vertical position
35377 * @param {Boolean} isInstant - disables transitions
35379 _processLayoutQueue : function( queue )
35381 for ( var i=0, len = queue.length; i < len; i++ ) {
35382 var obj = queue[i];
35383 obj.item.position('absolute');
35384 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35390 * Any logic you want to do after each layout,
35391 * i.e. size the container
35393 _postLayout : function()
35395 this.resizeContainer();
35398 resizeContainer : function()
35400 if ( !this.isResizingContainer ) {
35403 var size = this._getContainerSize();
35405 this.el.setSize(size.width,size.height);
35406 this.boxesEl.setSize(size.width,size.height);
35412 _resetLayout : function()
35414 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35415 this.colWidth = this.el.getWidth();
35416 //this.gutter = this.el.getWidth();
35418 this.measureColumns();
35424 this.colYs.push( 0 );
35430 measureColumns : function()
35432 this.getContainerWidth();
35433 // if columnWidth is 0, default to outerWidth of first item
35434 if ( !this.columnWidth ) {
35435 var firstItem = this.bricks.first();
35436 Roo.log(firstItem);
35437 this.columnWidth = this.containerWidth;
35438 if (firstItem && firstItem.attr('originalwidth') ) {
35439 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35441 // columnWidth fall back to item of first element
35442 Roo.log("set column width?");
35443 this.initialColumnWidth = this.columnWidth ;
35445 // if first elem has no width, default to size of container
35450 if (this.initialColumnWidth) {
35451 this.columnWidth = this.initialColumnWidth;
35456 // column width is fixed at the top - however if container width get's smaller we should
35459 // this bit calcs how man columns..
35461 var columnWidth = this.columnWidth += this.gutter;
35463 // calculate columns
35464 var containerWidth = this.containerWidth + this.gutter;
35466 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35467 // fix rounding errors, typically with gutters
35468 var excess = columnWidth - containerWidth % columnWidth;
35471 // if overshoot is less than a pixel, round up, otherwise floor it
35472 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35473 cols = Math[ mathMethod ]( cols );
35474 this.cols = Math.max( cols, 1 );
35475 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35477 // padding positioning..
35478 var totalColWidth = this.cols * this.columnWidth;
35479 var padavail = this.containerWidth - totalColWidth;
35480 // so for 2 columns - we need 3 'pads'
35482 var padNeeded = (1+this.cols) * this.padWidth;
35484 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35486 this.columnWidth += padExtra
35487 //this.padWidth = Math.floor(padavail / ( this.cols));
35489 // adjust colum width so that padding is fixed??
35491 // we have 3 columns ... total = width * 3
35492 // we have X left over... that should be used by
35494 //if (this.expandC) {
35502 getContainerWidth : function()
35504 /* // container is parent if fit width
35505 var container = this.isFitWidth ? this.element.parentNode : this.element;
35506 // check that this.size and size are there
35507 // IE8 triggers resize on body size change, so they might not be
35509 var size = getSize( container ); //FIXME
35510 this.containerWidth = size && size.innerWidth; //FIXME
35513 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35517 _getItemLayoutPosition : function( item ) // what is item?
35519 // we resize the item to our columnWidth..
35521 item.setWidth(this.columnWidth);
35522 item.autoBoxAdjust = false;
35524 var sz = item.getSize();
35526 // how many columns does this brick span
35527 var remainder = this.containerWidth % this.columnWidth;
35529 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35530 // round if off by 1 pixel, otherwise use ceil
35531 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35532 colSpan = Math.min( colSpan, this.cols );
35534 // normally this should be '1' as we dont' currently allow multi width columns..
35536 var colGroup = this._getColGroup( colSpan );
35537 // get the minimum Y value from the columns
35538 var minimumY = Math.min.apply( Math, colGroup );
35539 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35541 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35543 // position the brick
35545 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35546 y: this.currentSize.y + minimumY + this.padHeight
35550 // apply setHeight to necessary columns
35551 var setHeight = minimumY + sz.height + this.padHeight;
35552 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35554 var setSpan = this.cols + 1 - colGroup.length;
35555 for ( var i = 0; i < setSpan; i++ ) {
35556 this.colYs[ shortColIndex + i ] = setHeight ;
35563 * @param {Number} colSpan - number of columns the element spans
35564 * @returns {Array} colGroup
35566 _getColGroup : function( colSpan )
35568 if ( colSpan < 2 ) {
35569 // if brick spans only one column, use all the column Ys
35574 // how many different places could this brick fit horizontally
35575 var groupCount = this.cols + 1 - colSpan;
35576 // for each group potential horizontal position
35577 for ( var i = 0; i < groupCount; i++ ) {
35578 // make an array of colY values for that one group
35579 var groupColYs = this.colYs.slice( i, i + colSpan );
35580 // and get the max value of the array
35581 colGroup[i] = Math.max.apply( Math, groupColYs );
35586 _manageStamp : function( stamp )
35588 var stampSize = stamp.getSize();
35589 var offset = stamp.getBox();
35590 // get the columns that this stamp affects
35591 var firstX = this.isOriginLeft ? offset.x : offset.right;
35592 var lastX = firstX + stampSize.width;
35593 var firstCol = Math.floor( firstX / this.columnWidth );
35594 firstCol = Math.max( 0, firstCol );
35596 var lastCol = Math.floor( lastX / this.columnWidth );
35597 // lastCol should not go over if multiple of columnWidth #425
35598 lastCol -= lastX % this.columnWidth ? 0 : 1;
35599 lastCol = Math.min( this.cols - 1, lastCol );
35601 // set colYs to bottom of the stamp
35602 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35605 for ( var i = firstCol; i <= lastCol; i++ ) {
35606 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35611 _getContainerSize : function()
35613 this.maxY = Math.max.apply( Math, this.colYs );
35618 if ( this.isFitWidth ) {
35619 size.width = this._getContainerFitWidth();
35625 _getContainerFitWidth : function()
35627 var unusedCols = 0;
35628 // count unused columns
35631 if ( this.colYs[i] !== 0 ) {
35636 // fit container to columns that have been used
35637 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35640 needsResizeLayout : function()
35642 var previousWidth = this.containerWidth;
35643 this.getContainerWidth();
35644 return previousWidth !== this.containerWidth;
35659 * @class Roo.bootstrap.MasonryBrick
35660 * @extends Roo.bootstrap.Component
35661 * Bootstrap MasonryBrick class
35664 * Create a new MasonryBrick
35665 * @param {Object} config The config object
35668 Roo.bootstrap.MasonryBrick = function(config){
35670 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35672 Roo.bootstrap.MasonryBrick.register(this);
35678 * When a MasonryBrick is clcik
35679 * @param {Roo.bootstrap.MasonryBrick} this
35680 * @param {Roo.EventObject} e
35686 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35689 * @cfg {String} title
35693 * @cfg {String} html
35697 * @cfg {String} bgimage
35701 * @cfg {String} videourl
35705 * @cfg {String} cls
35709 * @cfg {String} href
35713 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35718 * @cfg {String} placetitle (center|bottom)
35723 * @cfg {Boolean} isFitContainer defalut true
35725 isFitContainer : true,
35728 * @cfg {Boolean} preventDefault defalut false
35730 preventDefault : false,
35733 * @cfg {Boolean} inverse defalut false
35735 maskInverse : false,
35737 getAutoCreate : function()
35739 if(!this.isFitContainer){
35740 return this.getSplitAutoCreate();
35743 var cls = 'masonry-brick masonry-brick-full';
35745 if(this.href.length){
35746 cls += ' masonry-brick-link';
35749 if(this.bgimage.length){
35750 cls += ' masonry-brick-image';
35753 if(this.maskInverse){
35754 cls += ' mask-inverse';
35757 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35758 cls += ' enable-mask';
35762 cls += ' masonry-' + this.size + '-brick';
35765 if(this.placetitle.length){
35767 switch (this.placetitle) {
35769 cls += ' masonry-center-title';
35772 cls += ' masonry-bottom-title';
35779 if(!this.html.length && !this.bgimage.length){
35780 cls += ' masonry-center-title';
35783 if(!this.html.length && this.bgimage.length){
35784 cls += ' masonry-bottom-title';
35789 cls += ' ' + this.cls;
35793 tag: (this.href.length) ? 'a' : 'div',
35798 cls: 'masonry-brick-mask'
35802 cls: 'masonry-brick-paragraph',
35808 if(this.href.length){
35809 cfg.href = this.href;
35812 var cn = cfg.cn[1].cn;
35814 if(this.title.length){
35817 cls: 'masonry-brick-title',
35822 if(this.html.length){
35825 cls: 'masonry-brick-text',
35830 if (!this.title.length && !this.html.length) {
35831 cfg.cn[1].cls += ' hide';
35834 if(this.bgimage.length){
35837 cls: 'masonry-brick-image-view',
35842 if(this.videourl.length){
35843 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35844 // youtube support only?
35847 cls: 'masonry-brick-image-view',
35850 allowfullscreen : true
35858 getSplitAutoCreate : function()
35860 var cls = 'masonry-brick masonry-brick-split';
35862 if(this.href.length){
35863 cls += ' masonry-brick-link';
35866 if(this.bgimage.length){
35867 cls += ' masonry-brick-image';
35871 cls += ' masonry-' + this.size + '-brick';
35874 switch (this.placetitle) {
35876 cls += ' masonry-center-title';
35879 cls += ' masonry-bottom-title';
35882 if(!this.bgimage.length){
35883 cls += ' masonry-center-title';
35886 if(this.bgimage.length){
35887 cls += ' masonry-bottom-title';
35893 cls += ' ' + this.cls;
35897 tag: (this.href.length) ? 'a' : 'div',
35902 cls: 'masonry-brick-split-head',
35906 cls: 'masonry-brick-paragraph',
35913 cls: 'masonry-brick-split-body',
35919 if(this.href.length){
35920 cfg.href = this.href;
35923 if(this.title.length){
35924 cfg.cn[0].cn[0].cn.push({
35926 cls: 'masonry-brick-title',
35931 if(this.html.length){
35932 cfg.cn[1].cn.push({
35934 cls: 'masonry-brick-text',
35939 if(this.bgimage.length){
35940 cfg.cn[0].cn.push({
35942 cls: 'masonry-brick-image-view',
35947 if(this.videourl.length){
35948 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35949 // youtube support only?
35950 cfg.cn[0].cn.cn.push({
35952 cls: 'masonry-brick-image-view',
35955 allowfullscreen : true
35962 initEvents: function()
35964 switch (this.size) {
35997 this.el.on('touchstart', this.onTouchStart, this);
35998 this.el.on('touchmove', this.onTouchMove, this);
35999 this.el.on('touchend', this.onTouchEnd, this);
36000 this.el.on('contextmenu', this.onContextMenu, this);
36002 this.el.on('mouseenter' ,this.enter, this);
36003 this.el.on('mouseleave', this.leave, this);
36004 this.el.on('click', this.onClick, this);
36007 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36008 this.parent().bricks.push(this);
36013 onClick: function(e, el)
36015 var time = this.endTimer - this.startTimer;
36016 // Roo.log(e.preventDefault());
36019 e.preventDefault();
36024 if(!this.preventDefault){
36028 e.preventDefault();
36030 if (this.activeClass != '') {
36031 this.selectBrick();
36034 this.fireEvent('click', this, e);
36037 enter: function(e, el)
36039 e.preventDefault();
36041 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36045 if(this.bgimage.length && this.html.length){
36046 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36050 leave: function(e, el)
36052 e.preventDefault();
36054 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36058 if(this.bgimage.length && this.html.length){
36059 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36063 onTouchStart: function(e, el)
36065 // e.preventDefault();
36067 this.touchmoved = false;
36069 if(!this.isFitContainer){
36073 if(!this.bgimage.length || !this.html.length){
36077 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36079 this.timer = new Date().getTime();
36083 onTouchMove: function(e, el)
36085 this.touchmoved = true;
36088 onContextMenu : function(e,el)
36090 e.preventDefault();
36091 e.stopPropagation();
36095 onTouchEnd: function(e, el)
36097 // e.preventDefault();
36099 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36106 if(!this.bgimage.length || !this.html.length){
36108 if(this.href.length){
36109 window.location.href = this.href;
36115 if(!this.isFitContainer){
36119 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36121 window.location.href = this.href;
36124 //selection on single brick only
36125 selectBrick : function() {
36127 if (!this.parentId) {
36131 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36132 var index = m.selectedBrick.indexOf(this.id);
36135 m.selectedBrick.splice(index,1);
36136 this.el.removeClass(this.activeClass);
36140 for(var i = 0; i < m.selectedBrick.length; i++) {
36141 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36142 b.el.removeClass(b.activeClass);
36145 m.selectedBrick = [];
36147 m.selectedBrick.push(this.id);
36148 this.el.addClass(this.activeClass);
36152 isSelected : function(){
36153 return this.el.hasClass(this.activeClass);
36158 Roo.apply(Roo.bootstrap.MasonryBrick, {
36161 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36163 * register a Masonry Brick
36164 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36167 register : function(brick)
36169 //this.groups[brick.id] = brick;
36170 this.groups.add(brick.id, brick);
36173 * fetch a masonry brick based on the masonry brick ID
36174 * @param {string} the masonry brick to add
36175 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36178 get: function(brick_id)
36180 // if (typeof(this.groups[brick_id]) == 'undefined') {
36183 // return this.groups[brick_id] ;
36185 if(this.groups.key(brick_id)) {
36186 return this.groups.key(brick_id);
36204 * @class Roo.bootstrap.Brick
36205 * @extends Roo.bootstrap.Component
36206 * Bootstrap Brick class
36209 * Create a new Brick
36210 * @param {Object} config The config object
36213 Roo.bootstrap.Brick = function(config){
36214 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36220 * When a Brick is click
36221 * @param {Roo.bootstrap.Brick} this
36222 * @param {Roo.EventObject} e
36228 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36231 * @cfg {String} title
36235 * @cfg {String} html
36239 * @cfg {String} bgimage
36243 * @cfg {String} cls
36247 * @cfg {String} href
36251 * @cfg {String} video
36255 * @cfg {Boolean} square
36259 getAutoCreate : function()
36261 var cls = 'roo-brick';
36263 if(this.href.length){
36264 cls += ' roo-brick-link';
36267 if(this.bgimage.length){
36268 cls += ' roo-brick-image';
36271 if(!this.html.length && !this.bgimage.length){
36272 cls += ' roo-brick-center-title';
36275 if(!this.html.length && this.bgimage.length){
36276 cls += ' roo-brick-bottom-title';
36280 cls += ' ' + this.cls;
36284 tag: (this.href.length) ? 'a' : 'div',
36289 cls: 'roo-brick-paragraph',
36295 if(this.href.length){
36296 cfg.href = this.href;
36299 var cn = cfg.cn[0].cn;
36301 if(this.title.length){
36304 cls: 'roo-brick-title',
36309 if(this.html.length){
36312 cls: 'roo-brick-text',
36319 if(this.bgimage.length){
36322 cls: 'roo-brick-image-view',
36330 initEvents: function()
36332 if(this.title.length || this.html.length){
36333 this.el.on('mouseenter' ,this.enter, this);
36334 this.el.on('mouseleave', this.leave, this);
36337 Roo.EventManager.onWindowResize(this.resize, this);
36339 if(this.bgimage.length){
36340 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36341 this.imageEl.on('load', this.onImageLoad, this);
36348 onImageLoad : function()
36353 resize : function()
36355 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36357 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36359 if(this.bgimage.length){
36360 var image = this.el.select('.roo-brick-image-view', true).first();
36362 image.setWidth(paragraph.getWidth());
36365 image.setHeight(paragraph.getWidth());
36368 this.el.setHeight(image.getHeight());
36369 paragraph.setHeight(image.getHeight());
36375 enter: function(e, el)
36377 e.preventDefault();
36379 if(this.bgimage.length){
36380 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36381 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36385 leave: function(e, el)
36387 e.preventDefault();
36389 if(this.bgimage.length){
36390 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36391 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36406 * @class Roo.bootstrap.NumberField
36407 * @extends Roo.bootstrap.Input
36408 * Bootstrap NumberField class
36414 * Create a new NumberField
36415 * @param {Object} config The config object
36418 Roo.bootstrap.NumberField = function(config){
36419 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36422 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36425 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36427 allowDecimals : true,
36429 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36431 decimalSeparator : ".",
36433 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36435 decimalPrecision : 2,
36437 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36439 allowNegative : true,
36442 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36446 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36448 minValue : Number.NEGATIVE_INFINITY,
36450 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36452 maxValue : Number.MAX_VALUE,
36454 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36456 minText : "The minimum value for this field is {0}",
36458 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36460 maxText : "The maximum value for this field is {0}",
36462 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36463 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36465 nanText : "{0} is not a valid number",
36467 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36469 thousandsDelimiter : false,
36471 * @cfg {String} valueAlign alignment of value
36473 valueAlign : "left",
36475 getAutoCreate : function()
36477 var hiddenInput = {
36481 cls: 'hidden-number-input'
36485 hiddenInput.name = this.name;
36490 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36492 this.name = hiddenInput.name;
36494 if(cfg.cn.length > 0) {
36495 cfg.cn.push(hiddenInput);
36502 initEvents : function()
36504 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36506 var allowed = "0123456789";
36508 if(this.allowDecimals){
36509 allowed += this.decimalSeparator;
36512 if(this.allowNegative){
36516 if(this.thousandsDelimiter) {
36520 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36522 var keyPress = function(e){
36524 var k = e.getKey();
36526 var c = e.getCharCode();
36529 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36530 allowed.indexOf(String.fromCharCode(c)) === -1
36536 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36540 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36545 this.el.on("keypress", keyPress, this);
36548 validateValue : function(value)
36551 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36555 var num = this.parseValue(value);
36558 this.markInvalid(String.format(this.nanText, value));
36562 if(num < this.minValue){
36563 this.markInvalid(String.format(this.minText, this.minValue));
36567 if(num > this.maxValue){
36568 this.markInvalid(String.format(this.maxText, this.maxValue));
36575 getValue : function()
36577 var v = this.hiddenEl().getValue();
36579 return this.fixPrecision(this.parseValue(v));
36582 parseValue : function(value)
36584 if(this.thousandsDelimiter) {
36586 r = new RegExp(",", "g");
36587 value = value.replace(r, "");
36590 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36591 return isNaN(value) ? '' : value;
36594 fixPrecision : function(value)
36596 if(this.thousandsDelimiter) {
36598 r = new RegExp(",", "g");
36599 value = value.replace(r, "");
36602 var nan = isNaN(value);
36604 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36605 return nan ? '' : value;
36607 return parseFloat(value).toFixed(this.decimalPrecision);
36610 setValue : function(v)
36612 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36618 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36620 this.inputEl().dom.value = (v == '') ? '' :
36621 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36623 if(!this.allowZero && v === '0') {
36624 this.hiddenEl().dom.value = '';
36625 this.inputEl().dom.value = '';
36632 decimalPrecisionFcn : function(v)
36634 return Math.floor(v);
36637 beforeBlur : function()
36639 var v = this.parseValue(this.getRawValue());
36641 if(v || v === 0 || v === ''){
36646 hiddenEl : function()
36648 return this.el.select('input.hidden-number-input',true).first();
36660 * @class Roo.bootstrap.DocumentSlider
36661 * @extends Roo.bootstrap.Component
36662 * Bootstrap DocumentSlider class
36665 * Create a new DocumentViewer
36666 * @param {Object} config The config object
36669 Roo.bootstrap.DocumentSlider = function(config){
36670 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36677 * Fire after initEvent
36678 * @param {Roo.bootstrap.DocumentSlider} this
36683 * Fire after update
36684 * @param {Roo.bootstrap.DocumentSlider} this
36690 * @param {Roo.bootstrap.DocumentSlider} this
36696 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36702 getAutoCreate : function()
36706 cls : 'roo-document-slider',
36710 cls : 'roo-document-slider-header',
36714 cls : 'roo-document-slider-header-title'
36720 cls : 'roo-document-slider-body',
36724 cls : 'roo-document-slider-prev',
36728 cls : 'fa fa-chevron-left'
36734 cls : 'roo-document-slider-thumb',
36738 cls : 'roo-document-slider-image'
36744 cls : 'roo-document-slider-next',
36748 cls : 'fa fa-chevron-right'
36760 initEvents : function()
36762 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36763 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36765 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36766 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36768 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36769 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36771 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36772 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36774 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36775 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36777 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36778 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36780 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36781 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36783 this.thumbEl.on('click', this.onClick, this);
36785 this.prevIndicator.on('click', this.prev, this);
36787 this.nextIndicator.on('click', this.next, this);
36791 initial : function()
36793 if(this.files.length){
36794 this.indicator = 1;
36798 this.fireEvent('initial', this);
36801 update : function()
36803 this.imageEl.attr('src', this.files[this.indicator - 1]);
36805 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36807 this.prevIndicator.show();
36809 if(this.indicator == 1){
36810 this.prevIndicator.hide();
36813 this.nextIndicator.show();
36815 if(this.indicator == this.files.length){
36816 this.nextIndicator.hide();
36819 this.thumbEl.scrollTo('top');
36821 this.fireEvent('update', this);
36824 onClick : function(e)
36826 e.preventDefault();
36828 this.fireEvent('click', this);
36833 e.preventDefault();
36835 this.indicator = Math.max(1, this.indicator - 1);
36842 e.preventDefault();
36844 this.indicator = Math.min(this.files.length, this.indicator + 1);
36858 * @class Roo.bootstrap.RadioSet
36859 * @extends Roo.bootstrap.Input
36860 * Bootstrap RadioSet class
36861 * @cfg {String} indicatorpos (left|right) default left
36862 * @cfg {Boolean} inline (true|false) inline the element (default true)
36863 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36865 * Create a new RadioSet
36866 * @param {Object} config The config object
36869 Roo.bootstrap.RadioSet = function(config){
36871 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36875 Roo.bootstrap.RadioSet.register(this);
36880 * Fires when the element is checked or unchecked.
36881 * @param {Roo.bootstrap.RadioSet} this This radio
36882 * @param {Roo.bootstrap.Radio} item The checked item
36887 * Fires when the element is click.
36888 * @param {Roo.bootstrap.RadioSet} this This radio set
36889 * @param {Roo.bootstrap.Radio} item The checked item
36890 * @param {Roo.EventObject} e The event object
36897 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36905 indicatorpos : 'left',
36907 getAutoCreate : function()
36911 cls : 'roo-radio-set-label',
36915 html : this.fieldLabel
36919 if (Roo.bootstrap.version == 3) {
36922 if(this.indicatorpos == 'left'){
36925 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36926 tooltip : 'This field is required'
36931 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36932 tooltip : 'This field is required'
36938 cls : 'roo-radio-set-items'
36941 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36943 if (align === 'left' && this.fieldLabel.length) {
36946 cls : "roo-radio-set-right",
36952 if(this.labelWidth > 12){
36953 label.style = "width: " + this.labelWidth + 'px';
36956 if(this.labelWidth < 13 && this.labelmd == 0){
36957 this.labelmd = this.labelWidth;
36960 if(this.labellg > 0){
36961 label.cls += ' col-lg-' + this.labellg;
36962 items.cls += ' col-lg-' + (12 - this.labellg);
36965 if(this.labelmd > 0){
36966 label.cls += ' col-md-' + this.labelmd;
36967 items.cls += ' col-md-' + (12 - this.labelmd);
36970 if(this.labelsm > 0){
36971 label.cls += ' col-sm-' + this.labelsm;
36972 items.cls += ' col-sm-' + (12 - this.labelsm);
36975 if(this.labelxs > 0){
36976 label.cls += ' col-xs-' + this.labelxs;
36977 items.cls += ' col-xs-' + (12 - this.labelxs);
36983 cls : 'roo-radio-set',
36987 cls : 'roo-radio-set-input',
36990 value : this.value ? this.value : ''
36997 if(this.weight.length){
36998 cfg.cls += ' roo-radio-' + this.weight;
37002 cfg.cls += ' roo-radio-set-inline';
37006 ['xs','sm','md','lg'].map(function(size){
37007 if (settings[size]) {
37008 cfg.cls += ' col-' + size + '-' + settings[size];
37016 initEvents : function()
37018 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37019 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37021 if(!this.fieldLabel.length){
37022 this.labelEl.hide();
37025 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37026 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37028 this.indicator = this.indicatorEl();
37030 if(this.indicator){
37031 this.indicator.addClass('invisible');
37034 this.originalValue = this.getValue();
37038 inputEl: function ()
37040 return this.el.select('.roo-radio-set-input', true).first();
37043 getChildContainer : function()
37045 return this.itemsEl;
37048 register : function(item)
37050 this.radioes.push(item);
37054 validate : function()
37056 if(this.getVisibilityEl().hasClass('hidden')){
37062 Roo.each(this.radioes, function(i){
37071 if(this.allowBlank) {
37075 if(this.disabled || valid){
37080 this.markInvalid();
37085 markValid : function()
37087 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37088 this.indicatorEl().removeClass('visible');
37089 this.indicatorEl().addClass('invisible');
37093 if (Roo.bootstrap.version == 3) {
37094 this.el.removeClass([this.invalidClass, this.validClass]);
37095 this.el.addClass(this.validClass);
37097 this.el.removeClass(['is-invalid','is-valid']);
37098 this.el.addClass(['is-valid']);
37100 this.fireEvent('valid', this);
37103 markInvalid : function(msg)
37105 if(this.allowBlank || this.disabled){
37109 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37110 this.indicatorEl().removeClass('invisible');
37111 this.indicatorEl().addClass('visible');
37113 if (Roo.bootstrap.version == 3) {
37114 this.el.removeClass([this.invalidClass, this.validClass]);
37115 this.el.addClass(this.invalidClass);
37117 this.el.removeClass(['is-invalid','is-valid']);
37118 this.el.addClass(['is-invalid']);
37121 this.fireEvent('invalid', this, msg);
37125 setValue : function(v, suppressEvent)
37127 if(this.value === v){
37134 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37137 Roo.each(this.radioes, function(i){
37139 i.el.removeClass('checked');
37142 Roo.each(this.radioes, function(i){
37144 if(i.value === v || i.value.toString() === v.toString()){
37146 i.el.addClass('checked');
37148 if(suppressEvent !== true){
37149 this.fireEvent('check', this, i);
37160 clearInvalid : function(){
37162 if(!this.el || this.preventMark){
37166 this.el.removeClass([this.invalidClass]);
37168 this.fireEvent('valid', this);
37173 Roo.apply(Roo.bootstrap.RadioSet, {
37177 register : function(set)
37179 this.groups[set.name] = set;
37182 get: function(name)
37184 if (typeof(this.groups[name]) == 'undefined') {
37188 return this.groups[name] ;
37194 * Ext JS Library 1.1.1
37195 * Copyright(c) 2006-2007, Ext JS, LLC.
37197 * Originally Released Under LGPL - original licence link has changed is not relivant.
37200 * <script type="text/javascript">
37205 * @class Roo.bootstrap.SplitBar
37206 * @extends Roo.util.Observable
37207 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37211 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37212 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37213 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37214 split.minSize = 100;
37215 split.maxSize = 600;
37216 split.animate = true;
37217 split.on('moved', splitterMoved);
37220 * Create a new SplitBar
37221 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37222 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37223 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37224 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37225 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37226 position of the SplitBar).
37228 Roo.bootstrap.SplitBar = function(cfg){
37233 // dragElement : elm
37234 // resizingElement: el,
37236 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37237 // placement : Roo.bootstrap.SplitBar.LEFT ,
37238 // existingProxy ???
37241 this.el = Roo.get(cfg.dragElement, true);
37242 this.el.dom.unselectable = "on";
37244 this.resizingEl = Roo.get(cfg.resizingElement, true);
37248 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37249 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37252 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37255 * The minimum size of the resizing element. (Defaults to 0)
37261 * The maximum size of the resizing element. (Defaults to 2000)
37264 this.maxSize = 2000;
37267 * Whether to animate the transition to the new size
37270 this.animate = false;
37273 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37276 this.useShim = false;
37281 if(!cfg.existingProxy){
37283 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37285 this.proxy = Roo.get(cfg.existingProxy).dom;
37288 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37291 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37294 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37297 this.dragSpecs = {};
37300 * @private The adapter to use to positon and resize elements
37302 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37303 this.adapter.init(this);
37305 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37307 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37308 this.el.addClass("roo-splitbar-h");
37311 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37312 this.el.addClass("roo-splitbar-v");
37318 * Fires when the splitter is moved (alias for {@link #event-moved})
37319 * @param {Roo.bootstrap.SplitBar} this
37320 * @param {Number} newSize the new width or height
37325 * Fires when the splitter is moved
37326 * @param {Roo.bootstrap.SplitBar} this
37327 * @param {Number} newSize the new width or height
37331 * @event beforeresize
37332 * Fires before the splitter is dragged
37333 * @param {Roo.bootstrap.SplitBar} this
37335 "beforeresize" : true,
37337 "beforeapply" : true
37340 Roo.util.Observable.call(this);
37343 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37344 onStartProxyDrag : function(x, y){
37345 this.fireEvent("beforeresize", this);
37347 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37349 o.enableDisplayMode("block");
37350 // all splitbars share the same overlay
37351 Roo.bootstrap.SplitBar.prototype.overlay = o;
37353 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37354 this.overlay.show();
37355 Roo.get(this.proxy).setDisplayed("block");
37356 var size = this.adapter.getElementSize(this);
37357 this.activeMinSize = this.getMinimumSize();;
37358 this.activeMaxSize = this.getMaximumSize();;
37359 var c1 = size - this.activeMinSize;
37360 var c2 = Math.max(this.activeMaxSize - size, 0);
37361 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37362 this.dd.resetConstraints();
37363 this.dd.setXConstraint(
37364 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37365 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37367 this.dd.setYConstraint(0, 0);
37369 this.dd.resetConstraints();
37370 this.dd.setXConstraint(0, 0);
37371 this.dd.setYConstraint(
37372 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37373 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37376 this.dragSpecs.startSize = size;
37377 this.dragSpecs.startPoint = [x, y];
37378 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37382 * @private Called after the drag operation by the DDProxy
37384 onEndProxyDrag : function(e){
37385 Roo.get(this.proxy).setDisplayed(false);
37386 var endPoint = Roo.lib.Event.getXY(e);
37388 this.overlay.hide();
37391 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37392 newSize = this.dragSpecs.startSize +
37393 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37394 endPoint[0] - this.dragSpecs.startPoint[0] :
37395 this.dragSpecs.startPoint[0] - endPoint[0]
37398 newSize = this.dragSpecs.startSize +
37399 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37400 endPoint[1] - this.dragSpecs.startPoint[1] :
37401 this.dragSpecs.startPoint[1] - endPoint[1]
37404 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37405 if(newSize != this.dragSpecs.startSize){
37406 if(this.fireEvent('beforeapply', this, newSize) !== false){
37407 this.adapter.setElementSize(this, newSize);
37408 this.fireEvent("moved", this, newSize);
37409 this.fireEvent("resize", this, newSize);
37415 * Get the adapter this SplitBar uses
37416 * @return The adapter object
37418 getAdapter : function(){
37419 return this.adapter;
37423 * Set the adapter this SplitBar uses
37424 * @param {Object} adapter A SplitBar adapter object
37426 setAdapter : function(adapter){
37427 this.adapter = adapter;
37428 this.adapter.init(this);
37432 * Gets the minimum size for the resizing element
37433 * @return {Number} The minimum size
37435 getMinimumSize : function(){
37436 return this.minSize;
37440 * Sets the minimum size for the resizing element
37441 * @param {Number} minSize The minimum size
37443 setMinimumSize : function(minSize){
37444 this.minSize = minSize;
37448 * Gets the maximum size for the resizing element
37449 * @return {Number} The maximum size
37451 getMaximumSize : function(){
37452 return this.maxSize;
37456 * Sets the maximum size for the resizing element
37457 * @param {Number} maxSize The maximum size
37459 setMaximumSize : function(maxSize){
37460 this.maxSize = maxSize;
37464 * Sets the initialize size for the resizing element
37465 * @param {Number} size The initial size
37467 setCurrentSize : function(size){
37468 var oldAnimate = this.animate;
37469 this.animate = false;
37470 this.adapter.setElementSize(this, size);
37471 this.animate = oldAnimate;
37475 * Destroy this splitbar.
37476 * @param {Boolean} removeEl True to remove the element
37478 destroy : function(removeEl){
37480 this.shim.remove();
37483 this.proxy.parentNode.removeChild(this.proxy);
37491 * @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.
37493 Roo.bootstrap.SplitBar.createProxy = function(dir){
37494 var proxy = new Roo.Element(document.createElement("div"));
37495 proxy.unselectable();
37496 var cls = 'roo-splitbar-proxy';
37497 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37498 document.body.appendChild(proxy.dom);
37503 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37504 * Default Adapter. It assumes the splitter and resizing element are not positioned
37505 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37507 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37510 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37511 // do nothing for now
37512 init : function(s){
37516 * Called before drag operations to get the current size of the resizing element.
37517 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37519 getElementSize : function(s){
37520 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37521 return s.resizingEl.getWidth();
37523 return s.resizingEl.getHeight();
37528 * Called after drag operations to set the size of the resizing element.
37529 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37530 * @param {Number} newSize The new size to set
37531 * @param {Function} onComplete A function to be invoked when resizing is complete
37533 setElementSize : function(s, newSize, onComplete){
37534 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37536 s.resizingEl.setWidth(newSize);
37538 onComplete(s, newSize);
37541 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37546 s.resizingEl.setHeight(newSize);
37548 onComplete(s, newSize);
37551 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37558 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37559 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37560 * Adapter that moves the splitter element to align with the resized sizing element.
37561 * Used with an absolute positioned SplitBar.
37562 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37563 * document.body, make sure you assign an id to the body element.
37565 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37566 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37567 this.container = Roo.get(container);
37570 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37571 init : function(s){
37572 this.basic.init(s);
37575 getElementSize : function(s){
37576 return this.basic.getElementSize(s);
37579 setElementSize : function(s, newSize, onComplete){
37580 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37583 moveSplitter : function(s){
37584 var yes = Roo.bootstrap.SplitBar;
37585 switch(s.placement){
37587 s.el.setX(s.resizingEl.getRight());
37590 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37593 s.el.setY(s.resizingEl.getBottom());
37596 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37603 * Orientation constant - Create a vertical SplitBar
37607 Roo.bootstrap.SplitBar.VERTICAL = 1;
37610 * Orientation constant - Create a horizontal SplitBar
37614 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37617 * Placement constant - The resizing element is to the left of the splitter element
37621 Roo.bootstrap.SplitBar.LEFT = 1;
37624 * Placement constant - The resizing element is to the right of the splitter element
37628 Roo.bootstrap.SplitBar.RIGHT = 2;
37631 * Placement constant - The resizing element is positioned above the splitter element
37635 Roo.bootstrap.SplitBar.TOP = 3;
37638 * Placement constant - The resizing element is positioned under splitter element
37642 Roo.bootstrap.SplitBar.BOTTOM = 4;
37643 Roo.namespace("Roo.bootstrap.layout");/*
37645 * Ext JS Library 1.1.1
37646 * Copyright(c) 2006-2007, Ext JS, LLC.
37648 * Originally Released Under LGPL - original licence link has changed is not relivant.
37651 * <script type="text/javascript">
37655 * @class Roo.bootstrap.layout.Manager
37656 * @extends Roo.bootstrap.Component
37657 * Base class for layout managers.
37659 Roo.bootstrap.layout.Manager = function(config)
37661 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37667 /** false to disable window resize monitoring @type Boolean */
37668 this.monitorWindowResize = true;
37673 * Fires when a layout is performed.
37674 * @param {Roo.LayoutManager} this
37678 * @event regionresized
37679 * Fires when the user resizes a region.
37680 * @param {Roo.LayoutRegion} region The resized region
37681 * @param {Number} newSize The new size (width for east/west, height for north/south)
37683 "regionresized" : true,
37685 * @event regioncollapsed
37686 * Fires when a region is collapsed.
37687 * @param {Roo.LayoutRegion} region The collapsed region
37689 "regioncollapsed" : true,
37691 * @event regionexpanded
37692 * Fires when a region is expanded.
37693 * @param {Roo.LayoutRegion} region The expanded region
37695 "regionexpanded" : true
37697 this.updating = false;
37700 this.el = Roo.get(config.el);
37706 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37711 monitorWindowResize : true,
37717 onRender : function(ct, position)
37720 this.el = Roo.get(ct);
37723 //this.fireEvent('render',this);
37727 initEvents: function()
37731 // ie scrollbar fix
37732 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37733 document.body.scroll = "no";
37734 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37735 this.el.position('relative');
37737 this.id = this.el.id;
37738 this.el.addClass("roo-layout-container");
37739 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37740 if(this.el.dom != document.body ) {
37741 this.el.on('resize', this.layout,this);
37742 this.el.on('show', this.layout,this);
37748 * Returns true if this layout is currently being updated
37749 * @return {Boolean}
37751 isUpdating : function(){
37752 return this.updating;
37756 * Suspend the LayoutManager from doing auto-layouts while
37757 * making multiple add or remove calls
37759 beginUpdate : function(){
37760 this.updating = true;
37764 * Restore auto-layouts and optionally disable the manager from performing a layout
37765 * @param {Boolean} noLayout true to disable a layout update
37767 endUpdate : function(noLayout){
37768 this.updating = false;
37774 layout: function(){
37778 onRegionResized : function(region, newSize){
37779 this.fireEvent("regionresized", region, newSize);
37783 onRegionCollapsed : function(region){
37784 this.fireEvent("regioncollapsed", region);
37787 onRegionExpanded : function(region){
37788 this.fireEvent("regionexpanded", region);
37792 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37793 * performs box-model adjustments.
37794 * @return {Object} The size as an object {width: (the width), height: (the height)}
37796 getViewSize : function()
37799 if(this.el.dom != document.body){
37800 size = this.el.getSize();
37802 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37804 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37805 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37810 * Returns the Element this layout is bound to.
37811 * @return {Roo.Element}
37813 getEl : function(){
37818 * Returns the specified region.
37819 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37820 * @return {Roo.LayoutRegion}
37822 getRegion : function(target){
37823 return this.regions[target.toLowerCase()];
37826 onWindowResize : function(){
37827 if(this.monitorWindowResize){
37834 * Ext JS Library 1.1.1
37835 * Copyright(c) 2006-2007, Ext JS, LLC.
37837 * Originally Released Under LGPL - original licence link has changed is not relivant.
37840 * <script type="text/javascript">
37843 * @class Roo.bootstrap.layout.Border
37844 * @extends Roo.bootstrap.layout.Manager
37845 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37846 * please see: examples/bootstrap/nested.html<br><br>
37848 <b>The container the layout is rendered into can be either the body element or any other element.
37849 If it is not the body element, the container needs to either be an absolute positioned element,
37850 or you will need to add "position:relative" to the css of the container. You will also need to specify
37851 the container size if it is not the body element.</b>
37854 * Create a new Border
37855 * @param {Object} config Configuration options
37857 Roo.bootstrap.layout.Border = function(config){
37858 config = config || {};
37859 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37863 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37864 if(config[region]){
37865 config[region].region = region;
37866 this.addRegion(config[region]);
37872 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37874 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37876 parent : false, // this might point to a 'nest' or a ???
37879 * Creates and adds a new region if it doesn't already exist.
37880 * @param {String} target The target region key (north, south, east, west or center).
37881 * @param {Object} config The regions config object
37882 * @return {BorderLayoutRegion} The new region
37884 addRegion : function(config)
37886 if(!this.regions[config.region]){
37887 var r = this.factory(config);
37888 this.bindRegion(r);
37890 return this.regions[config.region];
37894 bindRegion : function(r){
37895 this.regions[r.config.region] = r;
37897 r.on("visibilitychange", this.layout, this);
37898 r.on("paneladded", this.layout, this);
37899 r.on("panelremoved", this.layout, this);
37900 r.on("invalidated", this.layout, this);
37901 r.on("resized", this.onRegionResized, this);
37902 r.on("collapsed", this.onRegionCollapsed, this);
37903 r.on("expanded", this.onRegionExpanded, this);
37907 * Performs a layout update.
37909 layout : function()
37911 if(this.updating) {
37915 // render all the rebions if they have not been done alreayd?
37916 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37917 if(this.regions[region] && !this.regions[region].bodyEl){
37918 this.regions[region].onRender(this.el)
37922 var size = this.getViewSize();
37923 var w = size.width;
37924 var h = size.height;
37929 //var x = 0, y = 0;
37931 var rs = this.regions;
37932 var north = rs["north"];
37933 var south = rs["south"];
37934 var west = rs["west"];
37935 var east = rs["east"];
37936 var center = rs["center"];
37937 //if(this.hideOnLayout){ // not supported anymore
37938 //c.el.setStyle("display", "none");
37940 if(north && north.isVisible()){
37941 var b = north.getBox();
37942 var m = north.getMargins();
37943 b.width = w - (m.left+m.right);
37946 centerY = b.height + b.y + m.bottom;
37947 centerH -= centerY;
37948 north.updateBox(this.safeBox(b));
37950 if(south && south.isVisible()){
37951 var b = south.getBox();
37952 var m = south.getMargins();
37953 b.width = w - (m.left+m.right);
37955 var totalHeight = (b.height + m.top + m.bottom);
37956 b.y = h - totalHeight + m.top;
37957 centerH -= totalHeight;
37958 south.updateBox(this.safeBox(b));
37960 if(west && west.isVisible()){
37961 var b = west.getBox();
37962 var m = west.getMargins();
37963 b.height = centerH - (m.top+m.bottom);
37965 b.y = centerY + m.top;
37966 var totalWidth = (b.width + m.left + m.right);
37967 centerX += totalWidth;
37968 centerW -= totalWidth;
37969 west.updateBox(this.safeBox(b));
37971 if(east && east.isVisible()){
37972 var b = east.getBox();
37973 var m = east.getMargins();
37974 b.height = centerH - (m.top+m.bottom);
37975 var totalWidth = (b.width + m.left + m.right);
37976 b.x = w - totalWidth + m.left;
37977 b.y = centerY + m.top;
37978 centerW -= totalWidth;
37979 east.updateBox(this.safeBox(b));
37982 var m = center.getMargins();
37984 x: centerX + m.left,
37985 y: centerY + m.top,
37986 width: centerW - (m.left+m.right),
37987 height: centerH - (m.top+m.bottom)
37989 //if(this.hideOnLayout){
37990 //center.el.setStyle("display", "block");
37992 center.updateBox(this.safeBox(centerBox));
37995 this.fireEvent("layout", this);
37999 safeBox : function(box){
38000 box.width = Math.max(0, box.width);
38001 box.height = Math.max(0, box.height);
38006 * Adds a ContentPanel (or subclass) to this layout.
38007 * @param {String} target The target region key (north, south, east, west or center).
38008 * @param {Roo.ContentPanel} panel The panel to add
38009 * @return {Roo.ContentPanel} The added panel
38011 add : function(target, panel){
38013 target = target.toLowerCase();
38014 return this.regions[target].add(panel);
38018 * Remove a ContentPanel (or subclass) to this layout.
38019 * @param {String} target The target region key (north, south, east, west or center).
38020 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38021 * @return {Roo.ContentPanel} The removed panel
38023 remove : function(target, panel){
38024 target = target.toLowerCase();
38025 return this.regions[target].remove(panel);
38029 * Searches all regions for a panel with the specified id
38030 * @param {String} panelId
38031 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38033 findPanel : function(panelId){
38034 var rs = this.regions;
38035 for(var target in rs){
38036 if(typeof rs[target] != "function"){
38037 var p = rs[target].getPanel(panelId);
38047 * Searches all regions for a panel with the specified id and activates (shows) it.
38048 * @param {String/ContentPanel} panelId The panels id or the panel itself
38049 * @return {Roo.ContentPanel} The shown panel or null
38051 showPanel : function(panelId) {
38052 var rs = this.regions;
38053 for(var target in rs){
38054 var r = rs[target];
38055 if(typeof r != "function"){
38056 if(r.hasPanel(panelId)){
38057 return r.showPanel(panelId);
38065 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38066 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38069 restoreState : function(provider){
38071 provider = Roo.state.Manager;
38073 var sm = new Roo.LayoutStateManager();
38074 sm.init(this, provider);
38080 * Adds a xtype elements to the layout.
38084 xtype : 'ContentPanel',
38091 xtype : 'NestedLayoutPanel',
38097 items : [ ... list of content panels or nested layout panels.. ]
38101 * @param {Object} cfg Xtype definition of item to add.
38103 addxtype : function(cfg)
38105 // basically accepts a pannel...
38106 // can accept a layout region..!?!?
38107 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38110 // theory? children can only be panels??
38112 //if (!cfg.xtype.match(/Panel$/)) {
38117 if (typeof(cfg.region) == 'undefined') {
38118 Roo.log("Failed to add Panel, region was not set");
38122 var region = cfg.region;
38128 xitems = cfg.items;
38133 if ( region == 'center') {
38134 Roo.log("Center: " + cfg.title);
38140 case 'Content': // ContentPanel (el, cfg)
38141 case 'Scroll': // ContentPanel (el, cfg)
38143 cfg.autoCreate = cfg.autoCreate || true;
38144 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38146 // var el = this.el.createChild();
38147 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38150 this.add(region, ret);
38154 case 'TreePanel': // our new panel!
38155 cfg.el = this.el.createChild();
38156 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38157 this.add(region, ret);
38162 // create a new Layout (which is a Border Layout...
38164 var clayout = cfg.layout;
38165 clayout.el = this.el.createChild();
38166 clayout.items = clayout.items || [];
38170 // replace this exitems with the clayout ones..
38171 xitems = clayout.items;
38173 // force background off if it's in center...
38174 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38175 cfg.background = false;
38177 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38180 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38181 //console.log('adding nested layout panel ' + cfg.toSource());
38182 this.add(region, ret);
38183 nb = {}; /// find first...
38188 // needs grid and region
38190 //var el = this.getRegion(region).el.createChild();
38192 *var el = this.el.createChild();
38193 // create the grid first...
38194 cfg.grid.container = el;
38195 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38198 if (region == 'center' && this.active ) {
38199 cfg.background = false;
38202 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38204 this.add(region, ret);
38206 if (cfg.background) {
38207 // render grid on panel activation (if panel background)
38208 ret.on('activate', function(gp) {
38209 if (!gp.grid.rendered) {
38210 // gp.grid.render(el);
38214 // cfg.grid.render(el);
38220 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38221 // it was the old xcomponent building that caused this before.
38222 // espeically if border is the top element in the tree.
38232 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38234 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38235 this.add(region, ret);
38239 throw "Can not add '" + cfg.xtype + "' to Border";
38245 this.beginUpdate();
38249 Roo.each(xitems, function(i) {
38250 region = nb && i.region ? i.region : false;
38252 var add = ret.addxtype(i);
38255 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38256 if (!i.background) {
38257 abn[region] = nb[region] ;
38264 // make the last non-background panel active..
38265 //if (nb) { Roo.log(abn); }
38268 for(var r in abn) {
38269 region = this.getRegion(r);
38271 // tried using nb[r], but it does not work..
38273 region.showPanel(abn[r]);
38284 factory : function(cfg)
38287 var validRegions = Roo.bootstrap.layout.Border.regions;
38289 var target = cfg.region;
38292 var r = Roo.bootstrap.layout;
38296 return new r.North(cfg);
38298 return new r.South(cfg);
38300 return new r.East(cfg);
38302 return new r.West(cfg);
38304 return new r.Center(cfg);
38306 throw 'Layout region "'+target+'" not supported.';
38313 * Ext JS Library 1.1.1
38314 * Copyright(c) 2006-2007, Ext JS, LLC.
38316 * Originally Released Under LGPL - original licence link has changed is not relivant.
38319 * <script type="text/javascript">
38323 * @class Roo.bootstrap.layout.Basic
38324 * @extends Roo.util.Observable
38325 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38326 * and does not have a titlebar, tabs or any other features. All it does is size and position
38327 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38328 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38329 * @cfg {string} region the region that it inhabits..
38330 * @cfg {bool} skipConfig skip config?
38334 Roo.bootstrap.layout.Basic = function(config){
38336 this.mgr = config.mgr;
38338 this.position = config.region;
38340 var skipConfig = config.skipConfig;
38344 * @scope Roo.BasicLayoutRegion
38348 * @event beforeremove
38349 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38350 * @param {Roo.LayoutRegion} this
38351 * @param {Roo.ContentPanel} panel The panel
38352 * @param {Object} e The cancel event object
38354 "beforeremove" : true,
38356 * @event invalidated
38357 * Fires when the layout for this region is changed.
38358 * @param {Roo.LayoutRegion} this
38360 "invalidated" : true,
38362 * @event visibilitychange
38363 * Fires when this region is shown or hidden
38364 * @param {Roo.LayoutRegion} this
38365 * @param {Boolean} visibility true or false
38367 "visibilitychange" : true,
38369 * @event paneladded
38370 * Fires when a panel is added.
38371 * @param {Roo.LayoutRegion} this
38372 * @param {Roo.ContentPanel} panel The panel
38374 "paneladded" : true,
38376 * @event panelremoved
38377 * Fires when a panel is removed.
38378 * @param {Roo.LayoutRegion} this
38379 * @param {Roo.ContentPanel} panel The panel
38381 "panelremoved" : true,
38383 * @event beforecollapse
38384 * Fires when this region before collapse.
38385 * @param {Roo.LayoutRegion} this
38387 "beforecollapse" : true,
38390 * Fires when this region is collapsed.
38391 * @param {Roo.LayoutRegion} this
38393 "collapsed" : true,
38396 * Fires when this region is expanded.
38397 * @param {Roo.LayoutRegion} this
38402 * Fires when this region is slid into view.
38403 * @param {Roo.LayoutRegion} this
38405 "slideshow" : true,
38408 * Fires when this region slides out of view.
38409 * @param {Roo.LayoutRegion} this
38411 "slidehide" : true,
38413 * @event panelactivated
38414 * Fires when a panel is activated.
38415 * @param {Roo.LayoutRegion} this
38416 * @param {Roo.ContentPanel} panel The activated panel
38418 "panelactivated" : true,
38421 * Fires when the user resizes this region.
38422 * @param {Roo.LayoutRegion} this
38423 * @param {Number} newSize The new size (width for east/west, height for north/south)
38427 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38428 this.panels = new Roo.util.MixedCollection();
38429 this.panels.getKey = this.getPanelId.createDelegate(this);
38431 this.activePanel = null;
38432 // ensure listeners are added...
38434 if (config.listeners || config.events) {
38435 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38436 listeners : config.listeners || {},
38437 events : config.events || {}
38441 if(skipConfig !== true){
38442 this.applyConfig(config);
38446 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38448 getPanelId : function(p){
38452 applyConfig : function(config){
38453 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38454 this.config = config;
38459 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38460 * the width, for horizontal (north, south) the height.
38461 * @param {Number} newSize The new width or height
38463 resizeTo : function(newSize){
38464 var el = this.el ? this.el :
38465 (this.activePanel ? this.activePanel.getEl() : null);
38467 switch(this.position){
38470 el.setWidth(newSize);
38471 this.fireEvent("resized", this, newSize);
38475 el.setHeight(newSize);
38476 this.fireEvent("resized", this, newSize);
38482 getBox : function(){
38483 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38486 getMargins : function(){
38487 return this.margins;
38490 updateBox : function(box){
38492 var el = this.activePanel.getEl();
38493 el.dom.style.left = box.x + "px";
38494 el.dom.style.top = box.y + "px";
38495 this.activePanel.setSize(box.width, box.height);
38499 * Returns the container element for this region.
38500 * @return {Roo.Element}
38502 getEl : function(){
38503 return this.activePanel;
38507 * Returns true if this region is currently visible.
38508 * @return {Boolean}
38510 isVisible : function(){
38511 return this.activePanel ? true : false;
38514 setActivePanel : function(panel){
38515 panel = this.getPanel(panel);
38516 if(this.activePanel && this.activePanel != panel){
38517 this.activePanel.setActiveState(false);
38518 this.activePanel.getEl().setLeftTop(-10000,-10000);
38520 this.activePanel = panel;
38521 panel.setActiveState(true);
38523 panel.setSize(this.box.width, this.box.height);
38525 this.fireEvent("panelactivated", this, panel);
38526 this.fireEvent("invalidated");
38530 * Show the specified panel.
38531 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38532 * @return {Roo.ContentPanel} The shown panel or null
38534 showPanel : function(panel){
38535 panel = this.getPanel(panel);
38537 this.setActivePanel(panel);
38543 * Get the active panel for this region.
38544 * @return {Roo.ContentPanel} The active panel or null
38546 getActivePanel : function(){
38547 return this.activePanel;
38551 * Add the passed ContentPanel(s)
38552 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38553 * @return {Roo.ContentPanel} The panel added (if only one was added)
38555 add : function(panel){
38556 if(arguments.length > 1){
38557 for(var i = 0, len = arguments.length; i < len; i++) {
38558 this.add(arguments[i]);
38562 if(this.hasPanel(panel)){
38563 this.showPanel(panel);
38566 var el = panel.getEl();
38567 if(el.dom.parentNode != this.mgr.el.dom){
38568 this.mgr.el.dom.appendChild(el.dom);
38570 if(panel.setRegion){
38571 panel.setRegion(this);
38573 this.panels.add(panel);
38574 el.setStyle("position", "absolute");
38575 if(!panel.background){
38576 this.setActivePanel(panel);
38577 if(this.config.initialSize && this.panels.getCount()==1){
38578 this.resizeTo(this.config.initialSize);
38581 this.fireEvent("paneladded", this, panel);
38586 * Returns true if the panel is in this region.
38587 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38588 * @return {Boolean}
38590 hasPanel : function(panel){
38591 if(typeof panel == "object"){ // must be panel obj
38592 panel = panel.getId();
38594 return this.getPanel(panel) ? true : false;
38598 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38599 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38600 * @param {Boolean} preservePanel Overrides the config preservePanel option
38601 * @return {Roo.ContentPanel} The panel that was removed
38603 remove : function(panel, preservePanel){
38604 panel = this.getPanel(panel);
38609 this.fireEvent("beforeremove", this, panel, e);
38610 if(e.cancel === true){
38613 var panelId = panel.getId();
38614 this.panels.removeKey(panelId);
38619 * Returns the panel specified or null if it's not in this region.
38620 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38621 * @return {Roo.ContentPanel}
38623 getPanel : function(id){
38624 if(typeof id == "object"){ // must be panel obj
38627 return this.panels.get(id);
38631 * Returns this regions position (north/south/east/west/center).
38634 getPosition: function(){
38635 return this.position;
38639 * Ext JS Library 1.1.1
38640 * Copyright(c) 2006-2007, Ext JS, LLC.
38642 * Originally Released Under LGPL - original licence link has changed is not relivant.
38645 * <script type="text/javascript">
38649 * @class Roo.bootstrap.layout.Region
38650 * @extends Roo.bootstrap.layout.Basic
38651 * This class represents a region in a layout manager.
38653 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38654 * @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})
38655 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38656 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38657 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38658 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38659 * @cfg {String} title The title for the region (overrides panel titles)
38660 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38661 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38662 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38663 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38664 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38665 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38666 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38667 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38668 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38669 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38671 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38672 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38673 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38674 * @cfg {Number} width For East/West panels
38675 * @cfg {Number} height For North/South panels
38676 * @cfg {Boolean} split To show the splitter
38677 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38679 * @cfg {string} cls Extra CSS classes to add to region
38681 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38682 * @cfg {string} region the region that it inhabits..
38685 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38686 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38688 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38689 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38690 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38692 Roo.bootstrap.layout.Region = function(config)
38694 this.applyConfig(config);
38696 var mgr = config.mgr;
38697 var pos = config.region;
38698 config.skipConfig = true;
38699 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38702 this.onRender(mgr.el);
38705 this.visible = true;
38706 this.collapsed = false;
38707 this.unrendered_panels = [];
38710 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38712 position: '', // set by wrapper (eg. north/south etc..)
38713 unrendered_panels : null, // unrendered panels.
38715 tabPosition : false,
38717 mgr: false, // points to 'Border'
38720 createBody : function(){
38721 /** This region's body element
38722 * @type Roo.Element */
38723 this.bodyEl = this.el.createChild({
38725 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38729 onRender: function(ctr, pos)
38731 var dh = Roo.DomHelper;
38732 /** This region's container element
38733 * @type Roo.Element */
38734 this.el = dh.append(ctr.dom, {
38736 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38738 /** This region's title element
38739 * @type Roo.Element */
38741 this.titleEl = dh.append(this.el.dom, {
38743 unselectable: "on",
38744 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38746 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38747 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38751 this.titleEl.enableDisplayMode();
38752 /** This region's title text element
38753 * @type HTMLElement */
38754 this.titleTextEl = this.titleEl.dom.firstChild;
38755 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38757 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38758 this.closeBtn.enableDisplayMode();
38759 this.closeBtn.on("click", this.closeClicked, this);
38760 this.closeBtn.hide();
38762 this.createBody(this.config);
38763 if(this.config.hideWhenEmpty){
38765 this.on("paneladded", this.validateVisibility, this);
38766 this.on("panelremoved", this.validateVisibility, this);
38768 if(this.autoScroll){
38769 this.bodyEl.setStyle("overflow", "auto");
38771 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38773 //if(c.titlebar !== false){
38774 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38775 this.titleEl.hide();
38777 this.titleEl.show();
38778 if(this.config.title){
38779 this.titleTextEl.innerHTML = this.config.title;
38783 if(this.config.collapsed){
38784 this.collapse(true);
38786 if(this.config.hidden){
38790 if (this.unrendered_panels && this.unrendered_panels.length) {
38791 for (var i =0;i< this.unrendered_panels.length; i++) {
38792 this.add(this.unrendered_panels[i]);
38794 this.unrendered_panels = null;
38800 applyConfig : function(c)
38803 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38804 var dh = Roo.DomHelper;
38805 if(c.titlebar !== false){
38806 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38807 this.collapseBtn.on("click", this.collapse, this);
38808 this.collapseBtn.enableDisplayMode();
38810 if(c.showPin === true || this.showPin){
38811 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38812 this.stickBtn.enableDisplayMode();
38813 this.stickBtn.on("click", this.expand, this);
38814 this.stickBtn.hide();
38819 /** This region's collapsed element
38820 * @type Roo.Element */
38823 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38824 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38827 if(c.floatable !== false){
38828 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38829 this.collapsedEl.on("click", this.collapseClick, this);
38832 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38833 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38834 id: "message", unselectable: "on", style:{"float":"left"}});
38835 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38837 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38838 this.expandBtn.on("click", this.expand, this);
38842 if(this.collapseBtn){
38843 this.collapseBtn.setVisible(c.collapsible == true);
38846 this.cmargins = c.cmargins || this.cmargins ||
38847 (this.position == "west" || this.position == "east" ?
38848 {top: 0, left: 2, right:2, bottom: 0} :
38849 {top: 2, left: 0, right:0, bottom: 2});
38851 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38854 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38856 this.autoScroll = c.autoScroll || false;
38861 this.duration = c.duration || .30;
38862 this.slideDuration = c.slideDuration || .45;
38867 * Returns true if this region is currently visible.
38868 * @return {Boolean}
38870 isVisible : function(){
38871 return this.visible;
38875 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38876 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38878 //setCollapsedTitle : function(title){
38879 // title = title || " ";
38880 // if(this.collapsedTitleTextEl){
38881 // this.collapsedTitleTextEl.innerHTML = title;
38885 getBox : function(){
38887 // if(!this.collapsed){
38888 b = this.el.getBox(false, true);
38890 // b = this.collapsedEl.getBox(false, true);
38895 getMargins : function(){
38896 return this.margins;
38897 //return this.collapsed ? this.cmargins : this.margins;
38900 highlight : function(){
38901 this.el.addClass("x-layout-panel-dragover");
38904 unhighlight : function(){
38905 this.el.removeClass("x-layout-panel-dragover");
38908 updateBox : function(box)
38910 if (!this.bodyEl) {
38911 return; // not rendered yet..
38915 if(!this.collapsed){
38916 this.el.dom.style.left = box.x + "px";
38917 this.el.dom.style.top = box.y + "px";
38918 this.updateBody(box.width, box.height);
38920 this.collapsedEl.dom.style.left = box.x + "px";
38921 this.collapsedEl.dom.style.top = box.y + "px";
38922 this.collapsedEl.setSize(box.width, box.height);
38925 this.tabs.autoSizeTabs();
38929 updateBody : function(w, h)
38932 this.el.setWidth(w);
38933 w -= this.el.getBorderWidth("rl");
38934 if(this.config.adjustments){
38935 w += this.config.adjustments[0];
38938 if(h !== null && h > 0){
38939 this.el.setHeight(h);
38940 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38941 h -= this.el.getBorderWidth("tb");
38942 if(this.config.adjustments){
38943 h += this.config.adjustments[1];
38945 this.bodyEl.setHeight(h);
38947 h = this.tabs.syncHeight(h);
38950 if(this.panelSize){
38951 w = w !== null ? w : this.panelSize.width;
38952 h = h !== null ? h : this.panelSize.height;
38954 if(this.activePanel){
38955 var el = this.activePanel.getEl();
38956 w = w !== null ? w : el.getWidth();
38957 h = h !== null ? h : el.getHeight();
38958 this.panelSize = {width: w, height: h};
38959 this.activePanel.setSize(w, h);
38961 if(Roo.isIE && this.tabs){
38962 this.tabs.el.repaint();
38967 * Returns the container element for this region.
38968 * @return {Roo.Element}
38970 getEl : function(){
38975 * Hides this region.
38978 //if(!this.collapsed){
38979 this.el.dom.style.left = "-2000px";
38982 // this.collapsedEl.dom.style.left = "-2000px";
38983 // this.collapsedEl.hide();
38985 this.visible = false;
38986 this.fireEvent("visibilitychange", this, false);
38990 * Shows this region if it was previously hidden.
38993 //if(!this.collapsed){
38996 // this.collapsedEl.show();
38998 this.visible = true;
38999 this.fireEvent("visibilitychange", this, true);
39002 closeClicked : function(){
39003 if(this.activePanel){
39004 this.remove(this.activePanel);
39008 collapseClick : function(e){
39010 e.stopPropagation();
39013 e.stopPropagation();
39019 * Collapses this region.
39020 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39023 collapse : function(skipAnim, skipCheck = false){
39024 if(this.collapsed) {
39028 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39030 this.collapsed = true;
39032 this.split.el.hide();
39034 if(this.config.animate && skipAnim !== true){
39035 this.fireEvent("invalidated", this);
39036 this.animateCollapse();
39038 this.el.setLocation(-20000,-20000);
39040 this.collapsedEl.show();
39041 this.fireEvent("collapsed", this);
39042 this.fireEvent("invalidated", this);
39048 animateCollapse : function(){
39053 * Expands this region if it was previously collapsed.
39054 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39055 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39058 expand : function(e, skipAnim){
39060 e.stopPropagation();
39062 if(!this.collapsed || this.el.hasActiveFx()) {
39066 this.afterSlideIn();
39069 this.collapsed = false;
39070 if(this.config.animate && skipAnim !== true){
39071 this.animateExpand();
39075 this.split.el.show();
39077 this.collapsedEl.setLocation(-2000,-2000);
39078 this.collapsedEl.hide();
39079 this.fireEvent("invalidated", this);
39080 this.fireEvent("expanded", this);
39084 animateExpand : function(){
39088 initTabs : function()
39090 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39092 var ts = new Roo.bootstrap.panel.Tabs({
39093 el: this.bodyEl.dom,
39095 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39096 disableTooltips: this.config.disableTabTips,
39097 toolbar : this.config.toolbar
39100 if(this.config.hideTabs){
39101 ts.stripWrap.setDisplayed(false);
39104 ts.resizeTabs = this.config.resizeTabs === true;
39105 ts.minTabWidth = this.config.minTabWidth || 40;
39106 ts.maxTabWidth = this.config.maxTabWidth || 250;
39107 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39108 ts.monitorResize = false;
39109 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39110 ts.bodyEl.addClass('roo-layout-tabs-body');
39111 this.panels.each(this.initPanelAsTab, this);
39114 initPanelAsTab : function(panel){
39115 var ti = this.tabs.addTab(
39119 this.config.closeOnTab && panel.isClosable(),
39122 if(panel.tabTip !== undefined){
39123 ti.setTooltip(panel.tabTip);
39125 ti.on("activate", function(){
39126 this.setActivePanel(panel);
39129 if(this.config.closeOnTab){
39130 ti.on("beforeclose", function(t, e){
39132 this.remove(panel);
39136 panel.tabItem = ti;
39141 updatePanelTitle : function(panel, title)
39143 if(this.activePanel == panel){
39144 this.updateTitle(title);
39147 var ti = this.tabs.getTab(panel.getEl().id);
39149 if(panel.tabTip !== undefined){
39150 ti.setTooltip(panel.tabTip);
39155 updateTitle : function(title){
39156 if(this.titleTextEl && !this.config.title){
39157 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39161 setActivePanel : function(panel)
39163 panel = this.getPanel(panel);
39164 if(this.activePanel && this.activePanel != panel){
39165 if(this.activePanel.setActiveState(false) === false){
39169 this.activePanel = panel;
39170 panel.setActiveState(true);
39171 if(this.panelSize){
39172 panel.setSize(this.panelSize.width, this.panelSize.height);
39175 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39177 this.updateTitle(panel.getTitle());
39179 this.fireEvent("invalidated", this);
39181 this.fireEvent("panelactivated", this, panel);
39185 * Shows the specified panel.
39186 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39187 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39189 showPanel : function(panel)
39191 panel = this.getPanel(panel);
39194 var tab = this.tabs.getTab(panel.getEl().id);
39195 if(tab.isHidden()){
39196 this.tabs.unhideTab(tab.id);
39200 this.setActivePanel(panel);
39207 * Get the active panel for this region.
39208 * @return {Roo.ContentPanel} The active panel or null
39210 getActivePanel : function(){
39211 return this.activePanel;
39214 validateVisibility : function(){
39215 if(this.panels.getCount() < 1){
39216 this.updateTitle(" ");
39217 this.closeBtn.hide();
39220 if(!this.isVisible()){
39227 * Adds the passed ContentPanel(s) to this region.
39228 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39229 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39231 add : function(panel)
39233 if(arguments.length > 1){
39234 for(var i = 0, len = arguments.length; i < len; i++) {
39235 this.add(arguments[i]);
39240 // if we have not been rendered yet, then we can not really do much of this..
39241 if (!this.bodyEl) {
39242 this.unrendered_panels.push(panel);
39249 if(this.hasPanel(panel)){
39250 this.showPanel(panel);
39253 panel.setRegion(this);
39254 this.panels.add(panel);
39255 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39256 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39257 // and hide them... ???
39258 this.bodyEl.dom.appendChild(panel.getEl().dom);
39259 if(panel.background !== true){
39260 this.setActivePanel(panel);
39262 this.fireEvent("paneladded", this, panel);
39269 this.initPanelAsTab(panel);
39273 if(panel.background !== true){
39274 this.tabs.activate(panel.getEl().id);
39276 this.fireEvent("paneladded", this, panel);
39281 * Hides the tab for the specified panel.
39282 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39284 hidePanel : function(panel){
39285 if(this.tabs && (panel = this.getPanel(panel))){
39286 this.tabs.hideTab(panel.getEl().id);
39291 * Unhides the tab for a previously hidden panel.
39292 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39294 unhidePanel : function(panel){
39295 if(this.tabs && (panel = this.getPanel(panel))){
39296 this.tabs.unhideTab(panel.getEl().id);
39300 clearPanels : function(){
39301 while(this.panels.getCount() > 0){
39302 this.remove(this.panels.first());
39307 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39308 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39309 * @param {Boolean} preservePanel Overrides the config preservePanel option
39310 * @return {Roo.ContentPanel} The panel that was removed
39312 remove : function(panel, preservePanel)
39314 panel = this.getPanel(panel);
39319 this.fireEvent("beforeremove", this, panel, e);
39320 if(e.cancel === true){
39323 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39324 var panelId = panel.getId();
39325 this.panels.removeKey(panelId);
39327 document.body.appendChild(panel.getEl().dom);
39330 this.tabs.removeTab(panel.getEl().id);
39331 }else if (!preservePanel){
39332 this.bodyEl.dom.removeChild(panel.getEl().dom);
39334 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39335 var p = this.panels.first();
39336 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39337 tempEl.appendChild(p.getEl().dom);
39338 this.bodyEl.update("");
39339 this.bodyEl.dom.appendChild(p.getEl().dom);
39341 this.updateTitle(p.getTitle());
39343 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39344 this.setActivePanel(p);
39346 panel.setRegion(null);
39347 if(this.activePanel == panel){
39348 this.activePanel = null;
39350 if(this.config.autoDestroy !== false && preservePanel !== true){
39351 try{panel.destroy();}catch(e){}
39353 this.fireEvent("panelremoved", this, panel);
39358 * Returns the TabPanel component used by this region
39359 * @return {Roo.TabPanel}
39361 getTabs : function(){
39365 createTool : function(parentEl, className){
39366 var btn = Roo.DomHelper.append(parentEl, {
39368 cls: "x-layout-tools-button",
39371 cls: "roo-layout-tools-button-inner " + className,
39375 btn.addClassOnOver("roo-layout-tools-button-over");
39380 * Ext JS Library 1.1.1
39381 * Copyright(c) 2006-2007, Ext JS, LLC.
39383 * Originally Released Under LGPL - original licence link has changed is not relivant.
39386 * <script type="text/javascript">
39392 * @class Roo.SplitLayoutRegion
39393 * @extends Roo.LayoutRegion
39394 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39396 Roo.bootstrap.layout.Split = function(config){
39397 this.cursor = config.cursor;
39398 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39401 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39403 splitTip : "Drag to resize.",
39404 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39405 useSplitTips : false,
39407 applyConfig : function(config){
39408 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39411 onRender : function(ctr,pos) {
39413 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39414 if(!this.config.split){
39419 var splitEl = Roo.DomHelper.append(ctr.dom, {
39421 id: this.el.id + "-split",
39422 cls: "roo-layout-split roo-layout-split-"+this.position,
39425 /** The SplitBar for this region
39426 * @type Roo.SplitBar */
39427 // does not exist yet...
39428 Roo.log([this.position, this.orientation]);
39430 this.split = new Roo.bootstrap.SplitBar({
39431 dragElement : splitEl,
39432 resizingElement: this.el,
39433 orientation : this.orientation
39436 this.split.on("moved", this.onSplitMove, this);
39437 this.split.useShim = this.config.useShim === true;
39438 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39439 if(this.useSplitTips){
39440 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39442 //if(config.collapsible){
39443 // this.split.el.on("dblclick", this.collapse, this);
39446 if(typeof this.config.minSize != "undefined"){
39447 this.split.minSize = this.config.minSize;
39449 if(typeof this.config.maxSize != "undefined"){
39450 this.split.maxSize = this.config.maxSize;
39452 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39453 this.hideSplitter();
39458 getHMaxSize : function(){
39459 var cmax = this.config.maxSize || 10000;
39460 var center = this.mgr.getRegion("center");
39461 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39464 getVMaxSize : function(){
39465 var cmax = this.config.maxSize || 10000;
39466 var center = this.mgr.getRegion("center");
39467 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39470 onSplitMove : function(split, newSize){
39471 this.fireEvent("resized", this, newSize);
39475 * Returns the {@link Roo.SplitBar} for this region.
39476 * @return {Roo.SplitBar}
39478 getSplitBar : function(){
39483 this.hideSplitter();
39484 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39487 hideSplitter : function(){
39489 this.split.el.setLocation(-2000,-2000);
39490 this.split.el.hide();
39496 this.split.el.show();
39498 Roo.bootstrap.layout.Split.superclass.show.call(this);
39501 beforeSlide: function(){
39502 if(Roo.isGecko){// firefox overflow auto bug workaround
39503 this.bodyEl.clip();
39505 this.tabs.bodyEl.clip();
39507 if(this.activePanel){
39508 this.activePanel.getEl().clip();
39510 if(this.activePanel.beforeSlide){
39511 this.activePanel.beforeSlide();
39517 afterSlide : function(){
39518 if(Roo.isGecko){// firefox overflow auto bug workaround
39519 this.bodyEl.unclip();
39521 this.tabs.bodyEl.unclip();
39523 if(this.activePanel){
39524 this.activePanel.getEl().unclip();
39525 if(this.activePanel.afterSlide){
39526 this.activePanel.afterSlide();
39532 initAutoHide : function(){
39533 if(this.autoHide !== false){
39534 if(!this.autoHideHd){
39535 var st = new Roo.util.DelayedTask(this.slideIn, this);
39536 this.autoHideHd = {
39537 "mouseout": function(e){
39538 if(!e.within(this.el, true)){
39542 "mouseover" : function(e){
39548 this.el.on(this.autoHideHd);
39552 clearAutoHide : function(){
39553 if(this.autoHide !== false){
39554 this.el.un("mouseout", this.autoHideHd.mouseout);
39555 this.el.un("mouseover", this.autoHideHd.mouseover);
39559 clearMonitor : function(){
39560 Roo.get(document).un("click", this.slideInIf, this);
39563 // these names are backwards but not changed for compat
39564 slideOut : function(){
39565 if(this.isSlid || this.el.hasActiveFx()){
39568 this.isSlid = true;
39569 if(this.collapseBtn){
39570 this.collapseBtn.hide();
39572 this.closeBtnState = this.closeBtn.getStyle('display');
39573 this.closeBtn.hide();
39575 this.stickBtn.show();
39578 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39579 this.beforeSlide();
39580 this.el.setStyle("z-index", 10001);
39581 this.el.slideIn(this.getSlideAnchor(), {
39582 callback: function(){
39584 this.initAutoHide();
39585 Roo.get(document).on("click", this.slideInIf, this);
39586 this.fireEvent("slideshow", this);
39593 afterSlideIn : function(){
39594 this.clearAutoHide();
39595 this.isSlid = false;
39596 this.clearMonitor();
39597 this.el.setStyle("z-index", "");
39598 if(this.collapseBtn){
39599 this.collapseBtn.show();
39601 this.closeBtn.setStyle('display', this.closeBtnState);
39603 this.stickBtn.hide();
39605 this.fireEvent("slidehide", this);
39608 slideIn : function(cb){
39609 if(!this.isSlid || this.el.hasActiveFx()){
39613 this.isSlid = false;
39614 this.beforeSlide();
39615 this.el.slideOut(this.getSlideAnchor(), {
39616 callback: function(){
39617 this.el.setLeftTop(-10000, -10000);
39619 this.afterSlideIn();
39627 slideInIf : function(e){
39628 if(!e.within(this.el)){
39633 animateCollapse : function(){
39634 this.beforeSlide();
39635 this.el.setStyle("z-index", 20000);
39636 var anchor = this.getSlideAnchor();
39637 this.el.slideOut(anchor, {
39638 callback : function(){
39639 this.el.setStyle("z-index", "");
39640 this.collapsedEl.slideIn(anchor, {duration:.3});
39642 this.el.setLocation(-10000,-10000);
39644 this.fireEvent("collapsed", this);
39651 animateExpand : function(){
39652 this.beforeSlide();
39653 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39654 this.el.setStyle("z-index", 20000);
39655 this.collapsedEl.hide({
39658 this.el.slideIn(this.getSlideAnchor(), {
39659 callback : function(){
39660 this.el.setStyle("z-index", "");
39663 this.split.el.show();
39665 this.fireEvent("invalidated", this);
39666 this.fireEvent("expanded", this);
39694 getAnchor : function(){
39695 return this.anchors[this.position];
39698 getCollapseAnchor : function(){
39699 return this.canchors[this.position];
39702 getSlideAnchor : function(){
39703 return this.sanchors[this.position];
39706 getAlignAdj : function(){
39707 var cm = this.cmargins;
39708 switch(this.position){
39724 getExpandAdj : function(){
39725 var c = this.collapsedEl, cm = this.cmargins;
39726 switch(this.position){
39728 return [-(cm.right+c.getWidth()+cm.left), 0];
39731 return [cm.right+c.getWidth()+cm.left, 0];
39734 return [0, -(cm.top+cm.bottom+c.getHeight())];
39737 return [0, cm.top+cm.bottom+c.getHeight()];
39743 * Ext JS Library 1.1.1
39744 * Copyright(c) 2006-2007, Ext JS, LLC.
39746 * Originally Released Under LGPL - original licence link has changed is not relivant.
39749 * <script type="text/javascript">
39752 * These classes are private internal classes
39754 Roo.bootstrap.layout.Center = function(config){
39755 config.region = "center";
39756 Roo.bootstrap.layout.Region.call(this, config);
39757 this.visible = true;
39758 this.minWidth = config.minWidth || 20;
39759 this.minHeight = config.minHeight || 20;
39762 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39764 // center panel can't be hidden
39768 // center panel can't be hidden
39771 getMinWidth: function(){
39772 return this.minWidth;
39775 getMinHeight: function(){
39776 return this.minHeight;
39790 Roo.bootstrap.layout.North = function(config)
39792 config.region = 'north';
39793 config.cursor = 'n-resize';
39795 Roo.bootstrap.layout.Split.call(this, config);
39799 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39800 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39801 this.split.el.addClass("roo-layout-split-v");
39803 //var size = config.initialSize || config.height;
39804 //if(this.el && typeof size != "undefined"){
39805 // this.el.setHeight(size);
39808 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39810 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39813 onRender : function(ctr, pos)
39815 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39816 var size = this.config.initialSize || this.config.height;
39817 if(this.el && typeof size != "undefined"){
39818 this.el.setHeight(size);
39823 getBox : function(){
39824 if(this.collapsed){
39825 return this.collapsedEl.getBox();
39827 var box = this.el.getBox();
39829 box.height += this.split.el.getHeight();
39834 updateBox : function(box){
39835 if(this.split && !this.collapsed){
39836 box.height -= this.split.el.getHeight();
39837 this.split.el.setLeft(box.x);
39838 this.split.el.setTop(box.y+box.height);
39839 this.split.el.setWidth(box.width);
39841 if(this.collapsed){
39842 this.updateBody(box.width, null);
39844 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39852 Roo.bootstrap.layout.South = function(config){
39853 config.region = 'south';
39854 config.cursor = 's-resize';
39855 Roo.bootstrap.layout.Split.call(this, config);
39857 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39858 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39859 this.split.el.addClass("roo-layout-split-v");
39864 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39865 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39867 onRender : function(ctr, pos)
39869 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39870 var size = this.config.initialSize || this.config.height;
39871 if(this.el && typeof size != "undefined"){
39872 this.el.setHeight(size);
39877 getBox : function(){
39878 if(this.collapsed){
39879 return this.collapsedEl.getBox();
39881 var box = this.el.getBox();
39883 var sh = this.split.el.getHeight();
39890 updateBox : function(box){
39891 if(this.split && !this.collapsed){
39892 var sh = this.split.el.getHeight();
39895 this.split.el.setLeft(box.x);
39896 this.split.el.setTop(box.y-sh);
39897 this.split.el.setWidth(box.width);
39899 if(this.collapsed){
39900 this.updateBody(box.width, null);
39902 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39906 Roo.bootstrap.layout.East = function(config){
39907 config.region = "east";
39908 config.cursor = "e-resize";
39909 Roo.bootstrap.layout.Split.call(this, config);
39911 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39912 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39913 this.split.el.addClass("roo-layout-split-h");
39917 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39918 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39920 onRender : function(ctr, pos)
39922 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39923 var size = this.config.initialSize || this.config.width;
39924 if(this.el && typeof size != "undefined"){
39925 this.el.setWidth(size);
39930 getBox : function(){
39931 if(this.collapsed){
39932 return this.collapsedEl.getBox();
39934 var box = this.el.getBox();
39936 var sw = this.split.el.getWidth();
39943 updateBox : function(box){
39944 if(this.split && !this.collapsed){
39945 var sw = this.split.el.getWidth();
39947 this.split.el.setLeft(box.x);
39948 this.split.el.setTop(box.y);
39949 this.split.el.setHeight(box.height);
39952 if(this.collapsed){
39953 this.updateBody(null, box.height);
39955 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39959 Roo.bootstrap.layout.West = function(config){
39960 config.region = "west";
39961 config.cursor = "w-resize";
39963 Roo.bootstrap.layout.Split.call(this, config);
39965 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39966 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39967 this.split.el.addClass("roo-layout-split-h");
39971 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39972 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39974 onRender: function(ctr, pos)
39976 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39977 var size = this.config.initialSize || this.config.width;
39978 if(typeof size != "undefined"){
39979 this.el.setWidth(size);
39983 getBox : function(){
39984 if(this.collapsed){
39985 return this.collapsedEl.getBox();
39987 var box = this.el.getBox();
39988 if (box.width == 0) {
39989 box.width = this.config.width; // kludge?
39992 box.width += this.split.el.getWidth();
39997 updateBox : function(box){
39998 if(this.split && !this.collapsed){
39999 var sw = this.split.el.getWidth();
40001 this.split.el.setLeft(box.x+box.width);
40002 this.split.el.setTop(box.y);
40003 this.split.el.setHeight(box.height);
40005 if(this.collapsed){
40006 this.updateBody(null, box.height);
40008 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40010 });Roo.namespace("Roo.bootstrap.panel");/*
40012 * Ext JS Library 1.1.1
40013 * Copyright(c) 2006-2007, Ext JS, LLC.
40015 * Originally Released Under LGPL - original licence link has changed is not relivant.
40018 * <script type="text/javascript">
40021 * @class Roo.ContentPanel
40022 * @extends Roo.util.Observable
40023 * A basic ContentPanel element.
40024 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40025 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40026 * @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
40027 * @cfg {Boolean} closable True if the panel can be closed/removed
40028 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40029 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40030 * @cfg {Toolbar} toolbar A toolbar for this panel
40031 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40032 * @cfg {String} title The title for this panel
40033 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40034 * @cfg {String} url Calls {@link #setUrl} with this value
40035 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40036 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40037 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40038 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40039 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40040 * @cfg {Boolean} badges render the badges
40041 * @cfg {String} cls extra classes to use
40042 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40045 * Create a new ContentPanel.
40046 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40047 * @param {String/Object} config A string to set only the title or a config object
40048 * @param {String} content (optional) Set the HTML content for this panel
40049 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40051 Roo.bootstrap.panel.Content = function( config){
40053 this.tpl = config.tpl || false;
40055 var el = config.el;
40056 var content = config.content;
40058 if(config.autoCreate){ // xtype is available if this is called from factory
40061 this.el = Roo.get(el);
40062 if(!this.el && config && config.autoCreate){
40063 if(typeof config.autoCreate == "object"){
40064 if(!config.autoCreate.id){
40065 config.autoCreate.id = config.id||el;
40067 this.el = Roo.DomHelper.append(document.body,
40068 config.autoCreate, true);
40072 cls: (config.cls || '') +
40073 (config.background ? ' bg-' + config.background : '') +
40074 " roo-layout-inactive-content",
40077 if (config.iframe) {
40081 style : 'border: 0px',
40082 src : 'about:blank'
40088 elcfg.html = config.html;
40092 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40093 if (config.iframe) {
40094 this.iframeEl = this.el.select('iframe',true).first();
40099 this.closable = false;
40100 this.loaded = false;
40101 this.active = false;
40104 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40106 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40108 this.wrapEl = this.el; //this.el.wrap();
40110 if (config.toolbar.items) {
40111 ti = config.toolbar.items ;
40112 delete config.toolbar.items ;
40116 this.toolbar.render(this.wrapEl, 'before');
40117 for(var i =0;i < ti.length;i++) {
40118 // Roo.log(['add child', items[i]]);
40119 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40121 this.toolbar.items = nitems;
40122 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40123 delete config.toolbar;
40127 // xtype created footer. - not sure if will work as we normally have to render first..
40128 if (this.footer && !this.footer.el && this.footer.xtype) {
40129 if (!this.wrapEl) {
40130 this.wrapEl = this.el.wrap();
40133 this.footer.container = this.wrapEl.createChild();
40135 this.footer = Roo.factory(this.footer, Roo);
40140 if(typeof config == "string"){
40141 this.title = config;
40143 Roo.apply(this, config);
40147 this.resizeEl = Roo.get(this.resizeEl, true);
40149 this.resizeEl = this.el;
40151 // handle view.xtype
40159 * Fires when this panel is activated.
40160 * @param {Roo.ContentPanel} this
40164 * @event deactivate
40165 * Fires when this panel is activated.
40166 * @param {Roo.ContentPanel} this
40168 "deactivate" : true,
40172 * Fires when this panel is resized if fitToFrame is true.
40173 * @param {Roo.ContentPanel} this
40174 * @param {Number} width The width after any component adjustments
40175 * @param {Number} height The height after any component adjustments
40181 * Fires when this tab is created
40182 * @param {Roo.ContentPanel} this
40193 if(this.autoScroll && !this.iframe){
40194 this.resizeEl.setStyle("overflow", "auto");
40196 // fix randome scrolling
40197 //this.el.on('scroll', function() {
40198 // Roo.log('fix random scolling');
40199 // this.scrollTo('top',0);
40202 content = content || this.content;
40204 this.setContent(content);
40206 if(config && config.url){
40207 this.setUrl(this.url, this.params, this.loadOnce);
40212 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40214 if (this.view && typeof(this.view.xtype) != 'undefined') {
40215 this.view.el = this.el.appendChild(document.createElement("div"));
40216 this.view = Roo.factory(this.view);
40217 this.view.render && this.view.render(false, '');
40221 this.fireEvent('render', this);
40224 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40234 setRegion : function(region){
40235 this.region = region;
40236 this.setActiveClass(region && !this.background);
40240 setActiveClass: function(state)
40243 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40244 this.el.setStyle('position','relative');
40246 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40247 this.el.setStyle('position', 'absolute');
40252 * Returns the toolbar for this Panel if one was configured.
40253 * @return {Roo.Toolbar}
40255 getToolbar : function(){
40256 return this.toolbar;
40259 setActiveState : function(active)
40261 this.active = active;
40262 this.setActiveClass(active);
40264 if(this.fireEvent("deactivate", this) === false){
40269 this.fireEvent("activate", this);
40273 * Updates this panel's element (not for iframe)
40274 * @param {String} content The new content
40275 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40277 setContent : function(content, loadScripts){
40282 this.el.update(content, loadScripts);
40285 ignoreResize : function(w, h){
40286 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40289 this.lastSize = {width: w, height: h};
40294 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40295 * @return {Roo.UpdateManager} The UpdateManager
40297 getUpdateManager : function(){
40301 return this.el.getUpdateManager();
40304 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40305 * Does not work with IFRAME contents
40306 * @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:
40309 url: "your-url.php",
40310 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40311 callback: yourFunction,
40312 scope: yourObject, //(optional scope)
40315 text: "Loading...",
40321 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40322 * 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.
40323 * @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}
40324 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40325 * @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.
40326 * @return {Roo.ContentPanel} this
40334 var um = this.el.getUpdateManager();
40335 um.update.apply(um, arguments);
40341 * 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.
40342 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40343 * @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)
40344 * @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)
40345 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40347 setUrl : function(url, params, loadOnce){
40349 this.iframeEl.dom.src = url;
40353 if(this.refreshDelegate){
40354 this.removeListener("activate", this.refreshDelegate);
40356 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40357 this.on("activate", this.refreshDelegate);
40358 return this.el.getUpdateManager();
40361 _handleRefresh : function(url, params, loadOnce){
40362 if(!loadOnce || !this.loaded){
40363 var updater = this.el.getUpdateManager();
40364 updater.update(url, params, this._setLoaded.createDelegate(this));
40368 _setLoaded : function(){
40369 this.loaded = true;
40373 * Returns this panel's id
40376 getId : function(){
40381 * Returns this panel's element - used by regiosn to add.
40382 * @return {Roo.Element}
40384 getEl : function(){
40385 return this.wrapEl || this.el;
40390 adjustForComponents : function(width, height)
40392 //Roo.log('adjustForComponents ');
40393 if(this.resizeEl != this.el){
40394 width -= this.el.getFrameWidth('lr');
40395 height -= this.el.getFrameWidth('tb');
40398 var te = this.toolbar.getEl();
40399 te.setWidth(width);
40400 height -= te.getHeight();
40403 var te = this.footer.getEl();
40404 te.setWidth(width);
40405 height -= te.getHeight();
40409 if(this.adjustments){
40410 width += this.adjustments[0];
40411 height += this.adjustments[1];
40413 return {"width": width, "height": height};
40416 setSize : function(width, height){
40417 if(this.fitToFrame && !this.ignoreResize(width, height)){
40418 if(this.fitContainer && this.resizeEl != this.el){
40419 this.el.setSize(width, height);
40421 var size = this.adjustForComponents(width, height);
40423 this.iframeEl.setSize(width,height);
40426 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40427 this.fireEvent('resize', this, size.width, size.height);
40434 * Returns this panel's title
40437 getTitle : function(){
40439 if (typeof(this.title) != 'object') {
40444 for (var k in this.title) {
40445 if (!this.title.hasOwnProperty(k)) {
40449 if (k.indexOf('-') >= 0) {
40450 var s = k.split('-');
40451 for (var i = 0; i<s.length; i++) {
40452 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40455 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40462 * Set this panel's title
40463 * @param {String} title
40465 setTitle : function(title){
40466 this.title = title;
40468 this.region.updatePanelTitle(this, title);
40473 * Returns true is this panel was configured to be closable
40474 * @return {Boolean}
40476 isClosable : function(){
40477 return this.closable;
40480 beforeSlide : function(){
40482 this.resizeEl.clip();
40485 afterSlide : function(){
40487 this.resizeEl.unclip();
40491 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40492 * Will fail silently if the {@link #setUrl} method has not been called.
40493 * This does not activate the panel, just updates its content.
40495 refresh : function(){
40496 if(this.refreshDelegate){
40497 this.loaded = false;
40498 this.refreshDelegate();
40503 * Destroys this panel
40505 destroy : function(){
40506 this.el.removeAllListeners();
40507 var tempEl = document.createElement("span");
40508 tempEl.appendChild(this.el.dom);
40509 tempEl.innerHTML = "";
40515 * form - if the content panel contains a form - this is a reference to it.
40516 * @type {Roo.form.Form}
40520 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40521 * This contains a reference to it.
40527 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40537 * @param {Object} cfg Xtype definition of item to add.
40541 getChildContainer: function () {
40542 return this.getEl();
40547 var ret = new Roo.factory(cfg);
40552 if (cfg.xtype.match(/^Form$/)) {
40555 //if (this.footer) {
40556 // el = this.footer.container.insertSibling(false, 'before');
40558 el = this.el.createChild();
40561 this.form = new Roo.form.Form(cfg);
40564 if ( this.form.allItems.length) {
40565 this.form.render(el.dom);
40569 // should only have one of theses..
40570 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40571 // views.. should not be just added - used named prop 'view''
40573 cfg.el = this.el.appendChild(document.createElement("div"));
40576 var ret = new Roo.factory(cfg);
40578 ret.render && ret.render(false, ''); // render blank..
40588 * @class Roo.bootstrap.panel.Grid
40589 * @extends Roo.bootstrap.panel.Content
40591 * Create a new GridPanel.
40592 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40593 * @param {Object} config A the config object
40599 Roo.bootstrap.panel.Grid = function(config)
40603 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40604 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40606 config.el = this.wrapper;
40607 //this.el = this.wrapper;
40609 if (config.container) {
40610 // ctor'ed from a Border/panel.grid
40613 this.wrapper.setStyle("overflow", "hidden");
40614 this.wrapper.addClass('roo-grid-container');
40619 if(config.toolbar){
40620 var tool_el = this.wrapper.createChild();
40621 this.toolbar = Roo.factory(config.toolbar);
40623 if (config.toolbar.items) {
40624 ti = config.toolbar.items ;
40625 delete config.toolbar.items ;
40629 this.toolbar.render(tool_el);
40630 for(var i =0;i < ti.length;i++) {
40631 // Roo.log(['add child', items[i]]);
40632 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40634 this.toolbar.items = nitems;
40636 delete config.toolbar;
40639 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40640 config.grid.scrollBody = true;;
40641 config.grid.monitorWindowResize = false; // turn off autosizing
40642 config.grid.autoHeight = false;
40643 config.grid.autoWidth = false;
40645 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40647 if (config.background) {
40648 // render grid on panel activation (if panel background)
40649 this.on('activate', function(gp) {
40650 if (!gp.grid.rendered) {
40651 gp.grid.render(this.wrapper);
40652 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40657 this.grid.render(this.wrapper);
40658 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40661 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40662 // ??? needed ??? config.el = this.wrapper;
40667 // xtype created footer. - not sure if will work as we normally have to render first..
40668 if (this.footer && !this.footer.el && this.footer.xtype) {
40670 var ctr = this.grid.getView().getFooterPanel(true);
40671 this.footer.dataSource = this.grid.dataSource;
40672 this.footer = Roo.factory(this.footer, Roo);
40673 this.footer.render(ctr);
40683 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40684 getId : function(){
40685 return this.grid.id;
40689 * Returns the grid for this panel
40690 * @return {Roo.bootstrap.Table}
40692 getGrid : function(){
40696 setSize : function(width, height){
40697 if(!this.ignoreResize(width, height)){
40698 var grid = this.grid;
40699 var size = this.adjustForComponents(width, height);
40700 // tfoot is not a footer?
40703 var gridel = grid.getGridEl();
40704 gridel.setSize(size.width, size.height);
40706 var tbd = grid.getGridEl().select('tbody', true).first();
40707 var thd = grid.getGridEl().select('thead',true).first();
40708 var tbf= grid.getGridEl().select('tfoot', true).first();
40711 size.height -= tbf.getHeight();
40714 size.height -= thd.getHeight();
40717 tbd.setSize(size.width, size.height );
40718 // this is for the account management tab -seems to work there.
40719 var thd = grid.getGridEl().select('thead',true).first();
40721 // tbd.setSize(size.width, size.height - thd.getHeight());
40730 beforeSlide : function(){
40731 this.grid.getView().scroller.clip();
40734 afterSlide : function(){
40735 this.grid.getView().scroller.unclip();
40738 destroy : function(){
40739 this.grid.destroy();
40741 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40746 * @class Roo.bootstrap.panel.Nest
40747 * @extends Roo.bootstrap.panel.Content
40749 * Create a new Panel, that can contain a layout.Border.
40752 * @param {Roo.BorderLayout} layout The layout for this panel
40753 * @param {String/Object} config A string to set only the title or a config object
40755 Roo.bootstrap.panel.Nest = function(config)
40757 // construct with only one argument..
40758 /* FIXME - implement nicer consturctors
40759 if (layout.layout) {
40761 layout = config.layout;
40762 delete config.layout;
40764 if (layout.xtype && !layout.getEl) {
40765 // then layout needs constructing..
40766 layout = Roo.factory(layout, Roo);
40770 config.el = config.layout.getEl();
40772 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40774 config.layout.monitorWindowResize = false; // turn off autosizing
40775 this.layout = config.layout;
40776 this.layout.getEl().addClass("roo-layout-nested-layout");
40777 this.layout.parent = this;
40784 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40786 setSize : function(width, height){
40787 if(!this.ignoreResize(width, height)){
40788 var size = this.adjustForComponents(width, height);
40789 var el = this.layout.getEl();
40790 if (size.height < 1) {
40791 el.setWidth(size.width);
40793 el.setSize(size.width, size.height);
40795 var touch = el.dom.offsetWidth;
40796 this.layout.layout();
40797 // ie requires a double layout on the first pass
40798 if(Roo.isIE && !this.initialized){
40799 this.initialized = true;
40800 this.layout.layout();
40805 // activate all subpanels if not currently active..
40807 setActiveState : function(active){
40808 this.active = active;
40809 this.setActiveClass(active);
40812 this.fireEvent("deactivate", this);
40816 this.fireEvent("activate", this);
40817 // not sure if this should happen before or after..
40818 if (!this.layout) {
40819 return; // should not happen..
40822 for (var r in this.layout.regions) {
40823 reg = this.layout.getRegion(r);
40824 if (reg.getActivePanel()) {
40825 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40826 reg.setActivePanel(reg.getActivePanel());
40829 if (!reg.panels.length) {
40832 reg.showPanel(reg.getPanel(0));
40841 * Returns the nested BorderLayout for this panel
40842 * @return {Roo.BorderLayout}
40844 getLayout : function(){
40845 return this.layout;
40849 * Adds a xtype elements to the layout of the nested panel
40853 xtype : 'ContentPanel',
40860 xtype : 'NestedLayoutPanel',
40866 items : [ ... list of content panels or nested layout panels.. ]
40870 * @param {Object} cfg Xtype definition of item to add.
40872 addxtype : function(cfg) {
40873 return this.layout.addxtype(cfg);
40878 * Ext JS Library 1.1.1
40879 * Copyright(c) 2006-2007, Ext JS, LLC.
40881 * Originally Released Under LGPL - original licence link has changed is not relivant.
40884 * <script type="text/javascript">
40887 * @class Roo.TabPanel
40888 * @extends Roo.util.Observable
40889 * A lightweight tab container.
40893 // basic tabs 1, built from existing content
40894 var tabs = new Roo.TabPanel("tabs1");
40895 tabs.addTab("script", "View Script");
40896 tabs.addTab("markup", "View Markup");
40897 tabs.activate("script");
40899 // more advanced tabs, built from javascript
40900 var jtabs = new Roo.TabPanel("jtabs");
40901 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40903 // set up the UpdateManager
40904 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40905 var updater = tab2.getUpdateManager();
40906 updater.setDefaultUrl("ajax1.htm");
40907 tab2.on('activate', updater.refresh, updater, true);
40909 // Use setUrl for Ajax loading
40910 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40911 tab3.setUrl("ajax2.htm", null, true);
40914 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40917 jtabs.activate("jtabs-1");
40920 * Create a new TabPanel.
40921 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40922 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40924 Roo.bootstrap.panel.Tabs = function(config){
40926 * The container element for this TabPanel.
40927 * @type Roo.Element
40929 this.el = Roo.get(config.el);
40932 if(typeof config == "boolean"){
40933 this.tabPosition = config ? "bottom" : "top";
40935 Roo.apply(this, config);
40939 if(this.tabPosition == "bottom"){
40940 // if tabs are at the bottom = create the body first.
40941 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40942 this.el.addClass("roo-tabs-bottom");
40944 // next create the tabs holders
40946 if (this.tabPosition == "west"){
40948 var reg = this.region; // fake it..
40950 if (!reg.mgr.parent) {
40953 reg = reg.mgr.parent.region;
40955 Roo.log("got nest?");
40957 if (reg.mgr.getRegion('west')) {
40958 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40959 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40960 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40961 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40962 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40970 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40971 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40972 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40973 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40978 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40981 // finally - if tabs are at the top, then create the body last..
40982 if(this.tabPosition != "bottom"){
40983 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40984 * @type Roo.Element
40986 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40987 this.el.addClass("roo-tabs-top");
40991 this.bodyEl.setStyle("position", "relative");
40993 this.active = null;
40994 this.activateDelegate = this.activate.createDelegate(this);
40999 * Fires when the active tab changes
41000 * @param {Roo.TabPanel} this
41001 * @param {Roo.TabPanelItem} activePanel The new active tab
41005 * @event beforetabchange
41006 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41007 * @param {Roo.TabPanel} this
41008 * @param {Object} e Set cancel to true on this object to cancel the tab change
41009 * @param {Roo.TabPanelItem} tab The tab being changed to
41011 "beforetabchange" : true
41014 Roo.EventManager.onWindowResize(this.onResize, this);
41015 this.cpad = this.el.getPadding("lr");
41016 this.hiddenCount = 0;
41019 // toolbar on the tabbar support...
41020 if (this.toolbar) {
41021 alert("no toolbar support yet");
41022 this.toolbar = false;
41024 var tcfg = this.toolbar;
41025 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41026 this.toolbar = new Roo.Toolbar(tcfg);
41027 if (Roo.isSafari) {
41028 var tbl = tcfg.container.child('table', true);
41029 tbl.setAttribute('width', '100%');
41037 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41040 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41042 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41044 tabPosition : "top",
41046 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41048 currentTabWidth : 0,
41050 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41054 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41058 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41060 preferredTabWidth : 175,
41062 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41064 resizeTabs : false,
41066 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41068 monitorResize : true,
41070 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41072 toolbar : false, // set by caller..
41074 region : false, /// set by caller
41076 disableTooltips : true, // not used yet...
41079 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41080 * @param {String} id The id of the div to use <b>or create</b>
41081 * @param {String} text The text for the tab
41082 * @param {String} content (optional) Content to put in the TabPanelItem body
41083 * @param {Boolean} closable (optional) True to create a close icon on the tab
41084 * @return {Roo.TabPanelItem} The created TabPanelItem
41086 addTab : function(id, text, content, closable, tpl)
41088 var item = new Roo.bootstrap.panel.TabItem({
41092 closable : closable,
41095 this.addTabItem(item);
41097 item.setContent(content);
41103 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41104 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41105 * @return {Roo.TabPanelItem}
41107 getTab : function(id){
41108 return this.items[id];
41112 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41113 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41115 hideTab : function(id){
41116 var t = this.items[id];
41119 this.hiddenCount++;
41120 this.autoSizeTabs();
41125 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41126 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41128 unhideTab : function(id){
41129 var t = this.items[id];
41131 t.setHidden(false);
41132 this.hiddenCount--;
41133 this.autoSizeTabs();
41138 * Adds an existing {@link Roo.TabPanelItem}.
41139 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41141 addTabItem : function(item)
41143 this.items[item.id] = item;
41144 this.items.push(item);
41145 this.autoSizeTabs();
41146 // if(this.resizeTabs){
41147 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41148 // this.autoSizeTabs();
41150 // item.autoSize();
41155 * Removes a {@link Roo.TabPanelItem}.
41156 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41158 removeTab : function(id){
41159 var items = this.items;
41160 var tab = items[id];
41161 if(!tab) { return; }
41162 var index = items.indexOf(tab);
41163 if(this.active == tab && items.length > 1){
41164 var newTab = this.getNextAvailable(index);
41169 this.stripEl.dom.removeChild(tab.pnode.dom);
41170 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41171 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41173 items.splice(index, 1);
41174 delete this.items[tab.id];
41175 tab.fireEvent("close", tab);
41176 tab.purgeListeners();
41177 this.autoSizeTabs();
41180 getNextAvailable : function(start){
41181 var items = this.items;
41183 // look for a next tab that will slide over to
41184 // replace the one being removed
41185 while(index < items.length){
41186 var item = items[++index];
41187 if(item && !item.isHidden()){
41191 // if one isn't found select the previous tab (on the left)
41194 var item = items[--index];
41195 if(item && !item.isHidden()){
41203 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41204 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41206 disableTab : function(id){
41207 var tab = this.items[id];
41208 if(tab && this.active != tab){
41214 * Enables a {@link Roo.TabPanelItem} that is disabled.
41215 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41217 enableTab : function(id){
41218 var tab = this.items[id];
41223 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41224 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41225 * @return {Roo.TabPanelItem} The TabPanelItem.
41227 activate : function(id)
41229 //Roo.log('activite:' + id);
41231 var tab = this.items[id];
41235 if(tab == this.active || tab.disabled){
41239 this.fireEvent("beforetabchange", this, e, tab);
41240 if(e.cancel !== true && !tab.disabled){
41242 this.active.hide();
41244 this.active = this.items[id];
41245 this.active.show();
41246 this.fireEvent("tabchange", this, this.active);
41252 * Gets the active {@link Roo.TabPanelItem}.
41253 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41255 getActiveTab : function(){
41256 return this.active;
41260 * Updates the tab body element to fit the height of the container element
41261 * for overflow scrolling
41262 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41264 syncHeight : function(targetHeight){
41265 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41266 var bm = this.bodyEl.getMargins();
41267 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41268 this.bodyEl.setHeight(newHeight);
41272 onResize : function(){
41273 if(this.monitorResize){
41274 this.autoSizeTabs();
41279 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41281 beginUpdate : function(){
41282 this.updating = true;
41286 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41288 endUpdate : function(){
41289 this.updating = false;
41290 this.autoSizeTabs();
41294 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41296 autoSizeTabs : function()
41298 var count = this.items.length;
41299 var vcount = count - this.hiddenCount;
41302 this.stripEl.hide();
41304 this.stripEl.show();
41307 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41312 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41313 var availWidth = Math.floor(w / vcount);
41314 var b = this.stripBody;
41315 if(b.getWidth() > w){
41316 var tabs = this.items;
41317 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41318 if(availWidth < this.minTabWidth){
41319 /*if(!this.sleft){ // incomplete scrolling code
41320 this.createScrollButtons();
41323 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41326 if(this.currentTabWidth < this.preferredTabWidth){
41327 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41333 * Returns the number of tabs in this TabPanel.
41336 getCount : function(){
41337 return this.items.length;
41341 * Resizes all the tabs to the passed width
41342 * @param {Number} The new width
41344 setTabWidth : function(width){
41345 this.currentTabWidth = width;
41346 for(var i = 0, len = this.items.length; i < len; i++) {
41347 if(!this.items[i].isHidden()) {
41348 this.items[i].setWidth(width);
41354 * Destroys this TabPanel
41355 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41357 destroy : function(removeEl){
41358 Roo.EventManager.removeResizeListener(this.onResize, this);
41359 for(var i = 0, len = this.items.length; i < len; i++){
41360 this.items[i].purgeListeners();
41362 if(removeEl === true){
41363 this.el.update("");
41368 createStrip : function(container)
41370 var strip = document.createElement("nav");
41371 strip.className = Roo.bootstrap.version == 4 ?
41372 "navbar-light bg-light" :
41373 "navbar navbar-default"; //"x-tabs-wrap";
41374 container.appendChild(strip);
41378 createStripList : function(strip)
41380 // div wrapper for retard IE
41381 // returns the "tr" element.
41382 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41383 //'<div class="x-tabs-strip-wrap">'+
41384 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41385 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41386 return strip.firstChild; //.firstChild.firstChild.firstChild;
41388 createBody : function(container)
41390 var body = document.createElement("div");
41391 Roo.id(body, "tab-body");
41392 //Roo.fly(body).addClass("x-tabs-body");
41393 Roo.fly(body).addClass("tab-content");
41394 container.appendChild(body);
41397 createItemBody :function(bodyEl, id){
41398 var body = Roo.getDom(id);
41400 body = document.createElement("div");
41403 //Roo.fly(body).addClass("x-tabs-item-body");
41404 Roo.fly(body).addClass("tab-pane");
41405 bodyEl.insertBefore(body, bodyEl.firstChild);
41409 createStripElements : function(stripEl, text, closable, tpl)
41411 var td = document.createElement("li"); // was td..
41412 td.className = 'nav-item';
41414 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41417 stripEl.appendChild(td);
41419 td.className = "x-tabs-closable";
41420 if(!this.closeTpl){
41421 this.closeTpl = new Roo.Template(
41422 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41423 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41424 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41427 var el = this.closeTpl.overwrite(td, {"text": text});
41428 var close = el.getElementsByTagName("div")[0];
41429 var inner = el.getElementsByTagName("em")[0];
41430 return {"el": el, "close": close, "inner": inner};
41433 // not sure what this is..
41434 // if(!this.tabTpl){
41435 //this.tabTpl = new Roo.Template(
41436 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41437 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41439 // this.tabTpl = new Roo.Template(
41440 // '<a href="#">' +
41441 // '<span unselectable="on"' +
41442 // (this.disableTooltips ? '' : ' title="{text}"') +
41443 // ' >{text}</span></a>'
41449 var template = tpl || this.tabTpl || false;
41452 template = new Roo.Template(
41453 Roo.bootstrap.version == 4 ?
41455 '<a class="nav-link" href="#" unselectable="on"' +
41456 (this.disableTooltips ? '' : ' title="{text}"') +
41459 '<a class="nav-link" href="#">' +
41460 '<span unselectable="on"' +
41461 (this.disableTooltips ? '' : ' title="{text}"') +
41462 ' >{text}</span></a>'
41467 switch (typeof(template)) {
41471 template = new Roo.Template(template);
41477 var el = template.overwrite(td, {"text": text});
41479 var inner = el.getElementsByTagName("span")[0];
41481 return {"el": el, "inner": inner};
41489 * @class Roo.TabPanelItem
41490 * @extends Roo.util.Observable
41491 * Represents an individual item (tab plus body) in a TabPanel.
41492 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41493 * @param {String} id The id of this TabPanelItem
41494 * @param {String} text The text for the tab of this TabPanelItem
41495 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41497 Roo.bootstrap.panel.TabItem = function(config){
41499 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41500 * @type Roo.TabPanel
41502 this.tabPanel = config.panel;
41504 * The id for this TabPanelItem
41507 this.id = config.id;
41509 this.disabled = false;
41511 this.text = config.text;
41513 this.loaded = false;
41514 this.closable = config.closable;
41517 * The body element for this TabPanelItem.
41518 * @type Roo.Element
41520 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41521 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41522 this.bodyEl.setStyle("display", "block");
41523 this.bodyEl.setStyle("zoom", "1");
41524 //this.hideAction();
41526 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41528 this.el = Roo.get(els.el);
41529 this.inner = Roo.get(els.inner, true);
41530 this.textEl = Roo.bootstrap.version == 4 ?
41531 this.el : Roo.get(this.el.dom.firstChild, true);
41533 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41534 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41537 // this.el.on("mousedown", this.onTabMouseDown, this);
41538 this.el.on("click", this.onTabClick, this);
41540 if(config.closable){
41541 var c = Roo.get(els.close, true);
41542 c.dom.title = this.closeText;
41543 c.addClassOnOver("close-over");
41544 c.on("click", this.closeClick, this);
41550 * Fires when this tab becomes the active tab.
41551 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41552 * @param {Roo.TabPanelItem} this
41556 * @event beforeclose
41557 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41558 * @param {Roo.TabPanelItem} this
41559 * @param {Object} e Set cancel to true on this object to cancel the close.
41561 "beforeclose": true,
41564 * Fires when this tab is closed.
41565 * @param {Roo.TabPanelItem} this
41569 * @event deactivate
41570 * Fires when this tab is no longer the active tab.
41571 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41572 * @param {Roo.TabPanelItem} this
41574 "deactivate" : true
41576 this.hidden = false;
41578 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41581 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41583 purgeListeners : function(){
41584 Roo.util.Observable.prototype.purgeListeners.call(this);
41585 this.el.removeAllListeners();
41588 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41591 this.status_node.addClass("active");
41594 this.tabPanel.stripWrap.repaint();
41596 this.fireEvent("activate", this.tabPanel, this);
41600 * Returns true if this tab is the active tab.
41601 * @return {Boolean}
41603 isActive : function(){
41604 return this.tabPanel.getActiveTab() == this;
41608 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41611 this.status_node.removeClass("active");
41613 this.fireEvent("deactivate", this.tabPanel, this);
41616 hideAction : function(){
41617 this.bodyEl.hide();
41618 this.bodyEl.setStyle("position", "absolute");
41619 this.bodyEl.setLeft("-20000px");
41620 this.bodyEl.setTop("-20000px");
41623 showAction : function(){
41624 this.bodyEl.setStyle("position", "relative");
41625 this.bodyEl.setTop("");
41626 this.bodyEl.setLeft("");
41627 this.bodyEl.show();
41631 * Set the tooltip for the tab.
41632 * @param {String} tooltip The tab's tooltip
41634 setTooltip : function(text){
41635 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41636 this.textEl.dom.qtip = text;
41637 this.textEl.dom.removeAttribute('title');
41639 this.textEl.dom.title = text;
41643 onTabClick : function(e){
41644 e.preventDefault();
41645 this.tabPanel.activate(this.id);
41648 onTabMouseDown : function(e){
41649 e.preventDefault();
41650 this.tabPanel.activate(this.id);
41653 getWidth : function(){
41654 return this.inner.getWidth();
41657 setWidth : function(width){
41658 var iwidth = width - this.linode.getPadding("lr");
41659 this.inner.setWidth(iwidth);
41660 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41661 this.linode.setWidth(width);
41665 * Show or hide the tab
41666 * @param {Boolean} hidden True to hide or false to show.
41668 setHidden : function(hidden){
41669 this.hidden = hidden;
41670 this.linode.setStyle("display", hidden ? "none" : "");
41674 * Returns true if this tab is "hidden"
41675 * @return {Boolean}
41677 isHidden : function(){
41678 return this.hidden;
41682 * Returns the text for this tab
41685 getText : function(){
41689 autoSize : function(){
41690 //this.el.beginMeasure();
41691 this.textEl.setWidth(1);
41693 * #2804 [new] Tabs in Roojs
41694 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41696 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41697 //this.el.endMeasure();
41701 * Sets the text for the tab (Note: this also sets the tooltip text)
41702 * @param {String} text The tab's text and tooltip
41704 setText : function(text){
41706 this.textEl.update(text);
41707 this.setTooltip(text);
41708 //if(!this.tabPanel.resizeTabs){
41709 // this.autoSize();
41713 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41715 activate : function(){
41716 this.tabPanel.activate(this.id);
41720 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41722 disable : function(){
41723 if(this.tabPanel.active != this){
41724 this.disabled = true;
41725 this.status_node.addClass("disabled");
41730 * Enables this TabPanelItem if it was previously disabled.
41732 enable : function(){
41733 this.disabled = false;
41734 this.status_node.removeClass("disabled");
41738 * Sets the content for this TabPanelItem.
41739 * @param {String} content The content
41740 * @param {Boolean} loadScripts true to look for and load scripts
41742 setContent : function(content, loadScripts){
41743 this.bodyEl.update(content, loadScripts);
41747 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41748 * @return {Roo.UpdateManager} The UpdateManager
41750 getUpdateManager : function(){
41751 return this.bodyEl.getUpdateManager();
41755 * Set a URL to be used to load the content for this TabPanelItem.
41756 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41757 * @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)
41758 * @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)
41759 * @return {Roo.UpdateManager} The UpdateManager
41761 setUrl : function(url, params, loadOnce){
41762 if(this.refreshDelegate){
41763 this.un('activate', this.refreshDelegate);
41765 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41766 this.on("activate", this.refreshDelegate);
41767 return this.bodyEl.getUpdateManager();
41771 _handleRefresh : function(url, params, loadOnce){
41772 if(!loadOnce || !this.loaded){
41773 var updater = this.bodyEl.getUpdateManager();
41774 updater.update(url, params, this._setLoaded.createDelegate(this));
41779 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41780 * Will fail silently if the setUrl method has not been called.
41781 * This does not activate the panel, just updates its content.
41783 refresh : function(){
41784 if(this.refreshDelegate){
41785 this.loaded = false;
41786 this.refreshDelegate();
41791 _setLoaded : function(){
41792 this.loaded = true;
41796 closeClick : function(e){
41799 this.fireEvent("beforeclose", this, o);
41800 if(o.cancel !== true){
41801 this.tabPanel.removeTab(this.id);
41805 * The text displayed in the tooltip for the close icon.
41808 closeText : "Close this tab"
41811 * This script refer to:
41812 * Title: International Telephone Input
41813 * Author: Jack O'Connor
41814 * Code version: v12.1.12
41815 * Availability: https://github.com/jackocnr/intl-tel-input.git
41818 Roo.bootstrap.PhoneInputData = function() {
41821 "Afghanistan (افغانستان)",
41826 "Albania (Shqipëri)",
41831 "Algeria (الجزائر)",
41856 "Antigua and Barbuda",
41866 "Armenia (Հայաստան)",
41882 "Austria (Österreich)",
41887 "Azerbaijan (Azərbaycan)",
41897 "Bahrain (البحرين)",
41902 "Bangladesh (বাংলাদেশ)",
41912 "Belarus (Беларусь)",
41917 "Belgium (België)",
41947 "Bosnia and Herzegovina (Босна и Херцеговина)",
41962 "British Indian Ocean Territory",
41967 "British Virgin Islands",
41977 "Bulgaria (България)",
41987 "Burundi (Uburundi)",
41992 "Cambodia (កម្ពុជា)",
41997 "Cameroon (Cameroun)",
42006 ["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"]
42009 "Cape Verde (Kabu Verdi)",
42014 "Caribbean Netherlands",
42025 "Central African Republic (République centrafricaine)",
42045 "Christmas Island",
42051 "Cocos (Keeling) Islands",
42062 "Comoros (جزر القمر)",
42067 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42072 "Congo (Republic) (Congo-Brazzaville)",
42092 "Croatia (Hrvatska)",
42113 "Czech Republic (Česká republika)",
42118 "Denmark (Danmark)",
42133 "Dominican Republic (República Dominicana)",
42137 ["809", "829", "849"]
42155 "Equatorial Guinea (Guinea Ecuatorial)",
42175 "Falkland Islands (Islas Malvinas)",
42180 "Faroe Islands (Føroyar)",
42201 "French Guiana (Guyane française)",
42206 "French Polynesia (Polynésie française)",
42221 "Georgia (საქართველო)",
42226 "Germany (Deutschland)",
42246 "Greenland (Kalaallit Nunaat)",
42283 "Guinea-Bissau (Guiné Bissau)",
42308 "Hungary (Magyarország)",
42313 "Iceland (Ísland)",
42333 "Iraq (العراق)",
42349 "Israel (ישראל)",
42376 "Jordan (الأردن)",
42381 "Kazakhstan (Казахстан)",
42402 "Kuwait (الكويت)",
42407 "Kyrgyzstan (Кыргызстан)",
42417 "Latvia (Latvija)",
42422 "Lebanon (لبنان)",
42437 "Libya (ليبيا)",
42447 "Lithuania (Lietuva)",
42462 "Macedonia (FYROM) (Македонија)",
42467 "Madagascar (Madagasikara)",
42497 "Marshall Islands",
42507 "Mauritania (موريتانيا)",
42512 "Mauritius (Moris)",
42533 "Moldova (Republica Moldova)",
42543 "Mongolia (Монгол)",
42548 "Montenegro (Crna Gora)",
42558 "Morocco (المغرب)",
42564 "Mozambique (Moçambique)",
42569 "Myanmar (Burma) (မြန်မာ)",
42574 "Namibia (Namibië)",
42589 "Netherlands (Nederland)",
42594 "New Caledonia (Nouvelle-Calédonie)",
42629 "North Korea (조선 민주주의 인민 공화국)",
42634 "Northern Mariana Islands",
42650 "Pakistan (پاکستان)",
42660 "Palestine (فلسطين)",
42670 "Papua New Guinea",
42712 "Réunion (La Réunion)",
42718 "Romania (România)",
42734 "Saint Barthélemy",
42745 "Saint Kitts and Nevis",
42755 "Saint Martin (Saint-Martin (partie française))",
42761 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42766 "Saint Vincent and the Grenadines",
42781 "São Tomé and Príncipe (São Tomé e Príncipe)",
42786 "Saudi Arabia (المملكة العربية السعودية)",
42791 "Senegal (Sénégal)",
42821 "Slovakia (Slovensko)",
42826 "Slovenia (Slovenija)",
42836 "Somalia (Soomaaliya)",
42846 "South Korea (대한민국)",
42851 "South Sudan (جنوب السودان)",
42861 "Sri Lanka (ශ්රී ලංකාව)",
42866 "Sudan (السودان)",
42876 "Svalbard and Jan Mayen",
42887 "Sweden (Sverige)",
42892 "Switzerland (Schweiz)",
42897 "Syria (سوريا)",
42942 "Trinidad and Tobago",
42947 "Tunisia (تونس)",
42952 "Turkey (Türkiye)",
42962 "Turks and Caicos Islands",
42972 "U.S. Virgin Islands",
42982 "Ukraine (Україна)",
42987 "United Arab Emirates (الإمارات العربية المتحدة)",
43009 "Uzbekistan (Oʻzbekiston)",
43019 "Vatican City (Città del Vaticano)",
43030 "Vietnam (Việt Nam)",
43035 "Wallis and Futuna (Wallis-et-Futuna)",
43040 "Western Sahara (الصحراء الغربية)",
43046 "Yemen (اليمن)",
43070 * This script refer to:
43071 * Title: International Telephone Input
43072 * Author: Jack O'Connor
43073 * Code version: v12.1.12
43074 * Availability: https://github.com/jackocnr/intl-tel-input.git
43078 * @class Roo.bootstrap.PhoneInput
43079 * @extends Roo.bootstrap.TriggerField
43080 * An input with International dial-code selection
43082 * @cfg {String} defaultDialCode default '+852'
43083 * @cfg {Array} preferedCountries default []
43086 * Create a new PhoneInput.
43087 * @param {Object} config Configuration options
43090 Roo.bootstrap.PhoneInput = function(config) {
43091 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43094 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43096 listWidth: undefined,
43098 selectedClass: 'active',
43100 invalidClass : "has-warning",
43102 validClass: 'has-success',
43104 allowed: '0123456789',
43109 * @cfg {String} defaultDialCode The default dial code when initializing the input
43111 defaultDialCode: '+852',
43114 * @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
43116 preferedCountries: false,
43118 getAutoCreate : function()
43120 var data = Roo.bootstrap.PhoneInputData();
43121 var align = this.labelAlign || this.parentLabelAlign();
43124 this.allCountries = [];
43125 this.dialCodeMapping = [];
43127 for (var i = 0; i < data.length; i++) {
43129 this.allCountries[i] = {
43133 priority: c[3] || 0,
43134 areaCodes: c[4] || null
43136 this.dialCodeMapping[c[2]] = {
43139 priority: c[3] || 0,
43140 areaCodes: c[4] || null
43152 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43153 maxlength: this.max_length,
43154 cls : 'form-control tel-input',
43155 autocomplete: 'new-password'
43158 var hiddenInput = {
43161 cls: 'hidden-tel-input'
43165 hiddenInput.name = this.name;
43168 if (this.disabled) {
43169 input.disabled = true;
43172 var flag_container = {
43189 cls: this.hasFeedback ? 'has-feedback' : '',
43195 cls: 'dial-code-holder',
43202 cls: 'roo-select2-container input-group',
43209 if (this.fieldLabel.length) {
43212 tooltip: 'This field is required'
43218 cls: 'control-label',
43224 html: this.fieldLabel
43227 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43233 if(this.indicatorpos == 'right') {
43234 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43241 if(align == 'left') {
43249 if(this.labelWidth > 12){
43250 label.style = "width: " + this.labelWidth + 'px';
43252 if(this.labelWidth < 13 && this.labelmd == 0){
43253 this.labelmd = this.labelWidth;
43255 if(this.labellg > 0){
43256 label.cls += ' col-lg-' + this.labellg;
43257 input.cls += ' col-lg-' + (12 - this.labellg);
43259 if(this.labelmd > 0){
43260 label.cls += ' col-md-' + this.labelmd;
43261 container.cls += ' col-md-' + (12 - this.labelmd);
43263 if(this.labelsm > 0){
43264 label.cls += ' col-sm-' + this.labelsm;
43265 container.cls += ' col-sm-' + (12 - this.labelsm);
43267 if(this.labelxs > 0){
43268 label.cls += ' col-xs-' + this.labelxs;
43269 container.cls += ' col-xs-' + (12 - this.labelxs);
43279 var settings = this;
43281 ['xs','sm','md','lg'].map(function(size){
43282 if (settings[size]) {
43283 cfg.cls += ' col-' + size + '-' + settings[size];
43287 this.store = new Roo.data.Store({
43288 proxy : new Roo.data.MemoryProxy({}),
43289 reader : new Roo.data.JsonReader({
43300 'name' : 'dialCode',
43304 'name' : 'priority',
43308 'name' : 'areaCodes',
43315 if(!this.preferedCountries) {
43316 this.preferedCountries = [
43323 var p = this.preferedCountries.reverse();
43326 for (var i = 0; i < p.length; i++) {
43327 for (var j = 0; j < this.allCountries.length; j++) {
43328 if(this.allCountries[j].iso2 == p[i]) {
43329 var t = this.allCountries[j];
43330 this.allCountries.splice(j,1);
43331 this.allCountries.unshift(t);
43337 this.store.proxy.data = {
43339 data: this.allCountries
43345 initEvents : function()
43348 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43350 this.indicator = this.indicatorEl();
43351 this.flag = this.flagEl();
43352 this.dialCodeHolder = this.dialCodeHolderEl();
43354 this.trigger = this.el.select('div.flag-box',true).first();
43355 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43360 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43361 _this.list.setWidth(lw);
43364 this.list.on('mouseover', this.onViewOver, this);
43365 this.list.on('mousemove', this.onViewMove, this);
43366 this.inputEl().on("keyup", this.onKeyUp, this);
43367 this.inputEl().on("keypress", this.onKeyPress, this);
43369 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43371 this.view = new Roo.View(this.list, this.tpl, {
43372 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43375 this.view.on('click', this.onViewClick, this);
43376 this.setValue(this.defaultDialCode);
43379 onTriggerClick : function(e)
43381 Roo.log('trigger click');
43386 if(this.isExpanded()){
43388 this.hasFocus = false;
43390 this.store.load({});
43391 this.hasFocus = true;
43396 isExpanded : function()
43398 return this.list.isVisible();
43401 collapse : function()
43403 if(!this.isExpanded()){
43407 Roo.get(document).un('mousedown', this.collapseIf, this);
43408 Roo.get(document).un('mousewheel', this.collapseIf, this);
43409 this.fireEvent('collapse', this);
43413 expand : function()
43417 if(this.isExpanded() || !this.hasFocus){
43421 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43422 this.list.setWidth(lw);
43425 this.restrictHeight();
43427 Roo.get(document).on('mousedown', this.collapseIf, this);
43428 Roo.get(document).on('mousewheel', this.collapseIf, this);
43430 this.fireEvent('expand', this);
43433 restrictHeight : function()
43435 this.list.alignTo(this.inputEl(), this.listAlign);
43436 this.list.alignTo(this.inputEl(), this.listAlign);
43439 onViewOver : function(e, t)
43441 if(this.inKeyMode){
43444 var item = this.view.findItemFromChild(t);
43447 var index = this.view.indexOf(item);
43448 this.select(index, false);
43453 onViewClick : function(view, doFocus, el, e)
43455 var index = this.view.getSelectedIndexes()[0];
43457 var r = this.store.getAt(index);
43460 this.onSelect(r, index);
43462 if(doFocus !== false && !this.blockFocus){
43463 this.inputEl().focus();
43467 onViewMove : function(e, t)
43469 this.inKeyMode = false;
43472 select : function(index, scrollIntoView)
43474 this.selectedIndex = index;
43475 this.view.select(index);
43476 if(scrollIntoView !== false){
43477 var el = this.view.getNode(index);
43479 this.list.scrollChildIntoView(el, false);
43484 createList : function()
43486 this.list = Roo.get(document.body).createChild({
43488 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43489 style: 'display:none'
43492 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43495 collapseIf : function(e)
43497 var in_combo = e.within(this.el);
43498 var in_list = e.within(this.list);
43499 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43501 if (in_combo || in_list || is_list) {
43507 onSelect : function(record, index)
43509 if(this.fireEvent('beforeselect', this, record, index) !== false){
43511 this.setFlagClass(record.data.iso2);
43512 this.setDialCode(record.data.dialCode);
43513 this.hasFocus = false;
43515 this.fireEvent('select', this, record, index);
43519 flagEl : function()
43521 var flag = this.el.select('div.flag',true).first();
43528 dialCodeHolderEl : function()
43530 var d = this.el.select('input.dial-code-holder',true).first();
43537 setDialCode : function(v)
43539 this.dialCodeHolder.dom.value = '+'+v;
43542 setFlagClass : function(n)
43544 this.flag.dom.className = 'flag '+n;
43547 getValue : function()
43549 var v = this.inputEl().getValue();
43550 if(this.dialCodeHolder) {
43551 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43556 setValue : function(v)
43558 var d = this.getDialCode(v);
43560 //invalid dial code
43561 if(v.length == 0 || !d || d.length == 0) {
43563 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43564 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43570 this.setFlagClass(this.dialCodeMapping[d].iso2);
43571 this.setDialCode(d);
43572 this.inputEl().dom.value = v.replace('+'+d,'');
43573 this.hiddenEl().dom.value = this.getValue();
43578 getDialCode : function(v)
43582 if (v.length == 0) {
43583 return this.dialCodeHolder.dom.value;
43587 if (v.charAt(0) != "+") {
43590 var numericChars = "";
43591 for (var i = 1; i < v.length; i++) {
43592 var c = v.charAt(i);
43595 if (this.dialCodeMapping[numericChars]) {
43596 dialCode = v.substr(1, i);
43598 if (numericChars.length == 4) {
43608 this.setValue(this.defaultDialCode);
43612 hiddenEl : function()
43614 return this.el.select('input.hidden-tel-input',true).first();
43617 // after setting val
43618 onKeyUp : function(e){
43619 this.setValue(this.getValue());
43622 onKeyPress : function(e){
43623 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43630 * @class Roo.bootstrap.MoneyField
43631 * @extends Roo.bootstrap.ComboBox
43632 * Bootstrap MoneyField class
43635 * Create a new MoneyField.
43636 * @param {Object} config Configuration options
43639 Roo.bootstrap.MoneyField = function(config) {
43641 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43645 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43648 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43650 allowDecimals : true,
43652 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43654 decimalSeparator : ".",
43656 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43658 decimalPrecision : 0,
43660 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43662 allowNegative : true,
43664 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43668 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43670 minValue : Number.NEGATIVE_INFINITY,
43672 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43674 maxValue : Number.MAX_VALUE,
43676 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43678 minText : "The minimum value for this field is {0}",
43680 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43682 maxText : "The maximum value for this field is {0}",
43684 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43685 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43687 nanText : "{0} is not a valid number",
43689 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43693 * @cfg {String} defaults currency of the MoneyField
43694 * value should be in lkey
43696 defaultCurrency : false,
43698 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43700 thousandsDelimiter : false,
43702 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43713 getAutoCreate : function()
43715 var align = this.labelAlign || this.parentLabelAlign();
43727 cls : 'form-control roo-money-amount-input',
43728 autocomplete: 'new-password'
43731 var hiddenInput = {
43735 cls: 'hidden-number-input'
43738 if(this.max_length) {
43739 input.maxlength = this.max_length;
43743 hiddenInput.name = this.name;
43746 if (this.disabled) {
43747 input.disabled = true;
43750 var clg = 12 - this.inputlg;
43751 var cmd = 12 - this.inputmd;
43752 var csm = 12 - this.inputsm;
43753 var cxs = 12 - this.inputxs;
43757 cls : 'row roo-money-field',
43761 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43765 cls: 'roo-select2-container input-group',
43769 cls : 'form-control roo-money-currency-input',
43770 autocomplete: 'new-password',
43772 name : this.currencyName
43776 cls : 'input-group-addon',
43790 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43794 cls: this.hasFeedback ? 'has-feedback' : '',
43805 if (this.fieldLabel.length) {
43808 tooltip: 'This field is required'
43814 cls: 'control-label',
43820 html: this.fieldLabel
43823 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43829 if(this.indicatorpos == 'right') {
43830 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43837 if(align == 'left') {
43845 if(this.labelWidth > 12){
43846 label.style = "width: " + this.labelWidth + 'px';
43848 if(this.labelWidth < 13 && this.labelmd == 0){
43849 this.labelmd = this.labelWidth;
43851 if(this.labellg > 0){
43852 label.cls += ' col-lg-' + this.labellg;
43853 input.cls += ' col-lg-' + (12 - this.labellg);
43855 if(this.labelmd > 0){
43856 label.cls += ' col-md-' + this.labelmd;
43857 container.cls += ' col-md-' + (12 - this.labelmd);
43859 if(this.labelsm > 0){
43860 label.cls += ' col-sm-' + this.labelsm;
43861 container.cls += ' col-sm-' + (12 - this.labelsm);
43863 if(this.labelxs > 0){
43864 label.cls += ' col-xs-' + this.labelxs;
43865 container.cls += ' col-xs-' + (12 - this.labelxs);
43876 var settings = this;
43878 ['xs','sm','md','lg'].map(function(size){
43879 if (settings[size]) {
43880 cfg.cls += ' col-' + size + '-' + settings[size];
43887 initEvents : function()
43889 this.indicator = this.indicatorEl();
43891 this.initCurrencyEvent();
43893 this.initNumberEvent();
43896 initCurrencyEvent : function()
43899 throw "can not find store for combo";
43902 this.store = Roo.factory(this.store, Roo.data);
43903 this.store.parent = this;
43907 this.triggerEl = this.el.select('.input-group-addon', true).first();
43909 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43914 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43915 _this.list.setWidth(lw);
43918 this.list.on('mouseover', this.onViewOver, this);
43919 this.list.on('mousemove', this.onViewMove, this);
43920 this.list.on('scroll', this.onViewScroll, this);
43923 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43926 this.view = new Roo.View(this.list, this.tpl, {
43927 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43930 this.view.on('click', this.onViewClick, this);
43932 this.store.on('beforeload', this.onBeforeLoad, this);
43933 this.store.on('load', this.onLoad, this);
43934 this.store.on('loadexception', this.onLoadException, this);
43936 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43937 "up" : function(e){
43938 this.inKeyMode = true;
43942 "down" : function(e){
43943 if(!this.isExpanded()){
43944 this.onTriggerClick();
43946 this.inKeyMode = true;
43951 "enter" : function(e){
43954 if(this.fireEvent("specialkey", this, e)){
43955 this.onViewClick(false);
43961 "esc" : function(e){
43965 "tab" : function(e){
43968 if(this.fireEvent("specialkey", this, e)){
43969 this.onViewClick(false);
43977 doRelay : function(foo, bar, hname){
43978 if(hname == 'down' || this.scope.isExpanded()){
43979 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43987 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43991 initNumberEvent : function(e)
43993 this.inputEl().on("keydown" , this.fireKey, this);
43994 this.inputEl().on("focus", this.onFocus, this);
43995 this.inputEl().on("blur", this.onBlur, this);
43997 this.inputEl().relayEvent('keyup', this);
43999 if(this.indicator){
44000 this.indicator.addClass('invisible');
44003 this.originalValue = this.getValue();
44005 if(this.validationEvent == 'keyup'){
44006 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44007 this.inputEl().on('keyup', this.filterValidation, this);
44009 else if(this.validationEvent !== false){
44010 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44013 if(this.selectOnFocus){
44014 this.on("focus", this.preFocus, this);
44017 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44018 this.inputEl().on("keypress", this.filterKeys, this);
44020 this.inputEl().relayEvent('keypress', this);
44023 var allowed = "0123456789";
44025 if(this.allowDecimals){
44026 allowed += this.decimalSeparator;
44029 if(this.allowNegative){
44033 if(this.thousandsDelimiter) {
44037 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44039 var keyPress = function(e){
44041 var k = e.getKey();
44043 var c = e.getCharCode();
44046 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44047 allowed.indexOf(String.fromCharCode(c)) === -1
44053 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44057 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44062 this.inputEl().on("keypress", keyPress, this);
44066 onTriggerClick : function(e)
44073 this.loadNext = false;
44075 if(this.isExpanded()){
44080 this.hasFocus = true;
44082 if(this.triggerAction == 'all') {
44083 this.doQuery(this.allQuery, true);
44087 this.doQuery(this.getRawValue());
44090 getCurrency : function()
44092 var v = this.currencyEl().getValue();
44097 restrictHeight : function()
44099 this.list.alignTo(this.currencyEl(), this.listAlign);
44100 this.list.alignTo(this.currencyEl(), this.listAlign);
44103 onViewClick : function(view, doFocus, el, e)
44105 var index = this.view.getSelectedIndexes()[0];
44107 var r = this.store.getAt(index);
44110 this.onSelect(r, index);
44114 onSelect : function(record, index){
44116 if(this.fireEvent('beforeselect', this, record, index) !== false){
44118 this.setFromCurrencyData(index > -1 ? record.data : false);
44122 this.fireEvent('select', this, record, index);
44126 setFromCurrencyData : function(o)
44130 this.lastCurrency = o;
44132 if (this.currencyField) {
44133 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44135 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44138 this.lastSelectionText = currency;
44140 //setting default currency
44141 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44142 this.setCurrency(this.defaultCurrency);
44146 this.setCurrency(currency);
44149 setFromData : function(o)
44153 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44155 this.setFromCurrencyData(c);
44160 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44162 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44165 this.setValue(value);
44169 setCurrency : function(v)
44171 this.currencyValue = v;
44174 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44179 setValue : function(v)
44181 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44187 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44189 this.inputEl().dom.value = (v == '') ? '' :
44190 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44192 if(!this.allowZero && v === '0') {
44193 this.hiddenEl().dom.value = '';
44194 this.inputEl().dom.value = '';
44201 getRawValue : function()
44203 var v = this.inputEl().getValue();
44208 getValue : function()
44210 return this.fixPrecision(this.parseValue(this.getRawValue()));
44213 parseValue : function(value)
44215 if(this.thousandsDelimiter) {
44217 r = new RegExp(",", "g");
44218 value = value.replace(r, "");
44221 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44222 return isNaN(value) ? '' : value;
44226 fixPrecision : function(value)
44228 if(this.thousandsDelimiter) {
44230 r = new RegExp(",", "g");
44231 value = value.replace(r, "");
44234 var nan = isNaN(value);
44236 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44237 return nan ? '' : value;
44239 return parseFloat(value).toFixed(this.decimalPrecision);
44242 decimalPrecisionFcn : function(v)
44244 return Math.floor(v);
44247 validateValue : function(value)
44249 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44253 var num = this.parseValue(value);
44256 this.markInvalid(String.format(this.nanText, value));
44260 if(num < this.minValue){
44261 this.markInvalid(String.format(this.minText, this.minValue));
44265 if(num > this.maxValue){
44266 this.markInvalid(String.format(this.maxText, this.maxValue));
44273 validate : function()
44275 if(this.disabled || this.allowBlank){
44280 var currency = this.getCurrency();
44282 if(this.validateValue(this.getRawValue()) && currency.length){
44287 this.markInvalid();
44291 getName: function()
44296 beforeBlur : function()
44302 var v = this.parseValue(this.getRawValue());
44309 onBlur : function()
44313 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44314 //this.el.removeClass(this.focusClass);
44317 this.hasFocus = false;
44319 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44323 var v = this.getValue();
44325 if(String(v) !== String(this.startValue)){
44326 this.fireEvent('change', this, v, this.startValue);
44329 this.fireEvent("blur", this);
44332 inputEl : function()
44334 return this.el.select('.roo-money-amount-input', true).first();
44337 currencyEl : function()
44339 return this.el.select('.roo-money-currency-input', true).first();
44342 hiddenEl : function()
44344 return this.el.select('input.hidden-number-input',true).first();
44348 * @class Roo.bootstrap.BezierSignature
44349 * @extends Roo.bootstrap.Component
44350 * Bootstrap BezierSignature class
44351 * This script refer to:
44352 * Title: Signature Pad
44354 * Availability: https://github.com/szimek/signature_pad
44357 * Create a new BezierSignature
44358 * @param {Object} config The config object
44361 Roo.bootstrap.BezierSignature = function(config){
44362 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44368 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44375 mouse_btn_down: true,
44378 * @cfg {int} canvas height
44380 canvas_height: '200px',
44383 * @cfg {float|function} Radius of a single dot.
44388 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44393 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44398 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44403 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44408 * @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.
44410 bg_color: 'rgba(0, 0, 0, 0)',
44413 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44415 dot_color: 'black',
44418 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44420 velocity_filter_weight: 0.7,
44423 * @cfg {function} Callback when stroke begin.
44428 * @cfg {function} Callback when stroke end.
44432 getAutoCreate : function()
44434 var cls = 'roo-signature column';
44437 cls += ' ' + this.cls;
44447 for(var i = 0; i < col_sizes.length; i++) {
44448 if(this[col_sizes[i]]) {
44449 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44459 cls: 'roo-signature-body',
44463 cls: 'roo-signature-body-canvas',
44464 height: this.canvas_height,
44465 width: this.canvas_width
44472 style: 'display: none'
44480 initEvents: function()
44482 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44484 var canvas = this.canvasEl();
44486 // mouse && touch event swapping...
44487 canvas.dom.style.touchAction = 'none';
44488 canvas.dom.style.msTouchAction = 'none';
44490 this.mouse_btn_down = false;
44491 canvas.on('mousedown', this._handleMouseDown, this);
44492 canvas.on('mousemove', this._handleMouseMove, this);
44493 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44495 if (window.PointerEvent) {
44496 canvas.on('pointerdown', this._handleMouseDown, this);
44497 canvas.on('pointermove', this._handleMouseMove, this);
44498 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44501 if ('ontouchstart' in window) {
44502 canvas.on('touchstart', this._handleTouchStart, this);
44503 canvas.on('touchmove', this._handleTouchMove, this);
44504 canvas.on('touchend', this._handleTouchEnd, this);
44507 Roo.EventManager.onWindowResize(this.resize, this, true);
44509 // file input event
44510 this.fileEl().on('change', this.uploadImage, this);
44517 resize: function(){
44519 var canvas = this.canvasEl().dom;
44520 var ctx = this.canvasElCtx();
44521 var img_data = false;
44523 if(canvas.width > 0) {
44524 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44526 // setting canvas width will clean img data
44529 var style = window.getComputedStyle ?
44530 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44532 var padding_left = parseInt(style.paddingLeft) || 0;
44533 var padding_right = parseInt(style.paddingRight) || 0;
44535 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44538 ctx.putImageData(img_data, 0, 0);
44542 _handleMouseDown: function(e)
44544 if (e.browserEvent.which === 1) {
44545 this.mouse_btn_down = true;
44546 this.strokeBegin(e);
44550 _handleMouseMove: function (e)
44552 if (this.mouse_btn_down) {
44553 this.strokeMoveUpdate(e);
44557 _handleMouseUp: function (e)
44559 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44560 this.mouse_btn_down = false;
44565 _handleTouchStart: function (e) {
44567 e.preventDefault();
44568 if (e.browserEvent.targetTouches.length === 1) {
44569 // var touch = e.browserEvent.changedTouches[0];
44570 // this.strokeBegin(touch);
44572 this.strokeBegin(e); // assume e catching the correct xy...
44576 _handleTouchMove: function (e) {
44577 e.preventDefault();
44578 // var touch = event.targetTouches[0];
44579 // _this._strokeMoveUpdate(touch);
44580 this.strokeMoveUpdate(e);
44583 _handleTouchEnd: function (e) {
44584 var wasCanvasTouched = e.target === this.canvasEl().dom;
44585 if (wasCanvasTouched) {
44586 e.preventDefault();
44587 // var touch = event.changedTouches[0];
44588 // _this._strokeEnd(touch);
44593 reset: function () {
44594 this._lastPoints = [];
44595 this._lastVelocity = 0;
44596 this._lastWidth = (this.min_width + this.max_width) / 2;
44597 this.canvasElCtx().fillStyle = this.dot_color;
44600 strokeMoveUpdate: function(e)
44602 this.strokeUpdate(e);
44604 if (this.throttle) {
44605 this.throttleStroke(this.strokeUpdate, this.throttle);
44608 this.strokeUpdate(e);
44612 strokeBegin: function(e)
44614 var newPointGroup = {
44615 color: this.dot_color,
44619 if (typeof this.onBegin === 'function') {
44623 this.curve_data.push(newPointGroup);
44625 this.strokeUpdate(e);
44628 strokeUpdate: function(e)
44630 var rect = this.canvasEl().dom.getBoundingClientRect();
44631 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44632 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44633 var lastPoints = lastPointGroup.points;
44634 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44635 var isLastPointTooClose = lastPoint
44636 ? point.distanceTo(lastPoint) <= this.min_distance
44638 var color = lastPointGroup.color;
44639 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44640 var curve = this.addPoint(point);
44642 this.drawDot({color: color, point: point});
44645 this.drawCurve({color: color, curve: curve});
44655 strokeEnd: function(e)
44657 this.strokeUpdate(e);
44658 if (typeof this.onEnd === 'function') {
44663 addPoint: function (point) {
44664 var _lastPoints = this._lastPoints;
44665 _lastPoints.push(point);
44666 if (_lastPoints.length > 2) {
44667 if (_lastPoints.length === 3) {
44668 _lastPoints.unshift(_lastPoints[0]);
44670 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44671 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44672 _lastPoints.shift();
44678 calculateCurveWidths: function (startPoint, endPoint) {
44679 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44680 (1 - this.velocity_filter_weight) * this._lastVelocity;
44682 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44685 start: this._lastWidth
44688 this._lastVelocity = velocity;
44689 this._lastWidth = newWidth;
44693 drawDot: function (_a) {
44694 var color = _a.color, point = _a.point;
44695 var ctx = this.canvasElCtx();
44696 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44698 this.drawCurveSegment(point.x, point.y, width);
44700 ctx.fillStyle = color;
44704 drawCurve: function (_a) {
44705 var color = _a.color, curve = _a.curve;
44706 var ctx = this.canvasElCtx();
44707 var widthDelta = curve.endWidth - curve.startWidth;
44708 var drawSteps = Math.floor(curve.length()) * 2;
44710 ctx.fillStyle = color;
44711 for (var i = 0; i < drawSteps; i += 1) {
44712 var t = i / drawSteps;
44718 var x = uuu * curve.startPoint.x;
44719 x += 3 * uu * t * curve.control1.x;
44720 x += 3 * u * tt * curve.control2.x;
44721 x += ttt * curve.endPoint.x;
44722 var y = uuu * curve.startPoint.y;
44723 y += 3 * uu * t * curve.control1.y;
44724 y += 3 * u * tt * curve.control2.y;
44725 y += ttt * curve.endPoint.y;
44726 var width = curve.startWidth + ttt * widthDelta;
44727 this.drawCurveSegment(x, y, width);
44733 drawCurveSegment: function (x, y, width) {
44734 var ctx = this.canvasElCtx();
44736 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44737 this.is_empty = false;
44742 var ctx = this.canvasElCtx();
44743 var canvas = this.canvasEl().dom;
44744 ctx.fillStyle = this.bg_color;
44745 ctx.clearRect(0, 0, canvas.width, canvas.height);
44746 ctx.fillRect(0, 0, canvas.width, canvas.height);
44747 this.curve_data = [];
44749 this.is_empty = true;
44754 return this.el.select('input',true).first();
44757 canvasEl: function()
44759 return this.el.select('canvas',true).first();
44762 canvasElCtx: function()
44764 return this.el.select('canvas',true).first().dom.getContext('2d');
44767 getImage: function(type)
44769 if(this.is_empty) {
44774 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44777 drawFromImage: function(img_src)
44779 var img = new Image();
44781 img.onload = function(){
44782 this.canvasElCtx().drawImage(img, 0, 0);
44787 this.is_empty = false;
44790 selectImage: function()
44792 this.fileEl().dom.click();
44795 uploadImage: function(e)
44797 var reader = new FileReader();
44799 reader.onload = function(e){
44800 var img = new Image();
44801 img.onload = function(){
44803 this.canvasElCtx().drawImage(img, 0, 0);
44805 img.src = e.target.result;
44808 reader.readAsDataURL(e.target.files[0]);
44811 // Bezier Point Constructor
44812 Point: (function () {
44813 function Point(x, y, time) {
44816 this.time = time || Date.now();
44818 Point.prototype.distanceTo = function (start) {
44819 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44821 Point.prototype.equals = function (other) {
44822 return this.x === other.x && this.y === other.y && this.time === other.time;
44824 Point.prototype.velocityFrom = function (start) {
44825 return this.time !== start.time
44826 ? this.distanceTo(start) / (this.time - start.time)
44833 // Bezier Constructor
44834 Bezier: (function () {
44835 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44836 this.startPoint = startPoint;
44837 this.control2 = control2;
44838 this.control1 = control1;
44839 this.endPoint = endPoint;
44840 this.startWidth = startWidth;
44841 this.endWidth = endWidth;
44843 Bezier.fromPoints = function (points, widths, scope) {
44844 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44845 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44846 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44848 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44849 var dx1 = s1.x - s2.x;
44850 var dy1 = s1.y - s2.y;
44851 var dx2 = s2.x - s3.x;
44852 var dy2 = s2.y - s3.y;
44853 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44854 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44855 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44856 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44857 var dxm = m1.x - m2.x;
44858 var dym = m1.y - m2.y;
44859 var k = l2 / (l1 + l2);
44860 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44861 var tx = s2.x - cm.x;
44862 var ty = s2.y - cm.y;
44864 c1: new scope.Point(m1.x + tx, m1.y + ty),
44865 c2: new scope.Point(m2.x + tx, m2.y + ty)
44868 Bezier.prototype.length = function () {
44873 for (var i = 0; i <= steps; i += 1) {
44875 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44876 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44878 var xdiff = cx - px;
44879 var ydiff = cy - py;
44880 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44887 Bezier.prototype.point = function (t, start, c1, c2, end) {
44888 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44889 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44890 + (3.0 * c2 * (1.0 - t) * t * t)
44891 + (end * t * t * t);
44896 throttleStroke: function(fn, wait) {
44897 if (wait === void 0) { wait = 250; }
44899 var timeout = null;
44903 var later = function () {
44904 previous = Date.now();
44906 result = fn.apply(storedContext, storedArgs);
44908 storedContext = null;
44912 return function wrapper() {
44914 for (var _i = 0; _i < arguments.length; _i++) {
44915 args[_i] = arguments[_i];
44917 var now = Date.now();
44918 var remaining = wait - (now - previous);
44919 storedContext = this;
44921 if (remaining <= 0 || remaining > wait) {
44923 clearTimeout(timeout);
44927 result = fn.apply(storedContext, storedArgs);
44929 storedContext = null;
44933 else if (!timeout) {
44934 timeout = window.setTimeout(later, remaining);