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()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1953 * @cfg {String} title
1954 * @cfg {String} subtitle
1955 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1956 * @cfg {String} footer
1958 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960 * @cfg {String} margin (0|1|2|3|4|5|auto)
1961 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968 * @cfg {String} padding (0|1|2|3|4|5)
1969 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1970 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1971 * @cfg {String} padding_left (0|1|2|3|4|5)
1972 * @cfg {String} padding_right (0|1|2|3|4|5)
1973 * @cfg {String} padding_x (0|1|2|3|4|5)
1974 * @cfg {String} padding_y (0|1|2|3|4|5)
1976 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1977 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982 * @config {Boolean} dragable if this card can be dragged.
1983 * @config {String} drag_group group for drag
1984 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1985 * @config {String} drop_group group for drag
1987 * @config {Boolean} collapsable can the body be collapsed.
1988 * @config {Boolean} collapsed is the body collapsed when rendered...
1989 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1990 * @config {Boolean} rotated is the body rotated when rendered...
1993 * Create a new Container
1994 * @param {Object} config The config object
1997 Roo.bootstrap.Card = function(config){
1998 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2004 * When a element a card is dropped
2005 * @param {Roo.bootstrap.Card} this
2008 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2009 * @param {String} position 'above' or 'below'
2010 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2016 * When a element a card is rotate
2017 * @param {Roo.bootstrap.Element} this
2018 * @param {Roo.Element} n the node being dropped?
2019 * @param {Boolean} rotate status
2027 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2032 margin: '', /// may be better in component?
2062 collapsable : false,
2071 childContainer : false,
2072 dropEl : false, /// the dom placeholde element that indicates drop location.
2073 containerEl: false, // body container
2074 bodyEl: false, // card-body
2075 headerContainerEl : false, //
2078 layoutCls : function()
2082 Roo.log(this.margin_bottom.length);
2083 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2084 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2087 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2089 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2090 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2094 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2095 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2096 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2100 // more generic support?
2108 // Roo.log("Call onRender: " + this.xtype);
2109 /* We are looking at something like this.
2111 <img src="..." class="card-img-top" alt="...">
2112 <div class="card-body">
2113 <h5 class="card-title">Card title</h5>
2114 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116 >> this bit is really the body...
2117 <div> << we will ad dthis in hopefully it will not break shit.
2119 ** card text does not actually have any styling...
2121 <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>
2124 <a href="#" class="card-link">Card link</a>
2127 <div class="card-footer">
2128 <small class="text-muted">Last updated 3 mins ago</small>
2132 getAutoCreate : function(){
2140 if (this.weight.length && this.weight != 'light') {
2141 cfg.cls += ' text-white';
2143 cfg.cls += ' text-dark'; // need as it's nested..
2145 if (this.weight.length) {
2146 cfg.cls += ' bg-' + this.weight;
2149 cfg.cls += this.layoutCls();
2152 var hdr_ctr = false;
2153 if (this.header.length) {
2155 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2156 cls : 'card-header',
2164 cls : 'card-header d-none',
2170 if (this.collapsable) {
2173 cls : 'd-block user-select-none',
2177 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2182 hdr.cn.push(hdr_ctr);
2187 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2192 if (this.header_image.length) {
2195 cls : 'card-img-top',
2196 src: this.header_image // escape?
2201 cls : 'card-img-top d-none'
2207 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2211 if (this.collapsable || this.rotateable) {
2214 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2221 if (this.title.length) {
2225 src: this.title // escape?
2229 if (this.subtitle.length) {
2233 src: this.subtitle // escape?
2239 cls : 'roo-card-body-ctr'
2242 if (this.html.length) {
2248 // fixme ? handle objects?
2250 if (this.footer.length) {
2253 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2258 cfg.cn.push({cls : 'card-footer d-none'});
2267 getCardHeader : function()
2269 var ret = this.el.select('.card-header',true).first();
2270 if (ret.hasClass('d-none')) {
2271 ret.removeClass('d-none');
2276 getCardFooter : function()
2278 var ret = this.el.select('.card-footer',true).first();
2279 if (ret.hasClass('d-none')) {
2280 ret.removeClass('d-none');
2285 getCardImageTop : function()
2287 var ret = this.el.select('.card-img-top',true).first();
2288 if (ret.hasClass('d-none')) {
2289 ret.removeClass('d-none');
2295 getChildContainer : function()
2301 return this.el.select('.roo-card-body-ctr',true).first();
2304 initEvents: function()
2306 this.bodyEl = this.el.select('.card-body',true).first();
2307 this.containerEl = this.getChildContainer();
2309 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2310 containerScroll: true,
2311 ddGroup: this.drag_group || 'default_card_drag_group'
2313 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315 if (this.dropable) {
2316 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2317 containerScroll: true,
2318 ddGroup: this.drop_group || 'default_card_drag_group'
2320 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2321 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2322 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2323 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2324 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2327 if (this.collapsable) {
2328 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330 if (this.rotateable) {
2331 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333 this.collapsableEl = this.el.select('.roo-collapsable').first();
2335 this.footerEl = this.el.select('.card-footer').first();
2336 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2337 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2338 this.headerEl = this.el.select('.card-header',true).first();
2341 this.el.addClass('roo-card-rotated');
2342 this.fireEvent('rotate', this, true);
2346 getDragData : function(e)
2348 var target = this.getEl();
2350 //this.handleSelection(e);
2355 nodes: this.getEl(),
2360 dragData.ddel = target.dom ; // the div element
2361 Roo.log(target.getWidth( ));
2362 dragData.ddel.style.width = target.getWidth() + 'px';
2369 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2370 * whole Element becomes the target, and this causes the drop gesture to append.
2372 getTargetFromEvent : function(e, dragged_card_el)
2374 var target = e.getTarget();
2375 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2376 target = target.parentNode;
2387 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2388 // see if target is one of the 'cards'...
2391 //Roo.log(this.items.length);
2394 var last_card_n = 0;
2396 for (var i = 0;i< this.items.length;i++) {
2398 if (!this.items[i].el.hasClass('card')) {
2401 pos = this.getDropPoint(e, this.items[i].el.dom);
2403 cards_len = ret.cards.length;
2404 //Roo.log(this.items[i].el.dom.id);
2405 ret.cards.push(this.items[i]);
2407 if (ret.card_n < 0 && pos == 'above') {
2408 ret.position = cards_len > 0 ? 'below' : pos;
2409 ret.items_n = i > 0 ? i - 1 : 0;
2410 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2411 ret.card = ret.cards[ret.card_n];
2414 if (!ret.cards.length) {
2416 ret.position = 'below';
2420 // could not find a card.. stick it at the end..
2421 if (ret.card_n < 0) {
2422 ret.card_n = last_card_n;
2423 ret.card = ret.cards[last_card_n];
2424 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2425 ret.position = 'below';
2428 if (this.items[ret.items_n].el == dragged_card_el) {
2432 if (ret.position == 'below') {
2433 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435 if (card_after && card_after.el == dragged_card_el) {
2442 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444 if (card_before && card_before.el == dragged_card_el) {
2451 onNodeEnter : function(n, dd, e, data){
2454 onNodeOver : function(n, dd, e, data)
2457 var target_info = this.getTargetFromEvent(e,data.source.el);
2458 if (target_info === false) {
2459 this.dropPlaceHolder('hide');
2462 Roo.log(['getTargetFromEvent', target_info ]);
2465 this.dropPlaceHolder('show', target_info,data);
2469 onNodeOut : function(n, dd, e, data){
2470 this.dropPlaceHolder('hide');
2473 onNodeDrop : function(n, dd, e, data)
2476 // call drop - return false if
2478 // this could actually fail - if the Network drops..
2479 // we will ignore this at present..- client should probably reload
2480 // the whole set of cards if stuff like that fails.
2483 var info = this.getTargetFromEvent(e,data.source.el);
2484 if (info === false) {
2487 this.dropPlaceHolder('hide');
2493 this.acceptCard(data.source, info.position, info.card, info.items_n);
2497 firstChildCard : function()
2499 for (var i = 0;i< this.items.length;i++) {
2501 if (!this.items[i].el.hasClass('card')) {
2504 return this.items[i];
2506 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2511 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2513 acceptCard : function(move_card, position, next_to_card )
2515 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2519 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 var dom = move_card.el.dom;
2523 dom.parentNode.removeChild(dom);
2524 dom.style.width = ''; // clear with - which is set by drag.
2526 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2527 var cardel = next_to_card.el.dom;
2529 if (position == 'above' ) {
2530 cardel.parentNode.insertBefore(dom, cardel);
2531 } else if (cardel.nextSibling) {
2532 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534 cardel.parentNode.append(dom);
2537 // card container???
2538 this.containerEl.dom.append(dom);
2541 //FIXME HANDLE card = true
2543 // add this to the correct place in items.
2547 // remove Card from items.
2549 var old_parent = move_card.parent();
2551 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2579 /** Decide whether to drop above or below a View node. */
2580 getDropPoint : function(e, n, dd)
2585 if (n == this.containerEl.dom) {
2588 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2589 var c = t + (b - t) / 2;
2590 var y = Roo.lib.Event.getPageY(e);
2597 onToggleCollapse : function(e)
2599 if (this.collapsed) {
2600 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2601 this.collapsableEl.addClass('show');
2602 this.collapsed = false;
2605 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2606 this.collapsableEl.removeClass('show');
2607 this.collapsed = true;
2612 onToggleRotate : function(e)
2614 this.collapsableEl.removeClass('show');
2615 this.footerEl.removeClass('d-none');
2616 this.el.removeClass('roo-card-rotated');
2617 this.el.removeClass('d-none');
2620 this.collapsableEl.addClass('show');
2621 this.rotated = false;
2622 this.fireEvent('rotate', this, this.rotated);
2625 this.el.addClass('roo-card-rotated');
2626 this.footerEl.addClass('d-none');
2627 this.el.select('.roo-collapsable').removeClass('show');
2629 this.rotated = true;
2630 this.fireEvent('rotate', this, this.rotated);
2634 dropPlaceHolder: function (action, info, data)
2636 if (this.dropEl === false) {
2637 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2641 this.dropEl.removeClass(['d-none', 'd-block']);
2642 if (action == 'hide') {
2644 this.dropEl.addClass('d-none');
2647 // FIXME - info.card == true!!!
2648 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650 if (info.card !== true) {
2651 var cardel = info.card.el.dom;
2653 if (info.position == 'above') {
2654 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2655 } else if (cardel.nextSibling) {
2656 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658 cardel.parentNode.append(this.dropEl.dom);
2661 // card container???
2662 this.containerEl.dom.append(this.dropEl.dom);
2665 this.dropEl.addClass('d-block roo-card-dropzone');
2667 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674 setHeaderText: function(html)
2676 this.headerContainerEl.dom.innerHTML = html;
2685 * Card header - holder for the card header elements.
2690 * @class Roo.bootstrap.CardHeader
2691 * @extends Roo.bootstrap.Element
2692 * Bootstrap CardHeader class
2694 * Create a new Card Header - that you can embed children into
2695 * @param {Object} config The config object
2698 Roo.bootstrap.CardHeader = function(config){
2699 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2702 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2705 container_method : 'getCardHeader'
2718 * Card footer - holder for the card footer elements.
2723 * @class Roo.bootstrap.CardFooter
2724 * @extends Roo.bootstrap.Element
2725 * Bootstrap CardFooter class
2727 * Create a new Card Footer - that you can embed children into
2728 * @param {Object} config The config object
2731 Roo.bootstrap.CardFooter = function(config){
2732 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2735 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2738 container_method : 'getCardFooter'
2751 * Card header - holder for the card header elements.
2756 * @class Roo.bootstrap.CardImageTop
2757 * @extends Roo.bootstrap.Element
2758 * Bootstrap CardImageTop class
2760 * Create a new Card Image Top container
2761 * @param {Object} config The config object
2764 Roo.bootstrap.CardImageTop = function(config){
2765 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2768 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2771 container_method : 'getCardImageTop'
2789 * @class Roo.bootstrap.Img
2790 * @extends Roo.bootstrap.Component
2791 * Bootstrap Img class
2792 * @cfg {Boolean} imgResponsive false | true
2793 * @cfg {String} border rounded | circle | thumbnail
2794 * @cfg {String} src image source
2795 * @cfg {String} alt image alternative text
2796 * @cfg {String} href a tag href
2797 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2798 * @cfg {String} xsUrl xs image source
2799 * @cfg {String} smUrl sm image source
2800 * @cfg {String} mdUrl md image source
2801 * @cfg {String} lgUrl lg image source
2804 * Create a new Input
2805 * @param {Object} config The config object
2808 Roo.bootstrap.Img = function(config){
2809 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2815 * The img click event for the img.
2816 * @param {Roo.EventObject} e
2822 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2824 imgResponsive: true,
2834 getAutoCreate : function()
2836 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2837 return this.createSingleImg();
2842 cls: 'roo-image-responsive-group',
2847 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849 if(!_this[size + 'Url']){
2855 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2856 html: _this.html || cfg.html,
2857 src: _this[size + 'Url']
2860 img.cls += ' roo-image-responsive-' + size;
2862 var s = ['xs', 'sm', 'md', 'lg'];
2864 s.splice(s.indexOf(size), 1);
2866 Roo.each(s, function(ss){
2867 img.cls += ' hidden-' + ss;
2870 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2871 cfg.cls += ' img-' + _this.border;
2875 cfg.alt = _this.alt;
2888 a.target = _this.target;
2892 cfg.cn.push((_this.href) ? a : img);
2899 createSingleImg : function()
2903 cls: (this.imgResponsive) ? 'img-responsive' : '',
2905 src : 'about:blank' // just incase src get's set to undefined?!?
2908 cfg.html = this.html || cfg.html;
2910 cfg.src = this.src || cfg.src;
2912 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2913 cfg.cls += ' img-' + this.border;
2930 a.target = this.target;
2935 return (this.href) ? a : cfg;
2938 initEvents: function()
2941 this.el.on('click', this.onClick, this);
2946 onClick : function(e)
2948 Roo.log('img onclick');
2949 this.fireEvent('click', this, e);
2952 * Sets the url of the image - used to update it
2953 * @param {String} url the url of the image
2956 setSrc : function(url)
2960 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2961 this.el.dom.src = url;
2965 this.el.select('img', true).first().dom.src = url;
2981 * @class Roo.bootstrap.Link
2982 * @extends Roo.bootstrap.Component
2983 * Bootstrap Link Class
2984 * @cfg {String} alt image alternative text
2985 * @cfg {String} href a tag href
2986 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2987 * @cfg {String} html the content of the link.
2988 * @cfg {String} anchor name for the anchor link
2989 * @cfg {String} fa - favicon
2991 * @cfg {Boolean} preventDefault (true | false) default false
2995 * Create a new Input
2996 * @param {Object} config The config object
2999 Roo.bootstrap.Link = function(config){
3000 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3006 * The img click event for the img.
3007 * @param {Roo.EventObject} e
3013 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3017 preventDefault: false,
3023 getAutoCreate : function()
3025 var html = this.html || '';
3027 if (this.fa !== false) {
3028 html = '<i class="fa fa-' + this.fa + '"></i>';
3033 // anchor's do not require html/href...
3034 if (this.anchor === false) {
3036 cfg.href = this.href || '#';
3038 cfg.name = this.anchor;
3039 if (this.html !== false || this.fa !== false) {
3042 if (this.href !== false) {
3043 cfg.href = this.href;
3047 if(this.alt !== false){
3052 if(this.target !== false) {
3053 cfg.target = this.target;
3059 initEvents: function() {
3061 if(!this.href || this.preventDefault){
3062 this.el.on('click', this.onClick, this);
3066 onClick : function(e)
3068 if(this.preventDefault){
3071 //Roo.log('img onclick');
3072 this.fireEvent('click', this, e);
3085 * @class Roo.bootstrap.Header
3086 * @extends Roo.bootstrap.Component
3087 * Bootstrap Header class
3088 * @cfg {String} html content of header
3089 * @cfg {Number} level (1|2|3|4|5|6) default 1
3092 * Create a new Header
3093 * @param {Object} config The config object
3097 Roo.bootstrap.Header = function(config){
3098 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3101 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3109 getAutoCreate : function(){
3114 tag: 'h' + (1 *this.level),
3115 html: this.html || ''
3127 * Ext JS Library 1.1.1
3128 * Copyright(c) 2006-2007, Ext JS, LLC.
3130 * Originally Released Under LGPL - original licence link has changed is not relivant.
3133 * <script type="text/javascript">
3137 * @class Roo.bootstrap.MenuMgr
3138 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3141 Roo.bootstrap.MenuMgr = function(){
3142 var menus, active, groups = {}, attached = false, lastShow = new Date();
3144 // private - called when first menu is created
3147 active = new Roo.util.MixedCollection();
3148 Roo.get(document).addKeyListener(27, function(){
3149 if(active.length > 0){
3157 if(active && active.length > 0){
3158 var c = active.clone();
3168 if(active.length < 1){
3169 Roo.get(document).un("mouseup", onMouseDown);
3177 var last = active.last();
3178 lastShow = new Date();
3181 Roo.get(document).on("mouseup", onMouseDown);
3186 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3187 m.parentMenu.activeChild = m;
3188 }else if(last && last.isVisible()){
3189 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3194 function onBeforeHide(m){
3196 m.activeChild.hide();
3198 if(m.autoHideTimer){
3199 clearTimeout(m.autoHideTimer);
3200 delete m.autoHideTimer;
3205 function onBeforeShow(m){
3206 var pm = m.parentMenu;
3207 if(!pm && !m.allowOtherMenus){
3209 }else if(pm && pm.activeChild && active != m){
3210 pm.activeChild.hide();
3214 // private this should really trigger on mouseup..
3215 function onMouseDown(e){
3216 Roo.log("on Mouse Up");
3218 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3219 Roo.log("MenuManager hideAll");
3228 function onBeforeCheck(mi, state){
3230 var g = groups[mi.group];
3231 for(var i = 0, l = g.length; i < l; i++){
3233 g[i].setChecked(false);
3242 * Hides all menus that are currently visible
3244 hideAll : function(){
3249 register : function(menu){
3253 menus[menu.id] = menu;
3254 menu.on("beforehide", onBeforeHide);
3255 menu.on("hide", onHide);
3256 menu.on("beforeshow", onBeforeShow);
3257 menu.on("show", onShow);
3259 if(g && menu.events["checkchange"]){
3263 groups[g].push(menu);
3264 menu.on("checkchange", onCheck);
3269 * Returns a {@link Roo.menu.Menu} object
3270 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3271 * be used to generate and return a new Menu instance.
3273 get : function(menu){
3274 if(typeof menu == "string"){ // menu id
3276 }else if(menu.events){ // menu instance
3279 /*else if(typeof menu.length == 'number'){ // array of menu items?
3280 return new Roo.bootstrap.Menu({items:menu});
3281 }else{ // otherwise, must be a config
3282 return new Roo.bootstrap.Menu(menu);
3289 unregister : function(menu){
3290 delete menus[menu.id];
3291 menu.un("beforehide", onBeforeHide);
3292 menu.un("hide", onHide);
3293 menu.un("beforeshow", onBeforeShow);
3294 menu.un("show", onShow);
3296 if(g && menu.events["checkchange"]){
3297 groups[g].remove(menu);
3298 menu.un("checkchange", onCheck);
3303 registerCheckable : function(menuItem){
3304 var g = menuItem.group;
3309 groups[g].push(menuItem);
3310 menuItem.on("beforecheckchange", onBeforeCheck);
3315 unregisterCheckable : function(menuItem){
3316 var g = menuItem.group;
3318 groups[g].remove(menuItem);
3319 menuItem.un("beforecheckchange", onBeforeCheck);
3331 * @class Roo.bootstrap.Menu
3332 * @extends Roo.bootstrap.Component
3333 * Bootstrap Menu class - container for MenuItems
3334 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3335 * @cfg {bool} hidden if the menu should be hidden when rendered.
3336 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3337 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3341 * @param {Object} config The config object
3345 Roo.bootstrap.Menu = function(config){
3346 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3347 if (this.registerMenu && this.type != 'treeview') {
3348 Roo.bootstrap.MenuMgr.register(this);
3355 * Fires before this menu is displayed (return false to block)
3356 * @param {Roo.menu.Menu} this
3361 * Fires before this menu is hidden (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires after this menu is displayed
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is hidden
3374 * @param {Roo.menu.Menu} this
3379 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3380 * @param {Roo.menu.Menu} this
3381 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3382 * @param {Roo.EventObject} e
3387 * Fires when the mouse is hovering over this menu
3388 * @param {Roo.menu.Menu} this
3389 * @param {Roo.EventObject} e
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3395 * Fires when the mouse exits this menu
3396 * @param {Roo.menu.Menu} this
3397 * @param {Roo.EventObject} e
3398 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3403 * Fires when a menu item contained in this menu is clicked
3404 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3405 * @param {Roo.EventObject} e
3409 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3412 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3416 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3419 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421 registerMenu : true,
3423 menuItems :false, // stores the menu items..
3433 getChildContainer : function() {
3437 getAutoCreate : function(){
3439 //if (['right'].indexOf(this.align)!==-1) {
3440 // cfg.cn[1].cls += ' pull-right'
3446 cls : 'dropdown-menu' ,
3447 style : 'z-index:1000'
3451 if (this.type === 'submenu') {
3452 cfg.cls = 'submenu active';
3454 if (this.type === 'treeview') {
3455 cfg.cls = 'treeview-menu';
3460 initEvents : function() {
3462 // Roo.log("ADD event");
3463 // Roo.log(this.triggerEl.dom);
3465 this.triggerEl.on('click', this.onTriggerClick, this);
3467 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3470 if (this.triggerEl.hasClass('nav-item')) {
3471 // dropdown toggle on the 'a' in BS4?
3472 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474 this.triggerEl.addClass('dropdown-toggle');
3477 this.el.on('touchstart' , this.onTouch, this);
3479 this.el.on('click' , this.onClick, this);
3481 this.el.on("mouseover", this.onMouseOver, this);
3482 this.el.on("mouseout", this.onMouseOut, this);
3486 findTargetItem : function(e)
3488 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3492 //Roo.log(t); Roo.log(t.id);
3494 //Roo.log(this.menuitems);
3495 return this.menuitems.get(t.id);
3497 //return this.items.get(t.menuItemId);
3503 onTouch : function(e)
3505 Roo.log("menu.onTouch");
3506 //e.stopEvent(); this make the user popdown broken
3510 onClick : function(e)
3512 Roo.log("menu.onClick");
3514 var t = this.findTargetItem(e);
3515 if(!t || t.isContainer){
3520 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3521 if(t == this.activeItem && t.shouldDeactivate(e)){
3522 this.activeItem.deactivate();
3523 delete this.activeItem;
3527 this.setActiveItem(t, true);
3535 Roo.log('pass click event');
3539 this.fireEvent("click", this, t, e);
3543 if(!t.href.length || t.href == '#'){
3544 (function() { _this.hide(); }).defer(100);
3549 onMouseOver : function(e){
3550 var t = this.findTargetItem(e);
3553 // if(t.canActivate && !t.disabled){
3554 // this.setActiveItem(t, true);
3558 this.fireEvent("mouseover", this, e, t);
3560 isVisible : function(){
3561 return !this.hidden;
3563 onMouseOut : function(e){
3564 var t = this.findTargetItem(e);
3567 // if(t == this.activeItem && t.shouldDeactivate(e)){
3568 // this.activeItem.deactivate();
3569 // delete this.activeItem;
3572 this.fireEvent("mouseout", this, e, t);
3577 * Displays this menu relative to another element
3578 * @param {String/HTMLElement/Roo.Element} element The element to align to
3579 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3580 * the element (defaults to this.defaultAlign)
3581 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583 show : function(el, pos, parentMenu)
3585 if (false === this.fireEvent("beforeshow", this)) {
3586 Roo.log("show canceled");
3589 this.parentMenu = parentMenu;
3594 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3597 * Displays this menu at a specific xy position
3598 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3599 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601 showAt : function(xy, parentMenu, /* private: */_e){
3602 this.parentMenu = parentMenu;
3607 this.fireEvent("beforeshow", this);
3608 //xy = this.el.adjustForConstraints(xy);
3612 this.hideMenuItems();
3613 this.hidden = false;
3614 this.triggerEl.addClass('open');
3615 this.el.addClass('show');
3617 // reassign x when hitting right
3618 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3619 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3622 // reassign y when hitting bottom
3623 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3624 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3627 // but the list may align on trigger left or trigger top... should it be a properity?
3629 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3634 this.fireEvent("show", this);
3640 this.doFocus.defer(50, this);
3644 doFocus : function(){
3646 this.focusEl.focus();
3651 * Hides this menu and optionally all parent menus
3652 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654 hide : function(deep)
3656 if (false === this.fireEvent("beforehide", this)) {
3657 Roo.log("hide canceled");
3660 this.hideMenuItems();
3661 if(this.el && this.isVisible()){
3663 if(this.activeItem){
3664 this.activeItem.deactivate();
3665 this.activeItem = null;
3667 this.triggerEl.removeClass('open');;
3668 this.el.removeClass('show');
3670 this.fireEvent("hide", this);
3672 if(deep === true && this.parentMenu){
3673 this.parentMenu.hide(true);
3677 onTriggerClick : function(e)
3679 Roo.log('trigger click');
3681 var target = e.getTarget();
3683 Roo.log(target.nodeName.toLowerCase());
3685 if(target.nodeName.toLowerCase() === 'i'){
3691 onTriggerPress : function(e)
3693 Roo.log('trigger press');
3694 //Roo.log(e.getTarget());
3695 // Roo.log(this.triggerEl.dom);
3697 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3698 var pel = Roo.get(e.getTarget());
3699 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3700 Roo.log('is treeview or dropdown?');
3704 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3708 if (this.isVisible()) {
3713 this.show(this.triggerEl, '?', false);
3716 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723 hideMenuItems : function()
3725 Roo.log("hide Menu Items");
3730 this.el.select('.open',true).each(function(aa) {
3732 aa.removeClass('open');
3736 addxtypeChild : function (tree, cntr) {
3737 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739 this.menuitems.add(comp);
3751 this.getEl().dom.innerHTML = '';
3752 this.menuitems.clear();
3766 * @class Roo.bootstrap.MenuItem
3767 * @extends Roo.bootstrap.Component
3768 * Bootstrap MenuItem class
3769 * @cfg {String} html the menu label
3770 * @cfg {String} href the link
3771 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3772 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3773 * @cfg {Boolean} active used on sidebars to highlight active itesm
3774 * @cfg {String} fa favicon to show on left of menu item.
3775 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3779 * Create a new MenuItem
3780 * @param {Object} config The config object
3784 Roo.bootstrap.MenuItem = function(config){
3785 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3790 * The raw click event for the entire grid.
3791 * @param {Roo.bootstrap.MenuItem} this
3792 * @param {Roo.EventObject} e
3798 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3802 preventDefault: false,
3803 isContainer : false,
3807 getAutoCreate : function(){
3809 if(this.isContainer){
3812 cls: 'dropdown-menu-item '
3822 cls : 'dropdown-item',
3827 if (this.fa !== false) {
3830 cls : 'fa fa-' + this.fa
3839 cls: 'dropdown-menu-item',
3842 if (this.parent().type == 'treeview') {
3843 cfg.cls = 'treeview-menu';
3846 cfg.cls += ' active';
3851 anc.href = this.href || cfg.cn[0].href ;
3852 ctag.html = this.html || cfg.cn[0].html ;
3856 initEvents: function()
3858 if (this.parent().type == 'treeview') {
3859 this.el.select('a').on('click', this.onClick, this);
3863 this.menu.parentType = this.xtype;
3864 this.menu.triggerEl = this.el;
3865 this.menu = this.addxtype(Roo.apply({}, this.menu));
3869 onClick : function(e)
3871 Roo.log('item on click ');
3873 if(this.preventDefault){
3876 //this.parent().hideMenuItems();
3878 this.fireEvent('click', this, e);
3897 * @class Roo.bootstrap.MenuSeparator
3898 * @extends Roo.bootstrap.Component
3899 * Bootstrap MenuSeparator class
3902 * Create a new MenuItem
3903 * @param {Object} config The config object
3907 Roo.bootstrap.MenuSeparator = function(config){
3908 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3911 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3913 getAutoCreate : function(){
3932 * @class Roo.bootstrap.Modal
3933 * @extends Roo.bootstrap.Component
3934 * Bootstrap Modal class
3935 * @cfg {String} title Title of dialog
3936 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3937 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3938 * @cfg {Boolean} specificTitle default false
3939 * @cfg {Array} buttons Array of buttons or standard button set..
3940 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3941 * @cfg {Boolean} animate default true
3942 * @cfg {Boolean} allow_close default true
3943 * @cfg {Boolean} fitwindow default false
3944 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3945 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3946 * @cfg {String} size (sm|lg|xl) default empty
3947 * @cfg {Number} max_width set the max width of modal
3948 * @cfg {Boolean} editableTitle can the title be edited
3953 * Create a new Modal Dialog
3954 * @param {Object} config The config object
3957 Roo.bootstrap.Modal = function(config){
3958 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3963 * The raw btnclick event for the button
3964 * @param {Roo.EventObject} e
3969 * Fire when dialog resize
3970 * @param {Roo.bootstrap.Modal} this
3971 * @param {Roo.EventObject} e
3975 * @event titlechanged
3976 * Fire when the editable title has been changed
3977 * @param {Roo.bootstrap.Modal} this
3978 * @param {Roo.EventObject} value
3980 "titlechanged" : true
3983 this.buttons = this.buttons || [];
3986 this.tmpl = Roo.factory(this.tmpl);
3991 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3993 title : 'test dialog',
4003 specificTitle: false,
4005 buttonPosition: 'right',
4027 editableTitle : false,
4029 onRender : function(ct, position)
4031 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4034 var cfg = Roo.apply({}, this.getAutoCreate());
4037 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039 //if (!cfg.name.length) {
4043 cfg.cls += ' ' + this.cls;
4046 cfg.style = this.style;
4048 this.el = Roo.get(document.body).createChild(cfg, position);
4050 //var type = this.el.dom.type;
4053 if(this.tabIndex !== undefined){
4054 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4057 this.dialogEl = this.el.select('.modal-dialog',true).first();
4058 this.bodyEl = this.el.select('.modal-body',true).first();
4059 this.closeEl = this.el.select('.modal-header .close', true).first();
4060 this.headerEl = this.el.select('.modal-header',true).first();
4061 this.titleEl = this.el.select('.modal-title',true).first();
4062 this.footerEl = this.el.select('.modal-footer',true).first();
4064 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066 //this.el.addClass("x-dlg-modal");
4068 if (this.buttons.length) {
4069 Roo.each(this.buttons, function(bb) {
4070 var b = Roo.apply({}, bb);
4071 b.xns = b.xns || Roo.bootstrap;
4072 b.xtype = b.xtype || 'Button';
4073 if (typeof(b.listeners) == 'undefined') {
4074 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4077 var btn = Roo.factory(b);
4079 btn.render(this.getButtonContainer());
4083 // render the children.
4086 if(typeof(this.items) != 'undefined'){
4087 var items = this.items;
4090 for(var i =0;i < items.length;i++) {
4091 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4095 this.items = nitems;
4097 // where are these used - they used to be body/close/footer
4101 //this.el.addClass([this.fieldClass, this.cls]);
4105 getAutoCreate : function()
4107 // we will default to modal-body-overflow - might need to remove or make optional later.
4109 cls : 'modal-body enable-modal-body-overflow ',
4110 html : this.html || ''
4115 cls : 'modal-title',
4119 if(this.specificTitle){ // WTF is this?
4124 if (this.allow_close && Roo.bootstrap.version == 3) {
4134 if (this.editableTitle) {
4136 cls: 'form-control roo-editable-title d-none',
4142 if (this.allow_close && Roo.bootstrap.version == 4) {
4152 if(this.size.length){
4153 size = 'modal-' + this.size;
4156 var footer = Roo.bootstrap.version == 3 ?
4158 cls : 'modal-footer',
4162 cls: 'btn-' + this.buttonPosition
4167 { // BS4 uses mr-auto on left buttons....
4168 cls : 'modal-footer'
4179 cls: "modal-dialog " + size,
4182 cls : "modal-content",
4185 cls : 'modal-header',
4200 modal.cls += ' fade';
4206 getChildContainer : function() {
4211 getButtonContainer : function() {
4213 return Roo.bootstrap.version == 4 ?
4214 this.el.select('.modal-footer',true).first()
4215 : this.el.select('.modal-footer div',true).first();
4218 initEvents : function()
4220 if (this.allow_close) {
4221 this.closeEl.on('click', this.hide, this);
4223 Roo.EventManager.onWindowResize(this.resize, this, true);
4224 if (this.editableTitle) {
4225 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4226 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4227 this.headerEditEl.on('keyup', function(e) {
4228 if(e.isNavKeyPress()){
4229 this.toggleHeaderInput(false)
4232 this.headerEditEl.on('blur', function(e) {
4233 this.toggleHeaderInput(false)
4242 this.maskEl.setSize(
4243 Roo.lib.Dom.getViewWidth(true),
4244 Roo.lib.Dom.getViewHeight(true)
4247 if (this.fitwindow) {
4251 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4252 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4257 if(this.max_width !== 0) {
4259 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4262 this.setSize(w, this.height);
4266 if(this.max_height) {
4267 this.setSize(w,Math.min(
4269 Roo.lib.Dom.getViewportHeight(true) - 60
4275 if(!this.fit_content) {
4276 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4280 this.setSize(w, Math.min(
4282 this.headerEl.getHeight() +
4283 this.footerEl.getHeight() +
4284 this.getChildHeight(this.bodyEl.dom.childNodes),
4285 Roo.lib.Dom.getViewportHeight(true) - 60)
4291 setSize : function(w,h)
4302 if (!this.rendered) {
4306 //this.el.setStyle('display', 'block');
4307 this.el.removeClass('hideing');
4308 this.el.dom.style.display='block';
4310 Roo.get(document.body).addClass('modal-open');
4312 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4315 this.el.addClass('show');
4316 this.el.addClass('in');
4319 this.el.addClass('show');
4320 this.el.addClass('in');
4323 // not sure how we can show data in here..
4325 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4328 Roo.get(document.body).addClass("x-body-masked");
4330 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4331 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4332 this.maskEl.dom.style.display = 'block';
4333 this.maskEl.addClass('show');
4338 this.fireEvent('show', this);
4340 // set zindex here - otherwise it appears to be ignored...
4341 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4344 this.items.forEach( function(e) {
4345 e.layout ? e.layout() : false;
4353 if(this.fireEvent("beforehide", this) !== false){
4355 this.maskEl.removeClass('show');
4357 this.maskEl.dom.style.display = '';
4358 Roo.get(document.body).removeClass("x-body-masked");
4359 this.el.removeClass('in');
4360 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362 if(this.animate){ // why
4363 this.el.addClass('hideing');
4364 this.el.removeClass('show');
4366 if (!this.el.hasClass('hideing')) {
4367 return; // it's been shown again...
4370 this.el.dom.style.display='';
4372 Roo.get(document.body).removeClass('modal-open');
4373 this.el.removeClass('hideing');
4377 this.el.removeClass('show');
4378 this.el.dom.style.display='';
4379 Roo.get(document.body).removeClass('modal-open');
4382 this.fireEvent('hide', this);
4385 isVisible : function()
4388 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4392 addButton : function(str, cb)
4396 var b = Roo.apply({}, { html : str } );
4397 b.xns = b.xns || Roo.bootstrap;
4398 b.xtype = b.xtype || 'Button';
4399 if (typeof(b.listeners) == 'undefined') {
4400 b.listeners = { click : cb.createDelegate(this) };
4403 var btn = Roo.factory(b);
4405 btn.render(this.getButtonContainer());
4411 setDefaultButton : function(btn)
4413 //this.el.select('.modal-footer').()
4416 resizeTo: function(w,h)
4418 this.dialogEl.setWidth(w);
4420 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4422 this.bodyEl.setHeight(h - diff);
4424 this.fireEvent('resize', this);
4427 setContentSize : function(w, h)
4431 onButtonClick: function(btn,e)
4434 this.fireEvent('btnclick', btn.name, e);
4437 * Set the title of the Dialog
4438 * @param {String} str new Title
4440 setTitle: function(str) {
4441 this.titleEl.dom.innerHTML = str;
4445 * Set the body of the Dialog
4446 * @param {String} str new Title
4448 setBody: function(str) {
4449 this.bodyEl.dom.innerHTML = str;
4452 * Set the body of the Dialog using the template
4453 * @param {Obj} data - apply this data to the template and replace the body contents.
4455 applyBody: function(obj)
4458 Roo.log("Error - using apply Body without a template");
4461 this.tmpl.overwrite(this.bodyEl, obj);
4464 getChildHeight : function(child_nodes)
4468 child_nodes.length == 0
4473 var child_height = 0;
4475 for(var i = 0; i < child_nodes.length; i++) {
4478 * for modal with tabs...
4479 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481 var layout_childs = child_nodes[i].childNodes;
4483 for(var j = 0; j < layout_childs.length; j++) {
4485 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487 var layout_body_childs = layout_childs[j].childNodes;
4489 for(var k = 0; k < layout_body_childs.length; k++) {
4491 if(layout_body_childs[k].classList.contains('navbar')) {
4492 child_height += layout_body_childs[k].offsetHeight;
4496 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4503 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4518 child_height += child_nodes[i].offsetHeight;
4519 // Roo.log(child_nodes[i].offsetHeight);
4522 return child_height;
4524 toggleHeaderInput : function(is_edit)
4527 if (is_edit && this.is_header_editing) {
4528 return; // already editing..
4532 this.headerEditEl.dom.value = this.title;
4533 this.headerEditEl.removeClass('d-none');
4534 this.headerEditEl.dom.focus();
4535 this.titleEl.addClass('d-none');
4537 this.is_header_editing = true;
4540 // flip back to not editing.
4541 this.title = this.headerEditEl.dom.value;
4542 this.headerEditEl.addClass('d-none');
4543 this.titleEl.removeClass('d-none');
4544 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4545 this.is_header_editing = false;
4546 this.fireEvent('titlechanged', this, this.title);
4555 Roo.apply(Roo.bootstrap.Modal, {
4557 * Button config that displays a single OK button
4566 * Button config that displays Yes and No buttons
4582 * Button config that displays OK and Cancel buttons
4597 * Button config that displays Yes, No and Cancel buttons
4622 * messagebox - can be used as a replace
4626 * @class Roo.MessageBox
4627 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4631 Roo.Msg.alert('Status', 'Changes saved successfully.');
4633 // Prompt for user data:
4634 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4636 // process text value...
4640 // Show a dialog using config options:
4642 title:'Save Changes?',
4643 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4644 buttons: Roo.Msg.YESNOCANCEL,
4651 Roo.bootstrap.MessageBox = function(){
4652 var dlg, opt, mask, waitTimer;
4653 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4654 var buttons, activeTextEl, bwidth;
4658 var handleButton = function(button){
4660 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664 var handleHide = function(){
4666 dlg.el.removeClass(opt.cls);
4669 // Roo.TaskMgr.stop(waitTimer);
4670 // waitTimer = null;
4675 var updateButtons = function(b){
4678 buttons["ok"].hide();
4679 buttons["cancel"].hide();
4680 buttons["yes"].hide();
4681 buttons["no"].hide();
4682 dlg.footerEl.hide();
4686 dlg.footerEl.show();
4687 for(var k in buttons){
4688 if(typeof buttons[k] != "function"){
4691 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4692 width += buttons[k].el.getWidth()+15;
4702 var handleEsc = function(d, k, e){
4703 if(opt && opt.closable !== false){
4713 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4714 * @return {Roo.BasicDialog} The BasicDialog element
4716 getDialog : function(){
4718 dlg = new Roo.bootstrap.Modal( {
4721 //constraintoviewport:false,
4723 //collapsible : false,
4728 //buttonAlign:"center",
4729 closeClick : function(){
4730 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4733 handleButton("cancel");
4738 dlg.on("hide", handleHide);
4740 //dlg.addKeyListener(27, handleEsc);
4742 this.buttons = buttons;
4743 var bt = this.buttonText;
4744 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4745 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4746 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4747 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4749 bodyEl = dlg.bodyEl.createChild({
4751 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4752 '<textarea class="roo-mb-textarea"></textarea>' +
4753 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4755 msgEl = bodyEl.dom.firstChild;
4756 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4757 textboxEl.enableDisplayMode();
4758 textboxEl.addKeyListener([10,13], function(){
4759 if(dlg.isVisible() && opt && opt.buttons){
4762 }else if(opt.buttons.yes){
4763 handleButton("yes");
4767 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4768 textareaEl.enableDisplayMode();
4769 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4770 progressEl.enableDisplayMode();
4772 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4773 var pf = progressEl.dom.firstChild;
4775 pp = Roo.get(pf.firstChild);
4776 pp.setHeight(pf.offsetHeight);
4784 * Updates the message box body text
4785 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4786 * the XHTML-compliant non-breaking space character '&#160;')
4787 * @return {Roo.MessageBox} This message box
4789 updateText : function(text)
4791 if(!dlg.isVisible() && !opt.width){
4792 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4793 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4795 msgEl.innerHTML = text || ' ';
4797 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4798 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4800 Math.min(opt.width || cw , this.maxWidth),
4801 Math.max(opt.minWidth || this.minWidth, bwidth)
4804 activeTextEl.setWidth(w);
4806 if(dlg.isVisible()){
4807 dlg.fixedcenter = false;
4809 // to big, make it scroll. = But as usual stupid IE does not support
4812 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4813 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4814 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4816 bodyEl.dom.style.height = '';
4817 bodyEl.dom.style.overflowY = '';
4820 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4822 bodyEl.dom.style.overflowX = '';
4825 dlg.setContentSize(w, bodyEl.getHeight());
4826 if(dlg.isVisible()){
4827 dlg.fixedcenter = true;
4833 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4834 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4835 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4836 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4837 * @return {Roo.MessageBox} This message box
4839 updateProgress : function(value, text){
4841 this.updateText(text);
4844 if (pp) { // weird bug on my firefox - for some reason this is not defined
4845 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4846 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4852 * Returns true if the message box is currently displayed
4853 * @return {Boolean} True if the message box is visible, else false
4855 isVisible : function(){
4856 return dlg && dlg.isVisible();
4860 * Hides the message box if it is displayed
4863 if(this.isVisible()){
4869 * Displays a new message box, or reinitializes an existing message box, based on the config options
4870 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4871 * The following config object properties are supported:
4873 Property Type Description
4874 ---------- --------------- ------------------------------------------------------------------------------------
4875 animEl String/Element An id or Element from which the message box should animate as it opens and
4876 closes (defaults to undefined)
4877 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4878 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4879 closable Boolean False to hide the top-right close button (defaults to true). Note that
4880 progress and wait dialogs will ignore this property and always hide the
4881 close button as they can only be closed programmatically.
4882 cls String A custom CSS class to apply to the message box element
4883 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4884 displayed (defaults to 75)
4885 fn Function A callback function to execute after closing the dialog. The arguments to the
4886 function will be btn (the name of the button that was clicked, if applicable,
4887 e.g. "ok"), and text (the value of the active text field, if applicable).
4888 Progress and wait dialogs will ignore this option since they do not respond to
4889 user actions and can only be closed programmatically, so any required function
4890 should be called by the same code after it closes the dialog.
4891 icon String A CSS class that provides a background image to be used as an icon for
4892 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4893 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4894 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4895 modal Boolean False to allow user interaction with the page while the message box is
4896 displayed (defaults to true)
4897 msg String A string that will replace the existing message box body text (defaults
4898 to the XHTML-compliant non-breaking space character ' ')
4899 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4900 progress Boolean True to display a progress bar (defaults to false)
4901 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4902 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4903 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4904 title String The title text
4905 value String The string value to set into the active textbox element if displayed
4906 wait Boolean True to display a progress bar (defaults to false)
4907 width Number The width of the dialog in pixels
4914 msg: 'Please enter your address:',
4916 buttons: Roo.MessageBox.OKCANCEL,
4919 animEl: 'addAddressBtn'
4922 * @param {Object} config Configuration options
4923 * @return {Roo.MessageBox} This message box
4925 show : function(options)
4928 // this causes nightmares if you show one dialog after another
4929 // especially on callbacks..
4931 if(this.isVisible()){
4934 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4935 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4936 Roo.log("New Dialog Message:" + options.msg )
4937 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4938 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4941 var d = this.getDialog();
4943 d.setTitle(opt.title || " ");
4944 d.closeEl.setDisplayed(opt.closable !== false);
4945 activeTextEl = textboxEl;
4946 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4951 textareaEl.setHeight(typeof opt.multiline == "number" ?
4952 opt.multiline : this.defaultTextHeight);
4953 activeTextEl = textareaEl;
4962 progressEl.setDisplayed(opt.progress === true);
4964 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4966 this.updateProgress(0);
4967 activeTextEl.dom.value = opt.value || "";
4969 dlg.setDefaultButton(activeTextEl);
4971 var bs = opt.buttons;
4975 }else if(bs && bs.yes){
4976 db = buttons["yes"];
4978 dlg.setDefaultButton(db);
4980 bwidth = updateButtons(opt.buttons);
4981 this.updateText(opt.msg);
4983 d.el.addClass(opt.cls);
4985 d.proxyDrag = opt.proxyDrag === true;
4986 d.modal = opt.modal !== false;
4987 d.mask = opt.modal !== false ? mask : false;
4989 // force it to the end of the z-index stack so it gets a cursor in FF
4990 document.body.appendChild(dlg.el.dom);
4991 d.animateTarget = null;
4992 d.show(options.animEl);
4998 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4999 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5000 * and closing the message box when the process is complete.
5001 * @param {String} title The title bar text
5002 * @param {String} msg The message box body text
5003 * @return {Roo.MessageBox} This message box
5005 progress : function(title, msg){
5012 minWidth: this.minProgressWidth,
5019 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5020 * If a callback function is passed it will be called after the user clicks the button, and the
5021 * id of the button that was clicked will be passed as the only parameter to the callback
5022 * (could also be the top-right close button).
5023 * @param {String} title The title bar text
5024 * @param {String} msg The message box body text
5025 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5026 * @param {Object} scope (optional) The scope of the callback function
5027 * @return {Roo.MessageBox} This message box
5029 alert : function(title, msg, fn, scope)
5044 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5045 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5046 * You are responsible for closing the message box when the process is complete.
5047 * @param {String} msg The message box body text
5048 * @param {String} title (optional) The title bar text
5049 * @return {Roo.MessageBox} This message box
5051 wait : function(msg, title){
5062 waitTimer = Roo.TaskMgr.start({
5064 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5072 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5073 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5074 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5075 * @param {String} title The title bar text
5076 * @param {String} msg The message box body text
5077 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5078 * @param {Object} scope (optional) The scope of the callback function
5079 * @return {Roo.MessageBox} This message box
5081 confirm : function(title, msg, fn, scope){
5085 buttons: this.YESNO,
5094 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5095 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5096 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5097 * (could also be the top-right close button) and the text that was entered will be passed as the two
5098 * parameters to the callback.
5099 * @param {String} title The title bar text
5100 * @param {String} msg The message box body text
5101 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5102 * @param {Object} scope (optional) The scope of the callback function
5103 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5104 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5105 * @return {Roo.MessageBox} This message box
5107 prompt : function(title, msg, fn, scope, multiline){
5111 buttons: this.OKCANCEL,
5116 multiline: multiline,
5123 * Button config that displays a single OK button
5128 * Button config that displays Yes and No buttons
5131 YESNO : {yes:true, no:true},
5133 * Button config that displays OK and Cancel buttons
5136 OKCANCEL : {ok:true, cancel:true},
5138 * Button config that displays Yes, No and Cancel buttons
5141 YESNOCANCEL : {yes:true, no:true, cancel:true},
5144 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5147 defaultTextHeight : 75,
5149 * The maximum width in pixels of the message box (defaults to 600)
5154 * The minimum width in pixels of the message box (defaults to 100)
5159 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5160 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5163 minProgressWidth : 250,
5165 * An object containing the default button text strings that can be overriden for localized language support.
5166 * Supported properties are: ok, cancel, yes and no.
5167 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180 * Shorthand for {@link Roo.MessageBox}
5182 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5183 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 * @class Roo.bootstrap.Navbar
5193 * @extends Roo.bootstrap.Component
5194 * Bootstrap Navbar class
5197 * Create a new Navbar
5198 * @param {Object} config The config object
5202 Roo.bootstrap.Navbar = function(config){
5203 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207 * @event beforetoggle
5208 * Fire before toggle the menu
5209 * @param {Roo.EventObject} e
5211 "beforetoggle" : true
5215 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5224 getAutoCreate : function(){
5227 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231 initEvents :function ()
5233 //Roo.log(this.el.select('.navbar-toggle',true));
5234 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5241 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5243 var size = this.el.getSize();
5244 this.maskEl.setSize(size.width, size.height);
5245 this.maskEl.enableDisplayMode("block");
5254 getChildContainer : function()
5256 if (this.el && this.el.select('.collapse').getCount()) {
5257 return this.el.select('.collapse',true).first();
5272 onToggle : function()
5275 if(this.fireEvent('beforetoggle', this) === false){
5278 var ce = this.el.select('.navbar-collapse',true).first();
5280 if (!ce.hasClass('show')) {
5290 * Expand the navbar pulldown
5292 expand : function ()
5295 var ce = this.el.select('.navbar-collapse',true).first();
5296 if (ce.hasClass('collapsing')) {
5299 ce.dom.style.height = '';
5301 ce.addClass('in'); // old...
5302 ce.removeClass('collapse');
5303 ce.addClass('show');
5304 var h = ce.getHeight();
5306 ce.removeClass('show');
5307 // at this point we should be able to see it..
5308 ce.addClass('collapsing');
5310 ce.setHeight(0); // resize it ...
5311 ce.on('transitionend', function() {
5312 //Roo.log('done transition');
5313 ce.removeClass('collapsing');
5314 ce.addClass('show');
5315 ce.removeClass('collapse');
5317 ce.dom.style.height = '';
5318 }, this, { single: true} );
5320 ce.dom.scrollTop = 0;
5323 * Collapse the navbar pulldown
5325 collapse : function()
5327 var ce = this.el.select('.navbar-collapse',true).first();
5329 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5330 // it's collapsed or collapsing..
5333 ce.removeClass('in'); // old...
5334 ce.setHeight(ce.getHeight());
5335 ce.removeClass('show');
5336 ce.addClass('collapsing');
5338 ce.on('transitionend', function() {
5339 ce.dom.style.height = '';
5340 ce.removeClass('collapsing');
5341 ce.addClass('collapse');
5342 }, this, { single: true} );
5362 * @class Roo.bootstrap.NavSimplebar
5363 * @extends Roo.bootstrap.Navbar
5364 * Bootstrap Sidebar class
5366 * @cfg {Boolean} inverse is inverted color
5368 * @cfg {String} type (nav | pills | tabs)
5369 * @cfg {Boolean} arrangement stacked | justified
5370 * @cfg {String} align (left | right) alignment
5372 * @cfg {Boolean} main (true|false) main nav bar? default false
5373 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5375 * @cfg {String} tag (header|footer|nav|div) default is nav
5377 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381 * Create a new Sidebar
5382 * @param {Object} config The config object
5386 Roo.bootstrap.NavSimplebar = function(config){
5387 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5390 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5406 getAutoCreate : function(){
5410 tag : this.tag || 'div',
5411 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5413 if (['light','white'].indexOf(this.weight) > -1) {
5414 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5416 cfg.cls += ' bg-' + this.weight;
5419 cfg.cls += ' navbar-inverse';
5423 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5425 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434 cls: 'nav nav-' + this.xtype,
5440 this.type = this.type || 'nav';
5441 if (['tabs','pills'].indexOf(this.type) != -1) {
5442 cfg.cn[0].cls += ' nav-' + this.type
5446 if (this.type!=='nav') {
5447 Roo.log('nav type must be nav/tabs/pills')
5449 cfg.cn[0].cls += ' navbar-nav'
5455 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5456 cfg.cn[0].cls += ' nav-' + this.arrangement;
5460 if (this.align === 'right') {
5461 cfg.cn[0].cls += ' navbar-right';
5486 * navbar-expand-md fixed-top
5490 * @class Roo.bootstrap.NavHeaderbar
5491 * @extends Roo.bootstrap.NavSimplebar
5492 * Bootstrap Sidebar class
5494 * @cfg {String} brand what is brand
5495 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5496 * @cfg {String} brand_href href of the brand
5497 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5498 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5499 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5500 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5503 * Create a new Sidebar
5504 * @param {Object} config The config object
5508 Roo.bootstrap.NavHeaderbar = function(config){
5509 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5520 desktopCenter : false,
5523 getAutoCreate : function(){
5526 tag: this.nav || 'nav',
5527 cls: 'navbar navbar-expand-md',
5533 if (this.desktopCenter) {
5534 cn.push({cls : 'container', cn : []});
5542 cls: 'navbar-toggle navbar-toggler',
5543 'data-toggle': 'collapse',
5548 html: 'Toggle navigation'
5552 cls: 'icon-bar navbar-toggler-icon'
5565 cn.push( Roo.bootstrap.version == 4 ? btn : {
5567 cls: 'navbar-header',
5576 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5582 if (['light','white'].indexOf(this.weight) > -1) {
5583 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5585 cfg.cls += ' bg-' + this.weight;
5588 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5589 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5591 // tag can override this..
5593 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5596 if (this.brand !== '') {
5597 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5598 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5600 href: this.brand_href ? this.brand_href : '#',
5601 cls: 'navbar-brand',
5609 cfg.cls += ' main-nav';
5617 getHeaderChildContainer : function()
5619 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5620 return this.el.select('.navbar-header',true).first();
5623 return this.getChildContainer();
5626 getChildContainer : function()
5629 return this.el.select('.roo-navbar-collapse',true).first();
5634 initEvents : function()
5636 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5638 if (this.autohide) {
5643 Roo.get(document).on('scroll',function(e) {
5644 var ns = Roo.get(document).getScroll().top;
5645 var os = prevScroll;
5649 ft.removeClass('slideDown');
5650 ft.addClass('slideUp');
5653 ft.removeClass('slideUp');
5654 ft.addClass('slideDown');
5675 * @class Roo.bootstrap.NavSidebar
5676 * @extends Roo.bootstrap.Navbar
5677 * Bootstrap Sidebar class
5680 * Create a new Sidebar
5681 * @param {Object} config The config object
5685 Roo.bootstrap.NavSidebar = function(config){
5686 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5689 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5691 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5693 getAutoCreate : function(){
5698 cls: 'sidebar sidebar-nav'
5720 * @class Roo.bootstrap.NavGroup
5721 * @extends Roo.bootstrap.Component
5722 * Bootstrap NavGroup class
5723 * @cfg {String} align (left|right)
5724 * @cfg {Boolean} inverse
5725 * @cfg {String} type (nav|pills|tab) default nav
5726 * @cfg {String} navId - reference Id for navbar.
5730 * Create a new nav group
5731 * @param {Object} config The config object
5734 Roo.bootstrap.NavGroup = function(config){
5735 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5738 Roo.bootstrap.NavGroup.register(this);
5742 * Fires when the active item changes
5743 * @param {Roo.bootstrap.NavGroup} this
5744 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5745 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5752 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5763 getAutoCreate : function()
5765 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5771 if (Roo.bootstrap.version == 4) {
5772 if (['tabs','pills'].indexOf(this.type) != -1) {
5773 cfg.cls += ' nav-' + this.type;
5775 // trying to remove so header bar can right align top?
5776 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5777 // do not use on header bar...
5778 cfg.cls += ' navbar-nav';
5783 if (['tabs','pills'].indexOf(this.type) != -1) {
5784 cfg.cls += ' nav-' + this.type
5786 if (this.type !== 'nav') {
5787 Roo.log('nav type must be nav/tabs/pills')
5789 cfg.cls += ' navbar-nav'
5793 if (this.parent() && this.parent().sidebar) {
5796 cls: 'dashboard-menu sidebar-menu'
5802 if (this.form === true) {
5805 cls: 'navbar-form form-inline'
5807 //nav navbar-right ml-md-auto
5808 if (this.align === 'right') {
5809 cfg.cls += ' navbar-right ml-md-auto';
5811 cfg.cls += ' navbar-left';
5815 if (this.align === 'right') {
5816 cfg.cls += ' navbar-right ml-md-auto';
5818 cfg.cls += ' mr-auto';
5822 cfg.cls += ' navbar-inverse';
5830 * sets the active Navigation item
5831 * @param {Roo.bootstrap.NavItem} the new current navitem
5833 setActiveItem : function(item)
5836 Roo.each(this.navItems, function(v){
5841 v.setActive(false, true);
5848 item.setActive(true, true);
5849 this.fireEvent('changed', this, item, prev);
5854 * gets the active Navigation item
5855 * @return {Roo.bootstrap.NavItem} the current navitem
5857 getActive : function()
5861 Roo.each(this.navItems, function(v){
5872 indexOfNav : function()
5876 Roo.each(this.navItems, function(v,i){
5887 * adds a Navigation item
5888 * @param {Roo.bootstrap.NavItem} the navitem to add
5890 addItem : function(cfg)
5892 if (this.form && Roo.bootstrap.version == 4) {
5895 var cn = new Roo.bootstrap.NavItem(cfg);
5897 cn.parentId = this.id;
5898 cn.onRender(this.el, null);
5902 * register a Navigation item
5903 * @param {Roo.bootstrap.NavItem} the navitem to add
5905 register : function(item)
5907 this.navItems.push( item);
5908 item.navId = this.navId;
5913 * clear all the Navigation item
5916 clearAll : function()
5919 this.el.dom.innerHTML = '';
5922 getNavItem: function(tabId)
5925 Roo.each(this.navItems, function(e) {
5926 if (e.tabId == tabId) {
5936 setActiveNext : function()
5938 var i = this.indexOfNav(this.getActive());
5939 if (i > this.navItems.length) {
5942 this.setActiveItem(this.navItems[i+1]);
5944 setActivePrev : function()
5946 var i = this.indexOfNav(this.getActive());
5950 this.setActiveItem(this.navItems[i-1]);
5952 clearWasActive : function(except) {
5953 Roo.each(this.navItems, function(e) {
5954 if (e.tabId != except.tabId && e.was_active) {
5955 e.was_active = false;
5962 getWasActive : function ()
5965 Roo.each(this.navItems, function(e) {
5980 Roo.apply(Roo.bootstrap.NavGroup, {
5984 * register a Navigation Group
5985 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5987 register : function(navgrp)
5989 this.groups[navgrp.navId] = navgrp;
5993 * fetch a Navigation Group based on the navigation ID
5994 * @param {string} the navgroup to add
5995 * @returns {Roo.bootstrap.NavGroup} the navgroup
5997 get: function(navId) {
5998 if (typeof(this.groups[navId]) == 'undefined') {
6000 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6002 return this.groups[navId] ;
6017 * @class Roo.bootstrap.NavItem
6018 * @extends Roo.bootstrap.Component
6019 * Bootstrap Navbar.NavItem class
6020 * @cfg {String} href link to
6021 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6023 * @cfg {String} html content of button
6024 * @cfg {String} badge text inside badge
6025 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6026 * @cfg {String} glyphicon DEPRICATED - use fa
6027 * @cfg {String} icon DEPRICATED - use fa
6028 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6029 * @cfg {Boolean} active Is item active
6030 * @cfg {Boolean} disabled Is item disabled
6032 * @cfg {Boolean} preventDefault (true | false) default false
6033 * @cfg {String} tabId the tab that this item activates.
6034 * @cfg {String} tagtype (a|span) render as a href or span?
6035 * @cfg {Boolean} animateRef (true|false) link to element default false
6038 * Create a new Navbar Item
6039 * @param {Object} config The config object
6041 Roo.bootstrap.NavItem = function(config){
6042 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6047 * The raw click event for the entire grid.
6048 * @param {Roo.EventObject} e
6053 * Fires when the active item active state changes
6054 * @param {Roo.bootstrap.NavItem} this
6055 * @param {boolean} state the new state
6061 * Fires when scroll to element
6062 * @param {Roo.bootstrap.NavItem} this
6063 * @param {Object} options
6064 * @param {Roo.EventObject} e
6072 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6081 preventDefault : false,
6089 button_outline : false,
6093 getAutoCreate : function(){
6101 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6103 if (this.disabled) {
6104 cfg.cls += ' disabled';
6108 if (this.button_weight.length) {
6109 cfg.tag = this.href ? 'a' : 'button';
6110 cfg.html = this.html || '';
6111 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6113 cfg.href = this.href;
6116 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6119 // menu .. should add dropdown-menu class - so no need for carat..
6121 if (this.badge !== '') {
6123 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6128 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132 href : this.href || "#",
6133 html: this.html || ''
6136 if (this.tagtype == 'a') {
6137 cfg.cn[0].cls = 'nav-link';
6140 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6143 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6145 if(this.glyphicon) {
6146 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6151 cfg.cn[0].html += " <span class='caret'></span>";
6155 if (this.badge !== '') {
6157 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6165 onRender : function(ct, position)
6167 // Roo.log("Call onRender: " + this.xtype);
6168 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6173 this.navLink = this.el.select('.nav-link',true).first();
6178 initEvents: function()
6180 if (typeof (this.menu) != 'undefined') {
6181 this.menu.parentType = this.xtype;
6182 this.menu.triggerEl = this.el;
6183 this.menu = this.addxtype(Roo.apply({}, this.menu));
6186 this.el.select('a',true).on('click', this.onClick, this);
6188 if(this.tagtype == 'span'){
6189 this.el.select('span',true).on('click', this.onClick, this);
6192 // at this point parent should be available..
6193 this.parent().register(this);
6196 onClick : function(e)
6198 if (e.getTarget('.dropdown-menu-item')) {
6199 // did you click on a menu itemm.... - then don't trigger onclick..
6204 this.preventDefault ||
6207 Roo.log("NavItem - prevent Default?");
6211 if (this.disabled) {
6215 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6216 if (tg && tg.transition) {
6217 Roo.log("waiting for the transitionend");
6223 //Roo.log("fire event clicked");
6224 if(this.fireEvent('click', this, e) === false){
6228 if(this.tagtype == 'span'){
6232 //Roo.log(this.href);
6233 var ael = this.el.select('a',true).first();
6236 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6237 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6238 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6239 return; // ignore... - it's a 'hash' to another page.
6241 Roo.log("NavItem - prevent Default?");
6243 this.scrollToElement(e);
6247 var p = this.parent();
6249 if (['tabs','pills'].indexOf(p.type)!==-1) {
6250 if (typeof(p.setActiveItem) !== 'undefined') {
6251 p.setActiveItem(this);
6255 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6256 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6257 // remove the collapsed menu expand...
6258 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6262 isActive: function () {
6265 setActive : function(state, fire, is_was_active)
6267 if (this.active && !state && this.navId) {
6268 this.was_active = true;
6269 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6271 nv.clearWasActive(this);
6275 this.active = state;
6278 this.el.removeClass('active');
6279 this.navLink ? this.navLink.removeClass('active') : false;
6280 } else if (!this.el.hasClass('active')) {
6282 this.el.addClass('active');
6283 if (Roo.bootstrap.version == 4 && this.navLink ) {
6284 this.navLink.addClass('active');
6289 this.fireEvent('changed', this, state);
6292 // show a panel if it's registered and related..
6294 if (!this.navId || !this.tabId || !state || is_was_active) {
6298 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302 var pan = tg.getPanelByName(this.tabId);
6306 // if we can not flip to new panel - go back to old nav highlight..
6307 if (false == tg.showPanel(pan)) {
6308 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6310 var onav = nv.getWasActive();
6312 onav.setActive(true, false, true);
6321 // this should not be here...
6322 setDisabled : function(state)
6324 this.disabled = state;
6326 this.el.removeClass('disabled');
6327 } else if (!this.el.hasClass('disabled')) {
6328 this.el.addClass('disabled');
6334 * Fetch the element to display the tooltip on.
6335 * @return {Roo.Element} defaults to this.el
6337 tooltipEl : function()
6339 return this.el.select('' + this.tagtype + '', true).first();
6342 scrollToElement : function(e)
6344 var c = document.body;
6347 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6349 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6350 c = document.documentElement;
6353 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6359 var o = target.calcOffsetsTo(c);
6366 this.fireEvent('scrollto', this, options, e);
6368 Roo.get(c).scrollTo('top', options.value, true);
6381 * <span> icon </span>
6382 * <span> text </span>
6383 * <span>badge </span>
6387 * @class Roo.bootstrap.NavSidebarItem
6388 * @extends Roo.bootstrap.NavItem
6389 * Bootstrap Navbar.NavSidebarItem class
6390 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6391 * {Boolean} open is the menu open
6392 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6393 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6394 * {String} buttonSize (sm|md|lg)the extra classes for the button
6395 * {Boolean} showArrow show arrow next to the text (default true)
6397 * Create a new Navbar Button
6398 * @param {Object} config The config object
6400 Roo.bootstrap.NavSidebarItem = function(config){
6401 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6406 * The raw click event for the entire grid.
6407 * @param {Roo.EventObject} e
6412 * Fires when the active item active state changes
6413 * @param {Roo.bootstrap.NavSidebarItem} this
6414 * @param {boolean} state the new state
6422 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6424 badgeWeight : 'default',
6430 buttonWeight : 'default',
6436 getAutoCreate : function(){
6441 href : this.href || '#',
6447 if(this.buttonView){
6450 href : this.href || '#',
6451 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6464 cfg.cls += ' active';
6467 if (this.disabled) {
6468 cfg.cls += ' disabled';
6471 cfg.cls += ' open x-open';
6474 if (this.glyphicon || this.icon) {
6475 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6476 a.cn.push({ tag : 'i', cls : c }) ;
6479 if(!this.buttonView){
6482 html : this.html || ''
6489 if (this.badge !== '') {
6490 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6496 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6499 a.cls += ' dropdown-toggle treeview' ;
6505 initEvents : function()
6507 if (typeof (this.menu) != 'undefined') {
6508 this.menu.parentType = this.xtype;
6509 this.menu.triggerEl = this.el;
6510 this.menu = this.addxtype(Roo.apply({}, this.menu));
6513 this.el.on('click', this.onClick, this);
6515 if(this.badge !== ''){
6516 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6521 onClick : function(e)
6528 if(this.preventDefault){
6532 this.fireEvent('click', this, e);
6535 disable : function()
6537 this.setDisabled(true);
6542 this.setDisabled(false);
6545 setDisabled : function(state)
6547 if(this.disabled == state){
6551 this.disabled = state;
6554 this.el.addClass('disabled');
6558 this.el.removeClass('disabled');
6563 setActive : function(state)
6565 if(this.active == state){
6569 this.active = state;
6572 this.el.addClass('active');
6576 this.el.removeClass('active');
6581 isActive: function ()
6586 setBadge : function(str)
6592 this.badgeEl.dom.innerHTML = str;
6609 * @class Roo.bootstrap.Row
6610 * @extends Roo.bootstrap.Component
6611 * Bootstrap Row class (contains columns...)
6615 * @param {Object} config The config object
6618 Roo.bootstrap.Row = function(config){
6619 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6622 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6624 getAutoCreate : function(){
6643 * @class Roo.bootstrap.Pagination
6644 * @extends Roo.bootstrap.Component
6645 * Bootstrap Pagination class
6646 * @cfg {String} size xs | sm | md | lg
6647 * @cfg {Boolean} inverse false | true
6650 * Create a new Pagination
6651 * @param {Object} config The config object
6654 Roo.bootstrap.Pagination = function(config){
6655 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6658 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6664 getAutoCreate : function(){
6670 cfg.cls += ' inverse';
6676 cfg.cls += " " + this.cls;
6694 * @class Roo.bootstrap.PaginationItem
6695 * @extends Roo.bootstrap.Component
6696 * Bootstrap PaginationItem class
6697 * @cfg {String} html text
6698 * @cfg {String} href the link
6699 * @cfg {Boolean} preventDefault (true | false) default true
6700 * @cfg {Boolean} active (true | false) default false
6701 * @cfg {Boolean} disabled default false
6705 * Create a new PaginationItem
6706 * @param {Object} config The config object
6710 Roo.bootstrap.PaginationItem = function(config){
6711 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6716 * The raw click event for the entire grid.
6717 * @param {Roo.EventObject} e
6723 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6727 preventDefault: true,
6732 getAutoCreate : function(){
6738 href : this.href ? this.href : '#',
6739 html : this.html ? this.html : ''
6749 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6759 initEvents: function() {
6761 this.el.on('click', this.onClick, this);
6764 onClick : function(e)
6766 Roo.log('PaginationItem on click ');
6767 if(this.preventDefault){
6775 this.fireEvent('click', this, e);
6791 * @class Roo.bootstrap.Slider
6792 * @extends Roo.bootstrap.Component
6793 * Bootstrap Slider class
6796 * Create a new Slider
6797 * @param {Object} config The config object
6800 Roo.bootstrap.Slider = function(config){
6801 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6804 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6806 getAutoCreate : function(){
6810 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6826 * Ext JS Library 1.1.1
6827 * Copyright(c) 2006-2007, Ext JS, LLC.
6829 * Originally Released Under LGPL - original licence link has changed is not relivant.
6832 * <script type="text/javascript">
6837 * @class Roo.grid.ColumnModel
6838 * @extends Roo.util.Observable
6839 * This is the default implementation of a ColumnModel used by the Grid. It defines
6840 * the columns in the grid.
6843 var colModel = new Roo.grid.ColumnModel([
6844 {header: "Ticker", width: 60, sortable: true, locked: true},
6845 {header: "Company Name", width: 150, sortable: true},
6846 {header: "Market Cap.", width: 100, sortable: true},
6847 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6848 {header: "Employees", width: 100, sortable: true, resizable: false}
6853 * The config options listed for this class are options which may appear in each
6854 * individual column definition.
6855 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6857 * @param {Object} config An Array of column config objects. See this class's
6858 * config objects for details.
6860 Roo.grid.ColumnModel = function(config){
6862 * The config passed into the constructor
6864 this.config = config;
6867 // if no id, create one
6868 // if the column does not have a dataIndex mapping,
6869 // map it to the order it is in the config
6870 for(var i = 0, len = config.length; i < len; i++){
6872 if(typeof c.dataIndex == "undefined"){
6875 if(typeof c.renderer == "string"){
6876 c.renderer = Roo.util.Format[c.renderer];
6878 if(typeof c.id == "undefined"){
6881 if(c.editor && c.editor.xtype){
6882 c.editor = Roo.factory(c.editor, Roo.grid);
6884 if(c.editor && c.editor.isFormField){
6885 c.editor = new Roo.grid.GridEditor(c.editor);
6887 this.lookup[c.id] = c;
6891 * The width of columns which have no width specified (defaults to 100)
6894 this.defaultWidth = 100;
6897 * Default sortable of columns which have no sortable specified (defaults to false)
6900 this.defaultSortable = false;
6904 * @event widthchange
6905 * Fires when the width of a column changes.
6906 * @param {ColumnModel} this
6907 * @param {Number} columnIndex The column index
6908 * @param {Number} newWidth The new width
6910 "widthchange": true,
6912 * @event headerchange
6913 * Fires when the text of a header changes.
6914 * @param {ColumnModel} this
6915 * @param {Number} columnIndex The column index
6916 * @param {Number} newText The new header text
6918 "headerchange": true,
6920 * @event hiddenchange
6921 * Fires when a column is hidden or "unhidden".
6922 * @param {ColumnModel} this
6923 * @param {Number} columnIndex The column index
6924 * @param {Boolean} hidden true if hidden, false otherwise
6926 "hiddenchange": true,
6928 * @event columnmoved
6929 * Fires when a column is moved.
6930 * @param {ColumnModel} this
6931 * @param {Number} oldIndex
6932 * @param {Number} newIndex
6934 "columnmoved" : true,
6936 * @event columlockchange
6937 * Fires when a column's locked state is changed
6938 * @param {ColumnModel} this
6939 * @param {Number} colIndex
6940 * @param {Boolean} locked true if locked
6942 "columnlockchange" : true
6944 Roo.grid.ColumnModel.superclass.constructor.call(this);
6946 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6948 * @cfg {String} header The header text to display in the Grid view.
6951 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6952 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6953 * specified, the column's index is used as an index into the Record's data Array.
6956 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6957 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6960 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6961 * Defaults to the value of the {@link #defaultSortable} property.
6962 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6965 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6968 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6971 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6974 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6977 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6978 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6979 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6980 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6983 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6986 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6989 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6992 * @cfg {String} cursor (Optional)
6995 * @cfg {String} tooltip (Optional)
6998 * @cfg {Number} xs (Optional)
7001 * @cfg {Number} sm (Optional)
7004 * @cfg {Number} md (Optional)
7007 * @cfg {Number} lg (Optional)
7010 * Returns the id of the column at the specified index.
7011 * @param {Number} index The column index
7012 * @return {String} the id
7014 getColumnId : function(index){
7015 return this.config[index].id;
7019 * Returns the column for a specified id.
7020 * @param {String} id The column id
7021 * @return {Object} the column
7023 getColumnById : function(id){
7024 return this.lookup[id];
7029 * Returns the column for a specified dataIndex.
7030 * @param {String} dataIndex The column dataIndex
7031 * @return {Object|Boolean} the column or false if not found
7033 getColumnByDataIndex: function(dataIndex){
7034 var index = this.findColumnIndex(dataIndex);
7035 return index > -1 ? this.config[index] : false;
7039 * Returns the index for a specified column id.
7040 * @param {String} id The column id
7041 * @return {Number} the index, or -1 if not found
7043 getIndexById : function(id){
7044 for(var i = 0, len = this.config.length; i < len; i++){
7045 if(this.config[i].id == id){
7053 * Returns the index for a specified column dataIndex.
7054 * @param {String} dataIndex The column dataIndex
7055 * @return {Number} the index, or -1 if not found
7058 findColumnIndex : function(dataIndex){
7059 for(var i = 0, len = this.config.length; i < len; i++){
7060 if(this.config[i].dataIndex == dataIndex){
7068 moveColumn : function(oldIndex, newIndex){
7069 var c = this.config[oldIndex];
7070 this.config.splice(oldIndex, 1);
7071 this.config.splice(newIndex, 0, c);
7072 this.dataMap = null;
7073 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7076 isLocked : function(colIndex){
7077 return this.config[colIndex].locked === true;
7080 setLocked : function(colIndex, value, suppressEvent){
7081 if(this.isLocked(colIndex) == value){
7084 this.config[colIndex].locked = value;
7086 this.fireEvent("columnlockchange", this, colIndex, value);
7090 getTotalLockedWidth : function(){
7092 for(var i = 0; i < this.config.length; i++){
7093 if(this.isLocked(i) && !this.isHidden(i)){
7094 this.totalWidth += this.getColumnWidth(i);
7100 getLockedCount : function(){
7101 for(var i = 0, len = this.config.length; i < len; i++){
7102 if(!this.isLocked(i)){
7107 return this.config.length;
7111 * Returns the number of columns.
7114 getColumnCount : function(visibleOnly){
7115 if(visibleOnly === true){
7117 for(var i = 0, len = this.config.length; i < len; i++){
7118 if(!this.isHidden(i)){
7124 return this.config.length;
7128 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7129 * @param {Function} fn
7130 * @param {Object} scope (optional)
7131 * @return {Array} result
7133 getColumnsBy : function(fn, scope){
7135 for(var i = 0, len = this.config.length; i < len; i++){
7136 var c = this.config[i];
7137 if(fn.call(scope||this, c, i) === true){
7145 * Returns true if the specified column is sortable.
7146 * @param {Number} col The column index
7149 isSortable : function(col){
7150 if(typeof this.config[col].sortable == "undefined"){
7151 return this.defaultSortable;
7153 return this.config[col].sortable;
7157 * Returns the rendering (formatting) function defined for the column.
7158 * @param {Number} col The column index.
7159 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7161 getRenderer : function(col){
7162 if(!this.config[col].renderer){
7163 return Roo.grid.ColumnModel.defaultRenderer;
7165 return this.config[col].renderer;
7169 * Sets the rendering (formatting) function for a column.
7170 * @param {Number} col The column index
7171 * @param {Function} fn The function to use to process the cell's raw data
7172 * to return HTML markup for the grid view. The render function is called with
7173 * the following parameters:<ul>
7174 * <li>Data value.</li>
7175 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7176 * <li>css A CSS style string to apply to the table cell.</li>
7177 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7178 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7179 * <li>Row index</li>
7180 * <li>Column index</li>
7181 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7183 setRenderer : function(col, fn){
7184 this.config[col].renderer = fn;
7188 * Returns the width for the specified column.
7189 * @param {Number} col The column index
7192 getColumnWidth : function(col){
7193 return this.config[col].width * 1 || this.defaultWidth;
7197 * Sets the width for a column.
7198 * @param {Number} col The column index
7199 * @param {Number} width The new width
7201 setColumnWidth : function(col, width, suppressEvent){
7202 this.config[col].width = width;
7203 this.totalWidth = null;
7205 this.fireEvent("widthchange", this, col, width);
7210 * Returns the total width of all columns.
7211 * @param {Boolean} includeHidden True to include hidden column widths
7214 getTotalWidth : function(includeHidden){
7215 if(!this.totalWidth){
7216 this.totalWidth = 0;
7217 for(var i = 0, len = this.config.length; i < len; i++){
7218 if(includeHidden || !this.isHidden(i)){
7219 this.totalWidth += this.getColumnWidth(i);
7223 return this.totalWidth;
7227 * Returns the header for the specified column.
7228 * @param {Number} col The column index
7231 getColumnHeader : function(col){
7232 return this.config[col].header;
7236 * Sets the header for a column.
7237 * @param {Number} col The column index
7238 * @param {String} header The new header
7240 setColumnHeader : function(col, header){
7241 this.config[col].header = header;
7242 this.fireEvent("headerchange", this, col, header);
7246 * Returns the tooltip for the specified column.
7247 * @param {Number} col The column index
7250 getColumnTooltip : function(col){
7251 return this.config[col].tooltip;
7254 * Sets the tooltip for a column.
7255 * @param {Number} col The column index
7256 * @param {String} tooltip The new tooltip
7258 setColumnTooltip : function(col, tooltip){
7259 this.config[col].tooltip = tooltip;
7263 * Returns the dataIndex for the specified column.
7264 * @param {Number} col The column index
7267 getDataIndex : function(col){
7268 return this.config[col].dataIndex;
7272 * Sets the dataIndex for a column.
7273 * @param {Number} col The column index
7274 * @param {Number} dataIndex The new dataIndex
7276 setDataIndex : function(col, dataIndex){
7277 this.config[col].dataIndex = dataIndex;
7283 * Returns true if the cell is editable.
7284 * @param {Number} colIndex The column index
7285 * @param {Number} rowIndex The row index - this is nto actually used..?
7288 isCellEditable : function(colIndex, rowIndex){
7289 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293 * Returns the editor defined for the cell/column.
7294 * return false or null to disable editing.
7295 * @param {Number} colIndex The column index
7296 * @param {Number} rowIndex The row index
7299 getCellEditor : function(colIndex, rowIndex){
7300 return this.config[colIndex].editor;
7304 * Sets if a column is editable.
7305 * @param {Number} col The column index
7306 * @param {Boolean} editable True if the column is editable
7308 setEditable : function(col, editable){
7309 this.config[col].editable = editable;
7314 * Returns true if the column is hidden.
7315 * @param {Number} colIndex The column index
7318 isHidden : function(colIndex){
7319 return this.config[colIndex].hidden;
7324 * Returns true if the column width cannot be changed
7326 isFixed : function(colIndex){
7327 return this.config[colIndex].fixed;
7331 * Returns true if the column can be resized
7334 isResizable : function(colIndex){
7335 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7338 * Sets if a column is hidden.
7339 * @param {Number} colIndex The column index
7340 * @param {Boolean} hidden True if the column is hidden
7342 setHidden : function(colIndex, hidden){
7343 this.config[colIndex].hidden = hidden;
7344 this.totalWidth = null;
7345 this.fireEvent("hiddenchange", this, colIndex, hidden);
7349 * Sets the editor for a column.
7350 * @param {Number} col The column index
7351 * @param {Object} editor The editor object
7353 setEditor : function(col, editor){
7354 this.config[col].editor = editor;
7358 Roo.grid.ColumnModel.defaultRenderer = function(value)
7360 if(typeof value == "object") {
7363 if(typeof value == "string" && value.length < 1){
7367 return String.format("{0}", value);
7370 // Alias for backwards compatibility
7371 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7374 * Ext JS Library 1.1.1
7375 * Copyright(c) 2006-2007, Ext JS, LLC.
7377 * Originally Released Under LGPL - original licence link has changed is not relivant.
7380 * <script type="text/javascript">
7384 * @class Roo.LoadMask
7385 * A simple utility class for generically masking elements while loading data. If the element being masked has
7386 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7387 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7388 * element's UpdateManager load indicator and will be destroyed after the initial load.
7390 * Create a new LoadMask
7391 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7392 * @param {Object} config The config object
7394 Roo.LoadMask = function(el, config){
7395 this.el = Roo.get(el);
7396 Roo.apply(this, config);
7398 this.store.on('beforeload', this.onBeforeLoad, this);
7399 this.store.on('load', this.onLoad, this);
7400 this.store.on('loadexception', this.onLoadException, this);
7401 this.removeMask = false;
7403 var um = this.el.getUpdateManager();
7404 um.showLoadIndicator = false; // disable the default indicator
7405 um.on('beforeupdate', this.onBeforeLoad, this);
7406 um.on('update', this.onLoad, this);
7407 um.on('failure', this.onLoad, this);
7408 this.removeMask = true;
7412 Roo.LoadMask.prototype = {
7414 * @cfg {Boolean} removeMask
7415 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7416 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7420 * The text to display in a centered loading message box (defaults to 'Loading...')
7424 * @cfg {String} msgCls
7425 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7427 msgCls : 'x-mask-loading',
7430 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7436 * Disables the mask to prevent it from being displayed
7438 disable : function(){
7439 this.disabled = true;
7443 * Enables the mask so that it can be displayed
7445 enable : function(){
7446 this.disabled = false;
7449 onLoadException : function()
7453 if (typeof(arguments[3]) != 'undefined') {
7454 Roo.MessageBox.alert("Error loading",arguments[3]);
7458 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7459 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7466 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7471 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475 onBeforeLoad : function(){
7477 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7482 destroy : function(){
7484 this.store.un('beforeload', this.onBeforeLoad, this);
7485 this.store.un('load', this.onLoad, this);
7486 this.store.un('loadexception', this.onLoadException, this);
7488 var um = this.el.getUpdateManager();
7489 um.un('beforeupdate', this.onBeforeLoad, this);
7490 um.un('update', this.onLoad, this);
7491 um.un('failure', this.onLoad, this);
7502 * @class Roo.bootstrap.Table
7503 * @extends Roo.bootstrap.Component
7504 * Bootstrap Table class
7505 * @cfg {String} cls table class
7506 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7507 * @cfg {String} bgcolor Specifies the background color for a table
7508 * @cfg {Number} border Specifies whether the table cells should have borders or not
7509 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7510 * @cfg {Number} cellspacing Specifies the space between cells
7511 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7512 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7513 * @cfg {String} sortable Specifies that the table should be sortable
7514 * @cfg {String} summary Specifies a summary of the content of a table
7515 * @cfg {Number} width Specifies the width of a table
7516 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7518 * @cfg {boolean} striped Should the rows be alternative striped
7519 * @cfg {boolean} bordered Add borders to the table
7520 * @cfg {boolean} hover Add hover highlighting
7521 * @cfg {boolean} condensed Format condensed
7522 * @cfg {boolean} responsive Format condensed
7523 * @cfg {Boolean} loadMask (true|false) default false
7524 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7525 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7526 * @cfg {Boolean} rowSelection (true|false) default false
7527 * @cfg {Boolean} cellSelection (true|false) default false
7528 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7529 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7530 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7531 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7535 * Create a new Table
7536 * @param {Object} config The config object
7539 Roo.bootstrap.Table = function(config){
7540 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7545 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7546 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7547 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7548 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7550 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7552 this.sm.grid = this;
7553 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7554 this.sm = this.selModel;
7555 this.sm.xmodule = this.xmodule || false;
7558 if (this.cm && typeof(this.cm.config) == 'undefined') {
7559 this.colModel = new Roo.grid.ColumnModel(this.cm);
7560 this.cm = this.colModel;
7561 this.cm.xmodule = this.xmodule || false;
7564 this.store= Roo.factory(this.store, Roo.data);
7565 this.ds = this.store;
7566 this.ds.xmodule = this.xmodule || false;
7569 if (this.footer && this.store) {
7570 this.footer.dataSource = this.ds;
7571 this.footer = Roo.factory(this.footer);
7578 * Fires when a cell is clicked
7579 * @param {Roo.bootstrap.Table} this
7580 * @param {Roo.Element} el
7581 * @param {Number} rowIndex
7582 * @param {Number} columnIndex
7583 * @param {Roo.EventObject} e
7587 * @event celldblclick
7588 * Fires when a cell is double clicked
7589 * @param {Roo.bootstrap.Table} this
7590 * @param {Roo.Element} el
7591 * @param {Number} rowIndex
7592 * @param {Number} columnIndex
7593 * @param {Roo.EventObject} e
7595 "celldblclick" : true,
7598 * Fires when a row is clicked
7599 * @param {Roo.bootstrap.Table} this
7600 * @param {Roo.Element} el
7601 * @param {Number} rowIndex
7602 * @param {Roo.EventObject} e
7606 * @event rowdblclick
7607 * Fires when a row is double clicked
7608 * @param {Roo.bootstrap.Table} this
7609 * @param {Roo.Element} el
7610 * @param {Number} rowIndex
7611 * @param {Roo.EventObject} e
7613 "rowdblclick" : true,
7616 * Fires when a mouseover occur
7617 * @param {Roo.bootstrap.Table} this
7618 * @param {Roo.Element} el
7619 * @param {Number} rowIndex
7620 * @param {Number} columnIndex
7621 * @param {Roo.EventObject} e
7626 * Fires when a mouseout occur
7627 * @param {Roo.bootstrap.Table} this
7628 * @param {Roo.Element} el
7629 * @param {Number} rowIndex
7630 * @param {Number} columnIndex
7631 * @param {Roo.EventObject} e
7636 * Fires when a row is rendered, so you can change add a style to it.
7637 * @param {Roo.bootstrap.Table} this
7638 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7642 * @event rowsrendered
7643 * Fires when all the rows have been rendered
7644 * @param {Roo.bootstrap.Table} this
7646 'rowsrendered' : true,
7648 * @event contextmenu
7649 * The raw contextmenu event for the entire grid.
7650 * @param {Roo.EventObject} e
7652 "contextmenu" : true,
7654 * @event rowcontextmenu
7655 * Fires when a row is right clicked
7656 * @param {Roo.bootstrap.Table} this
7657 * @param {Number} rowIndex
7658 * @param {Roo.EventObject} e
7660 "rowcontextmenu" : true,
7662 * @event cellcontextmenu
7663 * Fires when a cell is right clicked
7664 * @param {Roo.bootstrap.Table} this
7665 * @param {Number} rowIndex
7666 * @param {Number} cellIndex
7667 * @param {Roo.EventObject} e
7669 "cellcontextmenu" : true,
7671 * @event headercontextmenu
7672 * Fires when a header is right clicked
7673 * @param {Roo.bootstrap.Table} this
7674 * @param {Number} columnIndex
7675 * @param {Roo.EventObject} e
7677 "headercontextmenu" : true
7681 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7707 rowSelection : false,
7708 cellSelection : false,
7711 // Roo.Element - the tbody
7713 // Roo.Element - thead element
7716 container: false, // used by gridpanel...
7722 auto_hide_footer : false,
7724 getAutoCreate : function()
7726 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7733 if (this.scrollBody) {
7734 cfg.cls += ' table-body-fixed';
7737 cfg.cls += ' table-striped';
7741 cfg.cls += ' table-hover';
7743 if (this.bordered) {
7744 cfg.cls += ' table-bordered';
7746 if (this.condensed) {
7747 cfg.cls += ' table-condensed';
7749 if (this.responsive) {
7750 cfg.cls += ' table-responsive';
7754 cfg.cls+= ' ' +this.cls;
7757 // this lot should be simplifed...
7770 ].forEach(function(k) {
7778 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7781 if(this.store || this.cm){
7782 if(this.headerShow){
7783 cfg.cn.push(this.renderHeader());
7786 cfg.cn.push(this.renderBody());
7788 if(this.footerShow){
7789 cfg.cn.push(this.renderFooter());
7791 // where does this come from?
7792 //cfg.cls+= ' TableGrid';
7795 return { cn : [ cfg ] };
7798 initEvents : function()
7800 if(!this.store || !this.cm){
7803 if (this.selModel) {
7804 this.selModel.initEvents();
7808 //Roo.log('initEvents with ds!!!!');
7810 this.mainBody = this.el.select('tbody', true).first();
7811 this.mainHead = this.el.select('thead', true).first();
7812 this.mainFoot = this.el.select('tfoot', true).first();
7818 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7819 e.on('click', _this.sort, _this);
7822 this.mainBody.on("click", this.onClick, this);
7823 this.mainBody.on("dblclick", this.onDblClick, this);
7825 // why is this done????? = it breaks dialogs??
7826 //this.parent().el.setStyle('position', 'relative');
7830 this.footer.parentId = this.id;
7831 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7834 this.el.select('tfoot tr td').first().addClass('hide');
7839 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7842 this.store.on('load', this.onLoad, this);
7843 this.store.on('beforeload', this.onBeforeLoad, this);
7844 this.store.on('update', this.onUpdate, this);
7845 this.store.on('add', this.onAdd, this);
7846 this.store.on("clear", this.clear, this);
7848 this.el.on("contextmenu", this.onContextMenu, this);
7850 this.mainBody.on('scroll', this.onBodyScroll, this);
7852 this.cm.on("headerchange", this.onHeaderChange, this);
7854 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858 onContextMenu : function(e, t)
7860 this.processEvent("contextmenu", e);
7863 processEvent : function(name, e)
7865 if (name != 'touchstart' ) {
7866 this.fireEvent(name, e);
7869 var t = e.getTarget();
7871 var cell = Roo.get(t);
7877 if(cell.findParent('tfoot', false, true)){
7881 if(cell.findParent('thead', false, true)){
7883 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7884 cell = Roo.get(t).findParent('th', false, true);
7886 Roo.log("failed to find th in thead?");
7887 Roo.log(e.getTarget());
7892 var cellIndex = cell.dom.cellIndex;
7894 var ename = name == 'touchstart' ? 'click' : name;
7895 this.fireEvent("header" + ename, this, cellIndex, e);
7900 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7901 cell = Roo.get(t).findParent('td', false, true);
7903 Roo.log("failed to find th in tbody?");
7904 Roo.log(e.getTarget());
7909 var row = cell.findParent('tr', false, true);
7910 var cellIndex = cell.dom.cellIndex;
7911 var rowIndex = row.dom.rowIndex - 1;
7915 this.fireEvent("row" + name, this, rowIndex, e);
7919 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7925 onMouseover : function(e, el)
7927 var cell = Roo.get(el);
7933 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7934 cell = cell.findParent('td', false, true);
7937 var row = cell.findParent('tr', false, true);
7938 var cellIndex = cell.dom.cellIndex;
7939 var rowIndex = row.dom.rowIndex - 1; // start from 0
7941 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945 onMouseout : function(e, el)
7947 var cell = Roo.get(el);
7953 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7954 cell = cell.findParent('td', false, true);
7957 var row = cell.findParent('tr', false, true);
7958 var cellIndex = cell.dom.cellIndex;
7959 var rowIndex = row.dom.rowIndex - 1; // start from 0
7961 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965 onClick : function(e, el)
7967 var cell = Roo.get(el);
7969 if(!cell || (!this.cellSelection && !this.rowSelection)){
7973 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7974 cell = cell.findParent('td', false, true);
7977 if(!cell || typeof(cell) == 'undefined'){
7981 var row = cell.findParent('tr', false, true);
7983 if(!row || typeof(row) == 'undefined'){
7987 var cellIndex = cell.dom.cellIndex;
7988 var rowIndex = this.getRowIndex(row);
7990 // why??? - should these not be based on SelectionModel?
7991 if(this.cellSelection){
7992 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7995 if(this.rowSelection){
7996 this.fireEvent('rowclick', this, row, rowIndex, e);
8002 onDblClick : function(e,el)
8004 var cell = Roo.get(el);
8006 if(!cell || (!this.cellSelection && !this.rowSelection)){
8010 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8011 cell = cell.findParent('td', false, true);
8014 if(!cell || typeof(cell) == 'undefined'){
8018 var row = cell.findParent('tr', false, true);
8020 if(!row || typeof(row) == 'undefined'){
8024 var cellIndex = cell.dom.cellIndex;
8025 var rowIndex = this.getRowIndex(row);
8027 if(this.cellSelection){
8028 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8031 if(this.rowSelection){
8032 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036 sort : function(e,el)
8038 var col = Roo.get(el);
8040 if(!col.hasClass('sortable')){
8044 var sort = col.attr('sort');
8047 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051 this.store.sortInfo = {field : sort, direction : dir};
8054 Roo.log("calling footer first");
8055 this.footer.onClick('first');
8058 this.store.load({ params : { start : 0 } });
8062 renderHeader : function()
8070 this.totalWidth = 0;
8072 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8074 var config = cm.config[i];
8078 cls : 'x-hcol-' + i,
8080 html: cm.getColumnHeader(i)
8085 if(typeof(config.sortable) != 'undefined' && config.sortable){
8087 c.html = '<i class="glyphicon"></i>' + c.html;
8090 // could use BS4 hidden-..-down
8092 if(typeof(config.lgHeader) != 'undefined'){
8093 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8096 if(typeof(config.mdHeader) != 'undefined'){
8097 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8100 if(typeof(config.smHeader) != 'undefined'){
8101 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8104 if(typeof(config.xsHeader) != 'undefined'){
8105 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8112 if(typeof(config.tooltip) != 'undefined'){
8113 c.tooltip = config.tooltip;
8116 if(typeof(config.colspan) != 'undefined'){
8117 c.colspan = config.colspan;
8120 if(typeof(config.hidden) != 'undefined' && config.hidden){
8121 c.style += ' display:none;';
8124 if(typeof(config.dataIndex) != 'undefined'){
8125 c.sort = config.dataIndex;
8130 if(typeof(config.align) != 'undefined' && config.align.length){
8131 c.style += ' text-align:' + config.align + ';';
8134 if(typeof(config.width) != 'undefined'){
8135 c.style += ' width:' + config.width + 'px;';
8136 this.totalWidth += config.width;
8138 this.totalWidth += 100; // assume minimum of 100 per column?
8141 if(typeof(config.cls) != 'undefined'){
8142 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8145 ['xs','sm','md','lg'].map(function(size){
8147 if(typeof(config[size]) == 'undefined'){
8151 if (!config[size]) { // 0 = hidden
8152 // BS 4 '0' is treated as hide that column and below.
8153 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157 c.cls += ' col-' + size + '-' + config[size] + (
8158 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8170 renderBody : function()
8180 colspan : this.cm.getColumnCount()
8190 renderFooter : function()
8200 colspan : this.cm.getColumnCount()
8214 // Roo.log('ds onload');
8219 var ds = this.store;
8221 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8223 if (_this.store.sortInfo) {
8225 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8226 e.select('i', true).addClass(['glyphicon-arrow-up']);
8229 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8230 e.select('i', true).addClass(['glyphicon-arrow-down']);
8235 var tbody = this.mainBody;
8237 if(ds.getCount() > 0){
8238 ds.data.each(function(d,rowIndex){
8239 var row = this.renderRow(cm, ds, rowIndex);
8241 tbody.createChild(row);
8245 if(row.cellObjects.length){
8246 Roo.each(row.cellObjects, function(r){
8247 _this.renderCellObject(r);
8254 var tfoot = this.el.select('tfoot', true).first();
8256 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8258 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8260 var total = this.ds.getTotalCount();
8262 if(this.footer.pageSize < total){
8263 this.mainFoot.show();
8267 Roo.each(this.el.select('tbody td', true).elements, function(e){
8268 e.on('mouseover', _this.onMouseover, _this);
8271 Roo.each(this.el.select('tbody td', true).elements, function(e){
8272 e.on('mouseout', _this.onMouseout, _this);
8274 this.fireEvent('rowsrendered', this);
8280 onUpdate : function(ds,record)
8282 this.refreshRow(record);
8286 onRemove : function(ds, record, index, isUpdate){
8287 if(isUpdate !== true){
8288 this.fireEvent("beforerowremoved", this, index, record);
8290 var bt = this.mainBody.dom;
8292 var rows = this.el.select('tbody > tr', true).elements;
8294 if(typeof(rows[index]) != 'undefined'){
8295 bt.removeChild(rows[index].dom);
8298 // if(bt.rows[index]){
8299 // bt.removeChild(bt.rows[index]);
8302 if(isUpdate !== true){
8303 //this.stripeRows(index);
8304 //this.syncRowHeights(index, index);
8306 this.fireEvent("rowremoved", this, index, record);
8310 onAdd : function(ds, records, rowIndex)
8312 //Roo.log('on Add called');
8313 // - note this does not handle multiple adding very well..
8314 var bt = this.mainBody.dom;
8315 for (var i =0 ; i < records.length;i++) {
8316 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8317 //Roo.log(records[i]);
8318 //Roo.log(this.store.getAt(rowIndex+i));
8319 this.insertRow(this.store, rowIndex + i, false);
8326 refreshRow : function(record){
8327 var ds = this.store, index;
8328 if(typeof record == 'number'){
8330 record = ds.getAt(index);
8332 index = ds.indexOf(record);
8334 return; // should not happen - but seems to
8337 this.insertRow(ds, index, true);
8339 this.onRemove(ds, record, index+1, true);
8341 //this.syncRowHeights(index, index);
8343 this.fireEvent("rowupdated", this, index, record);
8346 insertRow : function(dm, rowIndex, isUpdate){
8349 this.fireEvent("beforerowsinserted", this, rowIndex);
8351 //var s = this.getScrollState();
8352 var row = this.renderRow(this.cm, this.store, rowIndex);
8353 // insert before rowIndex..
8354 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358 if(row.cellObjects.length){
8359 Roo.each(row.cellObjects, function(r){
8360 _this.renderCellObject(r);
8365 this.fireEvent("rowsinserted", this, rowIndex);
8366 //this.syncRowHeights(firstRow, lastRow);
8367 //this.stripeRows(firstRow);
8374 getRowDom : function(rowIndex)
8376 var rows = this.el.select('tbody > tr', true).elements;
8378 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8381 // returns the object tree for a tr..
8384 renderRow : function(cm, ds, rowIndex)
8386 var d = ds.getAt(rowIndex);
8390 cls : 'x-row-' + rowIndex,
8394 var cellObjects = [];
8396 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8397 var config = cm.config[i];
8399 var renderer = cm.getRenderer(i);
8403 if(typeof(renderer) !== 'undefined'){
8404 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8406 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8407 // and are rendered into the cells after the row is rendered - using the id for the element.
8409 if(typeof(value) === 'object'){
8419 rowIndex : rowIndex,
8424 this.fireEvent('rowclass', this, rowcfg);
8428 cls : rowcfg.rowClass + ' x-col-' + i,
8430 html: (typeof(value) === 'object') ? '' : value
8437 if(typeof(config.colspan) != 'undefined'){
8438 td.colspan = config.colspan;
8441 if(typeof(config.hidden) != 'undefined' && config.hidden){
8442 td.style += ' display:none;';
8445 if(typeof(config.align) != 'undefined' && config.align.length){
8446 td.style += ' text-align:' + config.align + ';';
8448 if(typeof(config.valign) != 'undefined' && config.valign.length){
8449 td.style += ' vertical-align:' + config.valign + ';';
8452 if(typeof(config.width) != 'undefined'){
8453 td.style += ' width:' + config.width + 'px;';
8456 if(typeof(config.cursor) != 'undefined'){
8457 td.style += ' cursor:' + config.cursor + ';';
8460 if(typeof(config.cls) != 'undefined'){
8461 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8464 ['xs','sm','md','lg'].map(function(size){
8466 if(typeof(config[size]) == 'undefined'){
8472 if (!config[size]) { // 0 = hidden
8473 // BS 4 '0' is treated as hide that column and below.
8474 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478 td.cls += ' col-' + size + '-' + config[size] + (
8479 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8489 row.cellObjects = cellObjects;
8497 onBeforeLoad : function()
8506 this.el.select('tbody', true).first().dom.innerHTML = '';
8509 * Show or hide a row.
8510 * @param {Number} rowIndex to show or hide
8511 * @param {Boolean} state hide
8513 setRowVisibility : function(rowIndex, state)
8515 var bt = this.mainBody.dom;
8517 var rows = this.el.select('tbody > tr', true).elements;
8519 if(typeof(rows[rowIndex]) == 'undefined'){
8522 rows[rowIndex].dom.style.display = state ? '' : 'none';
8526 getSelectionModel : function(){
8528 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8530 return this.selModel;
8533 * Render the Roo.bootstrap object from renderder
8535 renderCellObject : function(r)
8539 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8541 var t = r.cfg.render(r.container);
8544 Roo.each(r.cfg.cn, function(c){
8546 container: t.getChildContainer(),
8549 _this.renderCellObject(child);
8554 getRowIndex : function(row)
8558 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8569 * Returns the grid's underlying element = used by panel.Grid
8570 * @return {Element} The element
8572 getGridEl : function(){
8576 * Forces a resize - used by panel.Grid
8577 * @return {Element} The element
8579 autoSize : function()
8581 //var ctr = Roo.get(this.container.dom.parentElement);
8582 var ctr = Roo.get(this.el.dom);
8584 var thd = this.getGridEl().select('thead',true).first();
8585 var tbd = this.getGridEl().select('tbody', true).first();
8586 var tfd = this.getGridEl().select('tfoot', true).first();
8588 var cw = ctr.getWidth();
8592 tbd.setWidth(ctr.getWidth());
8593 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8594 // this needs fixing for various usage - currently only hydra job advers I think..
8596 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8598 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8601 cw = Math.max(cw, this.totalWidth);
8602 this.getGridEl().select('tr',true).setWidth(cw);
8603 // resize 'expandable coloumn?
8605 return; // we doe not have a view in this design..
8608 onBodyScroll: function()
8610 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8612 this.mainHead.setStyle({
8613 'position' : 'relative',
8614 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8620 var scrollHeight = this.mainBody.dom.scrollHeight;
8622 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8624 var height = this.mainBody.getHeight();
8626 if(scrollHeight - height == scrollTop) {
8628 var total = this.ds.getTotalCount();
8630 if(this.footer.cursor + this.footer.pageSize < total){
8632 this.footer.ds.load({
8634 start : this.footer.cursor + this.footer.pageSize,
8635 limit : this.footer.pageSize
8645 onHeaderChange : function()
8647 var header = this.renderHeader();
8648 var table = this.el.select('table', true).first();
8650 this.mainHead.remove();
8651 this.mainHead = table.createChild(header, this.mainBody, false);
8654 onHiddenChange : function(colModel, colIndex, hidden)
8656 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8657 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8659 this.CSS.updateRule(thSelector, "display", "");
8660 this.CSS.updateRule(tdSelector, "display", "");
8663 this.CSS.updateRule(thSelector, "display", "none");
8664 this.CSS.updateRule(tdSelector, "display", "none");
8667 this.onHeaderChange();
8671 setColumnWidth: function(col_index, width)
8673 // width = "md-2 xs-2..."
8674 if(!this.colModel.config[col_index]) {
8678 var w = width.split(" ");
8680 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8682 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8685 for(var j = 0; j < w.length; j++) {
8691 var size_cls = w[j].split("-");
8693 if(!Number.isInteger(size_cls[1] * 1)) {
8697 if(!this.colModel.config[col_index][size_cls[0]]) {
8701 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8705 h_row[0].classList.replace(
8706 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8707 "col-"+size_cls[0]+"-"+size_cls[1]
8710 for(var i = 0; i < rows.length; i++) {
8712 var size_cls = w[j].split("-");
8714 if(!Number.isInteger(size_cls[1] * 1)) {
8718 if(!this.colModel.config[col_index][size_cls[0]]) {
8722 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8726 rows[i].classList.replace(
8727 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8728 "col-"+size_cls[0]+"-"+size_cls[1]
8732 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8747 * @class Roo.bootstrap.TableCell
8748 * @extends Roo.bootstrap.Component
8749 * Bootstrap TableCell class
8750 * @cfg {String} html cell contain text
8751 * @cfg {String} cls cell class
8752 * @cfg {String} tag cell tag (td|th) default td
8753 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8754 * @cfg {String} align Aligns the content in a cell
8755 * @cfg {String} axis Categorizes cells
8756 * @cfg {String} bgcolor Specifies the background color of a cell
8757 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8758 * @cfg {Number} colspan Specifies the number of columns a cell should span
8759 * @cfg {String} headers Specifies one or more header cells a cell is related to
8760 * @cfg {Number} height Sets the height of a cell
8761 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8762 * @cfg {Number} rowspan Sets the number of rows a cell should span
8763 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8764 * @cfg {String} valign Vertical aligns the content in a cell
8765 * @cfg {Number} width Specifies the width of a cell
8768 * Create a new TableCell
8769 * @param {Object} config The config object
8772 Roo.bootstrap.TableCell = function(config){
8773 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8776 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8796 getAutoCreate : function(){
8797 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8817 cfg.align=this.align
8823 cfg.bgcolor=this.bgcolor
8826 cfg.charoff=this.charoff
8829 cfg.colspan=this.colspan
8832 cfg.headers=this.headers
8835 cfg.height=this.height
8838 cfg.nowrap=this.nowrap
8841 cfg.rowspan=this.rowspan
8844 cfg.scope=this.scope
8847 cfg.valign=this.valign
8850 cfg.width=this.width
8869 * @class Roo.bootstrap.TableRow
8870 * @extends Roo.bootstrap.Component
8871 * Bootstrap TableRow class
8872 * @cfg {String} cls row class
8873 * @cfg {String} align Aligns the content in a table row
8874 * @cfg {String} bgcolor Specifies a background color for a table row
8875 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8876 * @cfg {String} valign Vertical aligns the content in a table row
8879 * Create a new TableRow
8880 * @param {Object} config The config object
8883 Roo.bootstrap.TableRow = function(config){
8884 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8887 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8895 getAutoCreate : function(){
8896 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8906 cfg.align = this.align;
8909 cfg.bgcolor = this.bgcolor;
8912 cfg.charoff = this.charoff;
8915 cfg.valign = this.valign;
8933 * @class Roo.bootstrap.TableBody
8934 * @extends Roo.bootstrap.Component
8935 * Bootstrap TableBody class
8936 * @cfg {String} cls element class
8937 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8938 * @cfg {String} align Aligns the content inside the element
8939 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8940 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8943 * Create a new TableBody
8944 * @param {Object} config The config object
8947 Roo.bootstrap.TableBody = function(config){
8948 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8951 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8959 getAutoCreate : function(){
8960 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8974 cfg.align = this.align;
8977 cfg.charoff = this.charoff;
8980 cfg.valign = this.valign;
8987 // initEvents : function()
8994 // this.store = Roo.factory(this.store, Roo.data);
8995 // this.store.on('load', this.onLoad, this);
8997 // this.store.load();
9001 // onLoad: function ()
9003 // this.fireEvent('load', this);
9013 * Ext JS Library 1.1.1
9014 * Copyright(c) 2006-2007, Ext JS, LLC.
9016 * Originally Released Under LGPL - original licence link has changed is not relivant.
9019 * <script type="text/javascript">
9022 // as we use this in bootstrap.
9023 Roo.namespace('Roo.form');
9025 * @class Roo.form.Action
9026 * Internal Class used to handle form actions
9028 * @param {Roo.form.BasicForm} el The form element or its id
9029 * @param {Object} config Configuration options
9034 // define the action interface
9035 Roo.form.Action = function(form, options){
9037 this.options = options || {};
9040 * Client Validation Failed
9043 Roo.form.Action.CLIENT_INVALID = 'client';
9045 * Server Validation Failed
9048 Roo.form.Action.SERVER_INVALID = 'server';
9050 * Connect to Server Failed
9053 Roo.form.Action.CONNECT_FAILURE = 'connect';
9055 * Reading Data from Server Failed
9058 Roo.form.Action.LOAD_FAILURE = 'load';
9060 Roo.form.Action.prototype = {
9062 failureType : undefined,
9063 response : undefined,
9067 run : function(options){
9072 success : function(response){
9077 handleResponse : function(response){
9081 // default connection failure
9082 failure : function(response){
9084 this.response = response;
9085 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9086 this.form.afterAction(this, false);
9089 processResponse : function(response){
9090 this.response = response;
9091 if(!response.responseText){
9094 this.result = this.handleResponse(response);
9098 // utility functions used internally
9099 getUrl : function(appendParams){
9100 var url = this.options.url || this.form.url || this.form.el.dom.action;
9102 var p = this.getParams();
9104 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9110 getMethod : function(){
9111 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9114 getParams : function(){
9115 var bp = this.form.baseParams;
9116 var p = this.options.params;
9118 if(typeof p == "object"){
9119 p = Roo.urlEncode(Roo.applyIf(p, bp));
9120 }else if(typeof p == 'string' && bp){
9121 p += '&' + Roo.urlEncode(bp);
9124 p = Roo.urlEncode(bp);
9129 createCallback : function(){
9131 success: this.success,
9132 failure: this.failure,
9134 timeout: (this.form.timeout*1000),
9135 upload: this.form.fileUpload ? this.success : undefined
9140 Roo.form.Action.Submit = function(form, options){
9141 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9144 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9147 haveProgress : false,
9148 uploadComplete : false,
9150 // uploadProgress indicator.
9151 uploadProgress : function()
9153 if (!this.form.progressUrl) {
9157 if (!this.haveProgress) {
9158 Roo.MessageBox.progress("Uploading", "Uploading");
9160 if (this.uploadComplete) {
9161 Roo.MessageBox.hide();
9165 this.haveProgress = true;
9167 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9169 var c = new Roo.data.Connection();
9171 url : this.form.progressUrl,
9176 success : function(req){
9177 //console.log(data);
9181 rdata = Roo.decode(req.responseText)
9183 Roo.log("Invalid data from server..");
9187 if (!rdata || !rdata.success) {
9189 Roo.MessageBox.alert(Roo.encode(rdata));
9192 var data = rdata.data;
9194 if (this.uploadComplete) {
9195 Roo.MessageBox.hide();
9200 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9201 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9204 this.uploadProgress.defer(2000,this);
9207 failure: function(data) {
9208 Roo.log('progress url failed ');
9219 // run get Values on the form, so it syncs any secondary forms.
9220 this.form.getValues();
9222 var o = this.options;
9223 var method = this.getMethod();
9224 var isPost = method == 'POST';
9225 if(o.clientValidation === false || this.form.isValid()){
9227 if (this.form.progressUrl) {
9228 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9229 (new Date() * 1) + '' + Math.random());
9234 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9235 form:this.form.el.dom,
9236 url:this.getUrl(!isPost),
9238 params:isPost ? this.getParams() : null,
9239 isUpload: this.form.fileUpload,
9240 formData : this.form.formData
9243 this.uploadProgress();
9245 }else if (o.clientValidation !== false){ // client validation failed
9246 this.failureType = Roo.form.Action.CLIENT_INVALID;
9247 this.form.afterAction(this, false);
9251 success : function(response)
9253 this.uploadComplete= true;
9254 if (this.haveProgress) {
9255 Roo.MessageBox.hide();
9259 var result = this.processResponse(response);
9260 if(result === true || result.success){
9261 this.form.afterAction(this, true);
9265 this.form.markInvalid(result.errors);
9266 this.failureType = Roo.form.Action.SERVER_INVALID;
9268 this.form.afterAction(this, false);
9270 failure : function(response)
9272 this.uploadComplete= true;
9273 if (this.haveProgress) {
9274 Roo.MessageBox.hide();
9277 this.response = response;
9278 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9279 this.form.afterAction(this, false);
9282 handleResponse : function(response){
9283 if(this.form.errorReader){
9284 var rs = this.form.errorReader.read(response);
9287 for(var i = 0, len = rs.records.length; i < len; i++) {
9288 var r = rs.records[i];
9292 if(errors.length < 1){
9296 success : rs.success,
9302 ret = Roo.decode(response.responseText);
9306 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9316 Roo.form.Action.Load = function(form, options){
9317 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9318 this.reader = this.form.reader;
9321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9326 Roo.Ajax.request(Roo.apply(
9327 this.createCallback(), {
9328 method:this.getMethod(),
9329 url:this.getUrl(false),
9330 params:this.getParams()
9334 success : function(response){
9336 var result = this.processResponse(response);
9337 if(result === true || !result.success || !result.data){
9338 this.failureType = Roo.form.Action.LOAD_FAILURE;
9339 this.form.afterAction(this, false);
9342 this.form.clearInvalid();
9343 this.form.setValues(result.data);
9344 this.form.afterAction(this, true);
9347 handleResponse : function(response){
9348 if(this.form.reader){
9349 var rs = this.form.reader.read(response);
9350 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9352 success : rs.success,
9356 return Roo.decode(response.responseText);
9360 Roo.form.Action.ACTION_TYPES = {
9361 'load' : Roo.form.Action.Load,
9362 'submit' : Roo.form.Action.Submit
9371 * @class Roo.bootstrap.Form
9372 * @extends Roo.bootstrap.Component
9373 * Bootstrap Form class
9374 * @cfg {String} method GET | POST (default POST)
9375 * @cfg {String} labelAlign top | left (default top)
9376 * @cfg {String} align left | right - for navbars
9377 * @cfg {Boolean} loadMask load mask when submit (default true)
9382 * @param {Object} config The config object
9386 Roo.bootstrap.Form = function(config){
9388 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9390 Roo.bootstrap.Form.popover.apply();
9394 * @event clientvalidation
9395 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9396 * @param {Form} this
9397 * @param {Boolean} valid true if the form has passed client-side validation
9399 clientvalidation: true,
9401 * @event beforeaction
9402 * Fires before any action is performed. Return false to cancel the action.
9403 * @param {Form} this
9404 * @param {Action} action The action to be performed
9408 * @event actionfailed
9409 * Fires when an action fails.
9410 * @param {Form} this
9411 * @param {Action} action The action that failed
9413 actionfailed : true,
9415 * @event actioncomplete
9416 * Fires when an action is completed.
9417 * @param {Form} this
9418 * @param {Action} action The action that completed
9420 actioncomplete : true
9424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9427 * @cfg {String} method
9428 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9433 * The URL to use for form actions if one isn't supplied in the action options.
9436 * @cfg {Boolean} fileUpload
9437 * Set to true if this form is a file upload.
9441 * @cfg {Object} baseParams
9442 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9446 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9450 * @cfg {Sting} align (left|right) for navbar forms
9455 activeAction : null,
9458 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9459 * element by passing it or its id or mask the form itself by passing in true.
9462 waitMsgTarget : false,
9467 * @cfg {Boolean} errorMask (true|false) default false
9472 * @cfg {Number} maskOffset Default 100
9477 * @cfg {Boolean} maskBody
9481 getAutoCreate : function(){
9485 method : this.method || 'POST',
9486 id : this.id || Roo.id(),
9489 if (this.parent().xtype.match(/^Nav/)) {
9490 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9494 if (this.labelAlign == 'left' ) {
9495 cfg.cls += ' form-horizontal';
9501 initEvents : function()
9503 this.el.on('submit', this.onSubmit, this);
9504 // this was added as random key presses on the form where triggering form submit.
9505 this.el.on('keypress', function(e) {
9506 if (e.getCharCode() != 13) {
9509 // we might need to allow it for textareas.. and some other items.
9510 // check e.getTarget().
9512 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9516 Roo.log("keypress blocked");
9524 onSubmit : function(e){
9529 * Returns true if client-side validation on the form is successful.
9532 isValid : function(){
9533 var items = this.getItems();
9537 items.each(function(f){
9543 Roo.log('invalid field: ' + f.name);
9547 if(!target && f.el.isVisible(true)){
9553 if(this.errorMask && !valid){
9554 Roo.bootstrap.Form.popover.mask(this, target);
9561 * Returns true if any fields in this form have changed since their original load.
9564 isDirty : function(){
9566 var items = this.getItems();
9567 items.each(function(f){
9577 * Performs a predefined action (submit or load) or custom actions you define on this form.
9578 * @param {String} actionName The name of the action type
9579 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9580 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9581 * accept other config options):
9583 Property Type Description
9584 ---------------- --------------- ----------------------------------------------------------------------------------
9585 url String The url for the action (defaults to the form's url)
9586 method String The form method to use (defaults to the form's method, or POST if not defined)
9587 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9588 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9589 validate the form on the client (defaults to false)
9591 * @return {BasicForm} this
9593 doAction : function(action, options){
9594 if(typeof action == 'string'){
9595 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9597 if(this.fireEvent('beforeaction', this, action) !== false){
9598 this.beforeAction(action);
9599 action.run.defer(100, action);
9605 beforeAction : function(action){
9606 var o = action.options;
9611 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9613 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9616 // not really supported yet.. ??
9618 //if(this.waitMsgTarget === true){
9619 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9620 //}else if(this.waitMsgTarget){
9621 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9622 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9624 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9630 afterAction : function(action, success){
9631 this.activeAction = null;
9632 var o = action.options;
9637 Roo.get(document.body).unmask();
9643 //if(this.waitMsgTarget === true){
9644 // this.el.unmask();
9645 //}else if(this.waitMsgTarget){
9646 // this.waitMsgTarget.unmask();
9648 // Roo.MessageBox.updateProgress(1);
9649 // Roo.MessageBox.hide();
9656 Roo.callback(o.success, o.scope, [this, action]);
9657 this.fireEvent('actioncomplete', this, action);
9661 // failure condition..
9662 // we have a scenario where updates need confirming.
9663 // eg. if a locking scenario exists..
9664 // we look for { errors : { needs_confirm : true }} in the response.
9666 (typeof(action.result) != 'undefined') &&
9667 (typeof(action.result.errors) != 'undefined') &&
9668 (typeof(action.result.errors.needs_confirm) != 'undefined')
9671 Roo.log("not supported yet");
9674 Roo.MessageBox.confirm(
9675 "Change requires confirmation",
9676 action.result.errorMsg,
9681 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9691 Roo.callback(o.failure, o.scope, [this, action]);
9692 // show an error message if no failed handler is set..
9693 if (!this.hasListener('actionfailed')) {
9694 Roo.log("need to add dialog support");
9696 Roo.MessageBox.alert("Error",
9697 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9698 action.result.errorMsg :
9699 "Saving Failed, please check your entries or try again"
9704 this.fireEvent('actionfailed', this, action);
9709 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9710 * @param {String} id The value to search for
9713 findField : function(id){
9714 var items = this.getItems();
9715 var field = items.get(id);
9717 items.each(function(f){
9718 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9725 return field || null;
9728 * Mark fields in this form invalid in bulk.
9729 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9730 * @return {BasicForm} this
9732 markInvalid : function(errors){
9733 if(errors instanceof Array){
9734 for(var i = 0, len = errors.length; i < len; i++){
9735 var fieldError = errors[i];
9736 var f = this.findField(fieldError.id);
9738 f.markInvalid(fieldError.msg);
9744 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9745 field.markInvalid(errors[id]);
9749 //Roo.each(this.childForms || [], function (f) {
9750 // f.markInvalid(errors);
9757 * Set values for fields in this form in bulk.
9758 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9759 * @return {BasicForm} this
9761 setValues : function(values){
9762 if(values instanceof Array){ // array of objects
9763 for(var i = 0, len = values.length; i < len; i++){
9765 var f = this.findField(v.id);
9767 f.setValue(v.value);
9768 if(this.trackResetOnLoad){
9769 f.originalValue = f.getValue();
9773 }else{ // object hash
9776 if(typeof values[id] != 'function' && (field = this.findField(id))){
9778 if (field.setFromData &&
9780 field.displayField &&
9781 // combos' with local stores can
9782 // be queried via setValue()
9783 // to set their value..
9784 (field.store && !field.store.isLocal)
9788 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9789 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9790 field.setFromData(sd);
9792 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9794 field.setFromData(values);
9797 field.setValue(values[id]);
9801 if(this.trackResetOnLoad){
9802 field.originalValue = field.getValue();
9808 //Roo.each(this.childForms || [], function (f) {
9809 // f.setValues(values);
9816 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9817 * they are returned as an array.
9818 * @param {Boolean} asString
9821 getValues : function(asString){
9822 //if (this.childForms) {
9823 // copy values from the child forms
9824 // Roo.each(this.childForms, function (f) {
9825 // this.setValues(f.getValues());
9831 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9832 if(asString === true){
9835 return Roo.urlDecode(fs);
9839 * Returns the fields in this form as an object with key/value pairs.
9840 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9843 getFieldValues : function(with_hidden)
9845 var items = this.getItems();
9847 items.each(function(f){
9853 var v = f.getValue();
9855 if (f.inputType =='radio') {
9856 if (typeof(ret[f.getName()]) == 'undefined') {
9857 ret[f.getName()] = ''; // empty..
9860 if (!f.el.dom.checked) {
9868 if(f.xtype == 'MoneyField'){
9869 ret[f.currencyName] = f.getCurrency();
9872 // not sure if this supported any more..
9873 if ((typeof(v) == 'object') && f.getRawValue) {
9874 v = f.getRawValue() ; // dates..
9876 // combo boxes where name != hiddenName...
9877 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9878 ret[f.name] = f.getRawValue();
9880 ret[f.getName()] = v;
9887 * Clears all invalid messages in this form.
9888 * @return {BasicForm} this
9890 clearInvalid : function(){
9891 var items = this.getItems();
9893 items.each(function(f){
9902 * @return {BasicForm} this
9905 var items = this.getItems();
9906 items.each(function(f){
9910 Roo.each(this.childForms || [], function (f) {
9918 getItems : function()
9920 var r=new Roo.util.MixedCollection(false, function(o){
9921 return o.id || (o.id = Roo.id());
9923 var iter = function(el) {
9930 Roo.each(el.items,function(e) {
9939 hideFields : function(items)
9941 Roo.each(items, function(i){
9943 var f = this.findField(i);
9954 showFields : function(items)
9956 Roo.each(items, function(i){
9958 var f = this.findField(i);
9971 Roo.apply(Roo.bootstrap.Form, {
9998 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9999 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10000 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10001 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10004 this.maskEl.top.enableDisplayMode("block");
10005 this.maskEl.left.enableDisplayMode("block");
10006 this.maskEl.bottom.enableDisplayMode("block");
10007 this.maskEl.right.enableDisplayMode("block");
10009 this.toolTip = new Roo.bootstrap.Tooltip({
10010 cls : 'roo-form-error-popover',
10012 'left' : ['r-l', [-2,0], 'right'],
10013 'right' : ['l-r', [2,0], 'left'],
10014 'bottom' : ['tl-bl', [0,2], 'top'],
10015 'top' : [ 'bl-tl', [0,-2], 'bottom']
10019 this.toolTip.render(Roo.get(document.body));
10021 this.toolTip.el.enableDisplayMode("block");
10023 Roo.get(document.body).on('click', function(){
10027 Roo.get(document.body).on('touchstart', function(){
10031 this.isApplied = true
10034 mask : function(form, target)
10038 this.target = target;
10040 if(!this.form.errorMask || !target.el){
10044 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10046 Roo.log(scrollable);
10048 var ot = this.target.el.calcOffsetsTo(scrollable);
10050 var scrollTo = ot[1] - this.form.maskOffset;
10052 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10054 scrollable.scrollTo('top', scrollTo);
10056 var box = this.target.el.getBox();
10058 var zIndex = Roo.bootstrap.Modal.zIndex++;
10061 this.maskEl.top.setStyle('position', 'absolute');
10062 this.maskEl.top.setStyle('z-index', zIndex);
10063 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10064 this.maskEl.top.setLeft(0);
10065 this.maskEl.top.setTop(0);
10066 this.maskEl.top.show();
10068 this.maskEl.left.setStyle('position', 'absolute');
10069 this.maskEl.left.setStyle('z-index', zIndex);
10070 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10071 this.maskEl.left.setLeft(0);
10072 this.maskEl.left.setTop(box.y - this.padding);
10073 this.maskEl.left.show();
10075 this.maskEl.bottom.setStyle('position', 'absolute');
10076 this.maskEl.bottom.setStyle('z-index', zIndex);
10077 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10078 this.maskEl.bottom.setLeft(0);
10079 this.maskEl.bottom.setTop(box.bottom + this.padding);
10080 this.maskEl.bottom.show();
10082 this.maskEl.right.setStyle('position', 'absolute');
10083 this.maskEl.right.setStyle('z-index', zIndex);
10084 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10085 this.maskEl.right.setLeft(box.right + this.padding);
10086 this.maskEl.right.setTop(box.y - this.padding);
10087 this.maskEl.right.show();
10089 this.toolTip.bindEl = this.target.el;
10091 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10093 var tip = this.target.blankText;
10095 if(this.target.getValue() !== '' ) {
10097 if (this.target.invalidText.length) {
10098 tip = this.target.invalidText;
10099 } else if (this.target.regexText.length){
10100 tip = this.target.regexText;
10104 this.toolTip.show(tip);
10106 this.intervalID = window.setInterval(function() {
10107 Roo.bootstrap.Form.popover.unmask();
10110 window.onwheel = function(){ return false;};
10112 (function(){ this.isMasked = true; }).defer(500, this);
10116 unmask : function()
10118 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10122 this.maskEl.top.setStyle('position', 'absolute');
10123 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10124 this.maskEl.top.hide();
10126 this.maskEl.left.setStyle('position', 'absolute');
10127 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10128 this.maskEl.left.hide();
10130 this.maskEl.bottom.setStyle('position', 'absolute');
10131 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10132 this.maskEl.bottom.hide();
10134 this.maskEl.right.setStyle('position', 'absolute');
10135 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10136 this.maskEl.right.hide();
10138 this.toolTip.hide();
10140 this.toolTip.el.hide();
10142 window.onwheel = function(){ return true;};
10144 if(this.intervalID){
10145 window.clearInterval(this.intervalID);
10146 this.intervalID = false;
10149 this.isMasked = false;
10159 * Ext JS Library 1.1.1
10160 * Copyright(c) 2006-2007, Ext JS, LLC.
10162 * Originally Released Under LGPL - original licence link has changed is not relivant.
10165 * <script type="text/javascript">
10168 * @class Roo.form.VTypes
10169 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10172 Roo.form.VTypes = function(){
10173 // closure these in so they are only created once.
10174 var alpha = /^[a-zA-Z_]+$/;
10175 var alphanum = /^[a-zA-Z0-9_]+$/;
10176 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10177 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10179 // All these messages and functions are configurable
10182 * The function used to validate email addresses
10183 * @param {String} value The email address
10185 'email' : function(v){
10186 return email.test(v);
10189 * The error text to display when the email validation function returns false
10192 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10194 * The keystroke filter mask to be applied on email input
10197 'emailMask' : /[a-z0-9_\.\-@]/i,
10200 * The function used to validate URLs
10201 * @param {String} value The URL
10203 'url' : function(v){
10204 return url.test(v);
10207 * The error text to display when the url validation function returns false
10210 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10213 * The function used to validate alpha values
10214 * @param {String} value The value
10216 'alpha' : function(v){
10217 return alpha.test(v);
10220 * The error text to display when the alpha validation function returns false
10223 'alphaText' : 'This field should only contain letters and _',
10225 * The keystroke filter mask to be applied on alpha input
10228 'alphaMask' : /[a-z_]/i,
10231 * The function used to validate alphanumeric values
10232 * @param {String} value The value
10234 'alphanum' : function(v){
10235 return alphanum.test(v);
10238 * The error text to display when the alphanumeric validation function returns false
10241 'alphanumText' : 'This field should only contain letters, numbers and _',
10243 * The keystroke filter mask to be applied on alphanumeric input
10246 'alphanumMask' : /[a-z0-9_]/i
10256 * @class Roo.bootstrap.Input
10257 * @extends Roo.bootstrap.Component
10258 * Bootstrap Input class
10259 * @cfg {Boolean} disabled is it disabled
10260 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10261 * @cfg {String} name name of the input
10262 * @cfg {string} fieldLabel - the label associated
10263 * @cfg {string} placeholder - placeholder to put in text.
10264 * @cfg {string} before - input group add on before
10265 * @cfg {string} after - input group add on after
10266 * @cfg {string} size - (lg|sm) or leave empty..
10267 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10268 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10269 * @cfg {Number} md colspan out of 12 for computer-sized screens
10270 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10271 * @cfg {string} value default value of the input
10272 * @cfg {Number} labelWidth set the width of label
10273 * @cfg {Number} labellg set the width of label (1-12)
10274 * @cfg {Number} labelmd set the width of label (1-12)
10275 * @cfg {Number} labelsm set the width of label (1-12)
10276 * @cfg {Number} labelxs set the width of label (1-12)
10277 * @cfg {String} labelAlign (top|left)
10278 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10279 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10280 * @cfg {String} indicatorpos (left|right) default left
10281 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10282 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10283 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10285 * @cfg {String} align (left|center|right) Default left
10286 * @cfg {Boolean} forceFeedback (true|false) Default false
10289 * Create a new Input
10290 * @param {Object} config The config object
10293 Roo.bootstrap.Input = function(config){
10295 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10300 * Fires when this field receives input focus.
10301 * @param {Roo.form.Field} this
10306 * Fires when this field loses input focus.
10307 * @param {Roo.form.Field} this
10311 * @event specialkey
10312 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10313 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10314 * @param {Roo.form.Field} this
10315 * @param {Roo.EventObject} e The event object
10320 * Fires just before the field blurs if the field value has changed.
10321 * @param {Roo.form.Field} this
10322 * @param {Mixed} newValue The new value
10323 * @param {Mixed} oldValue The original value
10328 * Fires after the field has been marked as invalid.
10329 * @param {Roo.form.Field} this
10330 * @param {String} msg The validation message
10335 * Fires after the field has been validated with no errors.
10336 * @param {Roo.form.Field} this
10341 * Fires after the key up
10342 * @param {Roo.form.Field} this
10343 * @param {Roo.EventObject} e The event Object
10349 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10351 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10352 automatic validation (defaults to "keyup").
10354 validationEvent : "keyup",
10356 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10358 validateOnBlur : true,
10360 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10362 validationDelay : 250,
10364 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10366 focusClass : "x-form-focus", // not needed???
10370 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10372 invalidClass : "has-warning",
10375 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10377 validClass : "has-success",
10380 * @cfg {Boolean} hasFeedback (true|false) default true
10382 hasFeedback : true,
10385 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10387 invalidFeedbackClass : "glyphicon-warning-sign",
10390 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10392 validFeedbackClass : "glyphicon-ok",
10395 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10397 selectOnFocus : false,
10400 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10404 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10409 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10411 disableKeyFilter : false,
10414 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10418 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10422 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10424 blankText : "Please complete this mandatory field",
10427 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10431 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10433 maxLength : Number.MAX_VALUE,
10435 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10437 minLengthText : "The minimum length for this field is {0}",
10439 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10441 maxLengthText : "The maximum length for this field is {0}",
10445 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10446 * If available, this function will be called only after the basic validators all return true, and will be passed the
10447 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10451 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10452 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10453 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10457 * @cfg {String} regexText -- Depricated - use Invalid Text
10462 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10468 autocomplete: false,
10472 inputType : 'text',
10475 placeholder: false,
10480 preventMark: false,
10481 isFormField : true,
10484 labelAlign : false,
10487 formatedValue : false,
10488 forceFeedback : false,
10490 indicatorpos : 'left',
10500 parentLabelAlign : function()
10503 while (parent.parent()) {
10504 parent = parent.parent();
10505 if (typeof(parent.labelAlign) !='undefined') {
10506 return parent.labelAlign;
10513 getAutoCreate : function()
10515 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10521 if(this.inputType != 'hidden'){
10522 cfg.cls = 'form-group' //input-group
10528 type : this.inputType,
10529 value : this.value,
10530 cls : 'form-control',
10531 placeholder : this.placeholder || '',
10532 autocomplete : this.autocomplete || 'new-password'
10534 if (this.inputType == 'file') {
10535 input.style = 'overflow:hidden'; // why not in CSS?
10538 if(this.capture.length){
10539 input.capture = this.capture;
10542 if(this.accept.length){
10543 input.accept = this.accept + "/*";
10547 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10550 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10551 input.maxLength = this.maxLength;
10554 if (this.disabled) {
10555 input.disabled=true;
10558 if (this.readOnly) {
10559 input.readonly=true;
10563 input.name = this.name;
10567 input.cls += ' input-' + this.size;
10571 ['xs','sm','md','lg'].map(function(size){
10572 if (settings[size]) {
10573 cfg.cls += ' col-' + size + '-' + settings[size];
10577 var inputblock = input;
10581 cls: 'glyphicon form-control-feedback'
10584 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10587 cls : 'has-feedback',
10595 if (this.before || this.after) {
10598 cls : 'input-group',
10602 if (this.before && typeof(this.before) == 'string') {
10604 inputblock.cn.push({
10606 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10610 if (this.before && typeof(this.before) == 'object') {
10611 this.before = Roo.factory(this.before);
10613 inputblock.cn.push({
10615 cls : 'roo-input-before input-group-prepend input-group-' +
10616 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10620 inputblock.cn.push(input);
10622 if (this.after && typeof(this.after) == 'string') {
10623 inputblock.cn.push({
10625 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10629 if (this.after && typeof(this.after) == 'object') {
10630 this.after = Roo.factory(this.after);
10632 inputblock.cn.push({
10634 cls : 'roo-input-after input-group-append input-group-' +
10635 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10639 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10640 inputblock.cls += ' has-feedback';
10641 inputblock.cn.push(feedback);
10646 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10647 tooltip : 'This field is required'
10649 if (this.allowBlank ) {
10650 indicator.style = this.allowBlank ? ' display:none' : '';
10652 if (align ==='left' && this.fieldLabel.length) {
10654 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10661 cls : 'control-label col-form-label',
10662 html : this.fieldLabel
10673 var labelCfg = cfg.cn[1];
10674 var contentCfg = cfg.cn[2];
10676 if(this.indicatorpos == 'right'){
10681 cls : 'control-label col-form-label',
10685 html : this.fieldLabel
10699 labelCfg = cfg.cn[0];
10700 contentCfg = cfg.cn[1];
10704 if(this.labelWidth > 12){
10705 labelCfg.style = "width: " + this.labelWidth + 'px';
10708 if(this.labelWidth < 13 && this.labelmd == 0){
10709 this.labelmd = this.labelWidth;
10712 if(this.labellg > 0){
10713 labelCfg.cls += ' col-lg-' + this.labellg;
10714 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10717 if(this.labelmd > 0){
10718 labelCfg.cls += ' col-md-' + this.labelmd;
10719 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10722 if(this.labelsm > 0){
10723 labelCfg.cls += ' col-sm-' + this.labelsm;
10724 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10727 if(this.labelxs > 0){
10728 labelCfg.cls += ' col-xs-' + this.labelxs;
10729 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10733 } else if ( this.fieldLabel.length) {
10740 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10741 tooltip : 'This field is required',
10742 style : this.allowBlank ? ' display:none' : ''
10746 //cls : 'input-group-addon',
10747 html : this.fieldLabel
10755 if(this.indicatorpos == 'right'){
10760 //cls : 'input-group-addon',
10761 html : this.fieldLabel
10766 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10767 tooltip : 'This field is required',
10768 style : this.allowBlank ? ' display:none' : ''
10788 if (this.parentType === 'Navbar' && this.parent().bar) {
10789 cfg.cls += ' navbar-form';
10792 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10793 // on BS4 we do this only if not form
10794 cfg.cls += ' navbar-form';
10802 * return the real input element.
10804 inputEl: function ()
10806 return this.el.select('input.form-control',true).first();
10809 tooltipEl : function()
10811 return this.inputEl();
10814 indicatorEl : function()
10816 if (Roo.bootstrap.version == 4) {
10817 return false; // not enabled in v4 yet.
10820 var indicator = this.el.select('i.roo-required-indicator',true).first();
10830 setDisabled : function(v)
10832 var i = this.inputEl().dom;
10834 i.removeAttribute('disabled');
10838 i.setAttribute('disabled','true');
10840 initEvents : function()
10843 this.inputEl().on("keydown" , this.fireKey, this);
10844 this.inputEl().on("focus", this.onFocus, this);
10845 this.inputEl().on("blur", this.onBlur, this);
10847 this.inputEl().relayEvent('keyup', this);
10849 this.indicator = this.indicatorEl();
10851 if(this.indicator){
10852 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10855 // reference to original value for reset
10856 this.originalValue = this.getValue();
10857 //Roo.form.TextField.superclass.initEvents.call(this);
10858 if(this.validationEvent == 'keyup'){
10859 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10860 this.inputEl().on('keyup', this.filterValidation, this);
10862 else if(this.validationEvent !== false){
10863 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10866 if(this.selectOnFocus){
10867 this.on("focus", this.preFocus, this);
10870 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10871 this.inputEl().on("keypress", this.filterKeys, this);
10873 this.inputEl().relayEvent('keypress', this);
10876 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10877 this.el.on("click", this.autoSize, this);
10880 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10881 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10884 if (typeof(this.before) == 'object') {
10885 this.before.render(this.el.select('.roo-input-before',true).first());
10887 if (typeof(this.after) == 'object') {
10888 this.after.render(this.el.select('.roo-input-after',true).first());
10891 this.inputEl().on('change', this.onChange, this);
10894 filterValidation : function(e){
10895 if(!e.isNavKeyPress()){
10896 this.validationTask.delay(this.validationDelay);
10900 * Validates the field value
10901 * @return {Boolean} True if the value is valid, else false
10903 validate : function(){
10904 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10905 if(this.disabled || this.validateValue(this.getRawValue())){
10910 this.markInvalid();
10916 * Validates a value according to the field's validation rules and marks the field as invalid
10917 * if the validation fails
10918 * @param {Mixed} value The value to validate
10919 * @return {Boolean} True if the value is valid, else false
10921 validateValue : function(value)
10923 if(this.getVisibilityEl().hasClass('hidden')){
10927 if(value.length < 1) { // if it's blank
10928 if(this.allowBlank){
10934 if(value.length < this.minLength){
10937 if(value.length > this.maxLength){
10941 var vt = Roo.form.VTypes;
10942 if(!vt[this.vtype](value, this)){
10946 if(typeof this.validator == "function"){
10947 var msg = this.validator(value);
10951 if (typeof(msg) == 'string') {
10952 this.invalidText = msg;
10956 if(this.regex && !this.regex.test(value)){
10964 fireKey : function(e){
10965 //Roo.log('field ' + e.getKey());
10966 if(e.isNavKeyPress()){
10967 this.fireEvent("specialkey", this, e);
10970 focus : function (selectText){
10972 this.inputEl().focus();
10973 if(selectText === true){
10974 this.inputEl().dom.select();
10980 onFocus : function(){
10981 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10982 // this.el.addClass(this.focusClass);
10984 if(!this.hasFocus){
10985 this.hasFocus = true;
10986 this.startValue = this.getValue();
10987 this.fireEvent("focus", this);
10991 beforeBlur : Roo.emptyFn,
10995 onBlur : function(){
10997 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10998 //this.el.removeClass(this.focusClass);
11000 this.hasFocus = false;
11001 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11004 var v = this.getValue();
11005 if(String(v) !== String(this.startValue)){
11006 this.fireEvent('change', this, v, this.startValue);
11008 this.fireEvent("blur", this);
11011 onChange : function(e)
11013 var v = this.getValue();
11014 if(String(v) !== String(this.startValue)){
11015 this.fireEvent('change', this, v, this.startValue);
11021 * Resets the current field value to the originally loaded value and clears any validation messages
11023 reset : function(){
11024 this.setValue(this.originalValue);
11028 * Returns the name of the field
11029 * @return {Mixed} name The name field
11031 getName: function(){
11035 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11036 * @return {Mixed} value The field value
11038 getValue : function(){
11040 var v = this.inputEl().getValue();
11045 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11046 * @return {Mixed} value The field value
11048 getRawValue : function(){
11049 var v = this.inputEl().getValue();
11055 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11056 * @param {Mixed} value The value to set
11058 setRawValue : function(v){
11059 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11062 selectText : function(start, end){
11063 var v = this.getRawValue();
11065 start = start === undefined ? 0 : start;
11066 end = end === undefined ? v.length : end;
11067 var d = this.inputEl().dom;
11068 if(d.setSelectionRange){
11069 d.setSelectionRange(start, end);
11070 }else if(d.createTextRange){
11071 var range = d.createTextRange();
11072 range.moveStart("character", start);
11073 range.moveEnd("character", v.length-end);
11080 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11081 * @param {Mixed} value The value to set
11083 setValue : function(v){
11086 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11092 processValue : function(value){
11093 if(this.stripCharsRe){
11094 var newValue = value.replace(this.stripCharsRe, '');
11095 if(newValue !== value){
11096 this.setRawValue(newValue);
11103 preFocus : function(){
11105 if(this.selectOnFocus){
11106 this.inputEl().dom.select();
11109 filterKeys : function(e){
11110 var k = e.getKey();
11111 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11114 var c = e.getCharCode(), cc = String.fromCharCode(c);
11115 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11118 if(!this.maskRe.test(cc)){
11123 * Clear any invalid styles/messages for this field
11125 clearInvalid : function(){
11127 if(!this.el || this.preventMark){ // not rendered
11132 this.el.removeClass([this.invalidClass, 'is-invalid']);
11134 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11136 var feedback = this.el.select('.form-control-feedback', true).first();
11139 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11144 if(this.indicator){
11145 this.indicator.removeClass('visible');
11146 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11149 this.fireEvent('valid', this);
11153 * Mark this field as valid
11155 markValid : function()
11157 if(!this.el || this.preventMark){ // not rendered...
11161 this.el.removeClass([this.invalidClass, this.validClass]);
11162 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11164 var feedback = this.el.select('.form-control-feedback', true).first();
11167 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11170 if(this.indicator){
11171 this.indicator.removeClass('visible');
11172 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11180 if(this.allowBlank && !this.getRawValue().length){
11183 if (Roo.bootstrap.version == 3) {
11184 this.el.addClass(this.validClass);
11186 this.inputEl().addClass('is-valid');
11189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11191 var feedback = this.el.select('.form-control-feedback', true).first();
11194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11195 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11200 this.fireEvent('valid', this);
11204 * Mark this field as invalid
11205 * @param {String} msg The validation message
11207 markInvalid : function(msg)
11209 if(!this.el || this.preventMark){ // not rendered
11213 this.el.removeClass([this.invalidClass, this.validClass]);
11214 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11216 var feedback = this.el.select('.form-control-feedback', true).first();
11219 this.el.select('.form-control-feedback', true).first().removeClass(
11220 [this.invalidFeedbackClass, this.validFeedbackClass]);
11227 if(this.allowBlank && !this.getRawValue().length){
11231 if(this.indicator){
11232 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11233 this.indicator.addClass('visible');
11235 if (Roo.bootstrap.version == 3) {
11236 this.el.addClass(this.invalidClass);
11238 this.inputEl().addClass('is-invalid');
11243 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11245 var feedback = this.el.select('.form-control-feedback', true).first();
11248 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11250 if(this.getValue().length || this.forceFeedback){
11251 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11258 this.fireEvent('invalid', this, msg);
11261 SafariOnKeyDown : function(event)
11263 // this is a workaround for a password hang bug on chrome/ webkit.
11264 if (this.inputEl().dom.type != 'password') {
11268 var isSelectAll = false;
11270 if(this.inputEl().dom.selectionEnd > 0){
11271 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11273 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11274 event.preventDefault();
11279 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11281 event.preventDefault();
11282 // this is very hacky as keydown always get's upper case.
11284 var cc = String.fromCharCode(event.getCharCode());
11285 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11289 adjustWidth : function(tag, w){
11290 tag = tag.toLowerCase();
11291 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11292 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11293 if(tag == 'input'){
11296 if(tag == 'textarea'){
11299 }else if(Roo.isOpera){
11300 if(tag == 'input'){
11303 if(tag == 'textarea'){
11311 setFieldLabel : function(v)
11313 if(!this.rendered){
11317 if(this.indicatorEl()){
11318 var ar = this.el.select('label > span',true);
11320 if (ar.elements.length) {
11321 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11322 this.fieldLabel = v;
11326 var br = this.el.select('label',true);
11328 if(br.elements.length) {
11329 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11330 this.fieldLabel = v;
11334 Roo.log('Cannot Found any of label > span || label in input');
11338 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11339 this.fieldLabel = v;
11354 * @class Roo.bootstrap.TextArea
11355 * @extends Roo.bootstrap.Input
11356 * Bootstrap TextArea class
11357 * @cfg {Number} cols Specifies the visible width of a text area
11358 * @cfg {Number} rows Specifies the visible number of lines in a text area
11359 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11360 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11361 * @cfg {string} html text
11364 * Create a new TextArea
11365 * @param {Object} config The config object
11368 Roo.bootstrap.TextArea = function(config){
11369 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11373 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11383 getAutoCreate : function(){
11385 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11391 if(this.inputType != 'hidden'){
11392 cfg.cls = 'form-group' //input-group
11400 value : this.value || '',
11401 html: this.html || '',
11402 cls : 'form-control',
11403 placeholder : this.placeholder || ''
11407 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11408 input.maxLength = this.maxLength;
11412 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11416 input.cols = this.cols;
11419 if (this.readOnly) {
11420 input.readonly = true;
11424 input.name = this.name;
11428 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11432 ['xs','sm','md','lg'].map(function(size){
11433 if (settings[size]) {
11434 cfg.cls += ' col-' + size + '-' + settings[size];
11438 var inputblock = input;
11440 if(this.hasFeedback && !this.allowBlank){
11444 cls: 'glyphicon form-control-feedback'
11448 cls : 'has-feedback',
11457 if (this.before || this.after) {
11460 cls : 'input-group',
11464 inputblock.cn.push({
11466 cls : 'input-group-addon',
11471 inputblock.cn.push(input);
11473 if(this.hasFeedback && !this.allowBlank){
11474 inputblock.cls += ' has-feedback';
11475 inputblock.cn.push(feedback);
11479 inputblock.cn.push({
11481 cls : 'input-group-addon',
11488 if (align ==='left' && this.fieldLabel.length) {
11493 cls : 'control-label',
11494 html : this.fieldLabel
11505 if(this.labelWidth > 12){
11506 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11509 if(this.labelWidth < 13 && this.labelmd == 0){
11510 this.labelmd = this.labelWidth;
11513 if(this.labellg > 0){
11514 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11515 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11518 if(this.labelmd > 0){
11519 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11520 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11523 if(this.labelsm > 0){
11524 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11525 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11528 if(this.labelxs > 0){
11529 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11530 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11533 } else if ( this.fieldLabel.length) {
11538 //cls : 'input-group-addon',
11539 html : this.fieldLabel
11557 if (this.disabled) {
11558 input.disabled=true;
11565 * return the real textarea element.
11567 inputEl: function ()
11569 return this.el.select('textarea.form-control',true).first();
11573 * Clear any invalid styles/messages for this field
11575 clearInvalid : function()
11578 if(!this.el || this.preventMark){ // not rendered
11582 var label = this.el.select('label', true).first();
11583 var icon = this.el.select('i.fa-star', true).first();
11588 this.el.removeClass( this.validClass);
11589 this.inputEl().removeClass('is-invalid');
11591 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11593 var feedback = this.el.select('.form-control-feedback', true).first();
11596 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11601 this.fireEvent('valid', this);
11605 * Mark this field as valid
11607 markValid : function()
11609 if(!this.el || this.preventMark){ // not rendered
11613 this.el.removeClass([this.invalidClass, this.validClass]);
11614 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11616 var feedback = this.el.select('.form-control-feedback', true).first();
11619 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622 if(this.disabled || this.allowBlank){
11626 var label = this.el.select('label', true).first();
11627 var icon = this.el.select('i.fa-star', true).first();
11632 if (Roo.bootstrap.version == 3) {
11633 this.el.addClass(this.validClass);
11635 this.inputEl().addClass('is-valid');
11639 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11641 var feedback = this.el.select('.form-control-feedback', true).first();
11644 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11645 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650 this.fireEvent('valid', this);
11654 * Mark this field as invalid
11655 * @param {String} msg The validation message
11657 markInvalid : function(msg)
11659 if(!this.el || this.preventMark){ // not rendered
11663 this.el.removeClass([this.invalidClass, this.validClass]);
11664 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11666 var feedback = this.el.select('.form-control-feedback', true).first();
11669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11672 if(this.disabled || this.allowBlank){
11676 var label = this.el.select('label', true).first();
11677 var icon = this.el.select('i.fa-star', true).first();
11679 if(!this.getValue().length && label && !icon){
11680 this.el.createChild({
11682 cls : 'text-danger fa fa-lg fa-star',
11683 tooltip : 'This field is required',
11684 style : 'margin-right:5px;'
11688 if (Roo.bootstrap.version == 3) {
11689 this.el.addClass(this.invalidClass);
11691 this.inputEl().addClass('is-invalid');
11694 // fixme ... this may be depricated need to test..
11695 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11697 var feedback = this.el.select('.form-control-feedback', true).first();
11700 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11702 if(this.getValue().length || this.forceFeedback){
11703 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11710 this.fireEvent('invalid', this, msg);
11718 * trigger field - base class for combo..
11723 * @class Roo.bootstrap.TriggerField
11724 * @extends Roo.bootstrap.Input
11725 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11726 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11727 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11728 * for which you can provide a custom implementation. For example:
11730 var trigger = new Roo.bootstrap.TriggerField();
11731 trigger.onTriggerClick = myTriggerFn;
11732 trigger.applyTo('my-field');
11735 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11736 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11737 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11738 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11739 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11742 * Create a new TriggerField.
11743 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11744 * to the base TextField)
11746 Roo.bootstrap.TriggerField = function(config){
11747 this.mimicing = false;
11748 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11751 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11753 * @cfg {String} triggerClass A CSS class to apply to the trigger
11756 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11761 * @cfg {Boolean} removable (true|false) special filter default false
11765 /** @cfg {Boolean} grow @hide */
11766 /** @cfg {Number} growMin @hide */
11767 /** @cfg {Number} growMax @hide */
11773 autoSize: Roo.emptyFn,
11777 deferHeight : true,
11780 actionMode : 'wrap',
11785 getAutoCreate : function(){
11787 var align = this.labelAlign || this.parentLabelAlign();
11792 cls: 'form-group' //input-group
11799 type : this.inputType,
11800 cls : 'form-control',
11801 autocomplete: 'new-password',
11802 placeholder : this.placeholder || ''
11806 input.name = this.name;
11809 input.cls += ' input-' + this.size;
11812 if (this.disabled) {
11813 input.disabled=true;
11816 var inputblock = input;
11818 if(this.hasFeedback && !this.allowBlank){
11822 cls: 'glyphicon form-control-feedback'
11825 if(this.removable && !this.editable ){
11827 cls : 'has-feedback',
11833 cls : 'roo-combo-removable-btn close'
11840 cls : 'has-feedback',
11849 if(this.removable && !this.editable ){
11851 cls : 'roo-removable',
11857 cls : 'roo-combo-removable-btn close'
11864 if (this.before || this.after) {
11867 cls : 'input-group',
11871 inputblock.cn.push({
11873 cls : 'input-group-addon input-group-prepend input-group-text',
11878 inputblock.cn.push(input);
11880 if(this.hasFeedback && !this.allowBlank){
11881 inputblock.cls += ' has-feedback';
11882 inputblock.cn.push(feedback);
11886 inputblock.cn.push({
11888 cls : 'input-group-addon input-group-append input-group-text',
11897 var ibwrap = inputblock;
11902 cls: 'roo-select2-choices',
11906 cls: 'roo-select2-search-field',
11918 cls: 'roo-select2-container input-group',
11923 cls: 'form-hidden-field'
11929 if(!this.multiple && this.showToggleBtn){
11935 if (this.caret != false) {
11938 cls: 'fa fa-' + this.caret
11945 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11947 Roo.bootstrap.version == 3 ? caret : '',
11950 cls: 'combobox-clear',
11964 combobox.cls += ' roo-select2-container-multi';
11968 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11969 tooltip : 'This field is required'
11971 if (Roo.bootstrap.version == 4) {
11974 style : 'display:none'
11979 if (align ==='left' && this.fieldLabel.length) {
11981 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11988 cls : 'control-label',
11989 html : this.fieldLabel
12001 var labelCfg = cfg.cn[1];
12002 var contentCfg = cfg.cn[2];
12004 if(this.indicatorpos == 'right'){
12009 cls : 'control-label',
12013 html : this.fieldLabel
12027 labelCfg = cfg.cn[0];
12028 contentCfg = cfg.cn[1];
12031 if(this.labelWidth > 12){
12032 labelCfg.style = "width: " + this.labelWidth + 'px';
12035 if(this.labelWidth < 13 && this.labelmd == 0){
12036 this.labelmd = this.labelWidth;
12039 if(this.labellg > 0){
12040 labelCfg.cls += ' col-lg-' + this.labellg;
12041 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12044 if(this.labelmd > 0){
12045 labelCfg.cls += ' col-md-' + this.labelmd;
12046 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12049 if(this.labelsm > 0){
12050 labelCfg.cls += ' col-sm-' + this.labelsm;
12051 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12054 if(this.labelxs > 0){
12055 labelCfg.cls += ' col-xs-' + this.labelxs;
12056 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12059 } else if ( this.fieldLabel.length) {
12060 // Roo.log(" label");
12065 //cls : 'input-group-addon',
12066 html : this.fieldLabel
12074 if(this.indicatorpos == 'right'){
12082 html : this.fieldLabel
12096 // Roo.log(" no label && no align");
12103 ['xs','sm','md','lg'].map(function(size){
12104 if (settings[size]) {
12105 cfg.cls += ' col-' + size + '-' + settings[size];
12116 onResize : function(w, h){
12117 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12118 // if(typeof w == 'number'){
12119 // var x = w - this.trigger.getWidth();
12120 // this.inputEl().setWidth(this.adjustWidth('input', x));
12121 // this.trigger.setStyle('left', x+'px');
12126 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12129 getResizeEl : function(){
12130 return this.inputEl();
12134 getPositionEl : function(){
12135 return this.inputEl();
12139 alignErrorIcon : function(){
12140 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12144 initEvents : function(){
12148 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12149 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12150 if(!this.multiple && this.showToggleBtn){
12151 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12152 if(this.hideTrigger){
12153 this.trigger.setDisplayed(false);
12155 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12159 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12162 if(this.removable && !this.editable && !this.tickable){
12163 var close = this.closeTriggerEl();
12166 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12167 close.on('click', this.removeBtnClick, this, close);
12171 //this.trigger.addClassOnOver('x-form-trigger-over');
12172 //this.trigger.addClassOnClick('x-form-trigger-click');
12175 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12179 closeTriggerEl : function()
12181 var close = this.el.select('.roo-combo-removable-btn', true).first();
12182 return close ? close : false;
12185 removeBtnClick : function(e, h, el)
12187 e.preventDefault();
12189 if(this.fireEvent("remove", this) !== false){
12191 this.fireEvent("afterremove", this)
12195 createList : function()
12197 this.list = Roo.get(document.body).createChild({
12198 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12199 cls: 'typeahead typeahead-long dropdown-menu',
12200 style: 'display:none'
12203 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12208 initTrigger : function(){
12213 onDestroy : function(){
12215 this.trigger.removeAllListeners();
12216 // this.trigger.remove();
12219 // this.wrap.remove();
12221 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12225 onFocus : function(){
12226 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12228 if(!this.mimicing){
12229 this.wrap.addClass('x-trigger-wrap-focus');
12230 this.mimicing = true;
12231 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12232 if(this.monitorTab){
12233 this.el.on("keydown", this.checkTab, this);
12240 checkTab : function(e){
12241 if(e.getKey() == e.TAB){
12242 this.triggerBlur();
12247 onBlur : function(){
12252 mimicBlur : function(e, t){
12254 if(!this.wrap.contains(t) && this.validateBlur()){
12255 this.triggerBlur();
12261 triggerBlur : function(){
12262 this.mimicing = false;
12263 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12264 if(this.monitorTab){
12265 this.el.un("keydown", this.checkTab, this);
12267 //this.wrap.removeClass('x-trigger-wrap-focus');
12268 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12272 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12273 validateBlur : function(e, t){
12278 onDisable : function(){
12279 this.inputEl().dom.disabled = true;
12280 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12282 // this.wrap.addClass('x-item-disabled');
12287 onEnable : function(){
12288 this.inputEl().dom.disabled = false;
12289 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12291 // this.el.removeClass('x-item-disabled');
12296 onShow : function(){
12297 var ae = this.getActionEl();
12300 ae.dom.style.display = '';
12301 ae.dom.style.visibility = 'visible';
12307 onHide : function(){
12308 var ae = this.getActionEl();
12309 ae.dom.style.display = 'none';
12313 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12314 * by an implementing function.
12316 * @param {EventObject} e
12318 onTriggerClick : Roo.emptyFn
12326 * @class Roo.bootstrap.CardUploader
12327 * @extends Roo.bootstrap.Button
12328 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12329 * @cfg {Number} errorTimeout default 3000
12330 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12331 * @cfg {Array} html The button text.
12335 * Create a new CardUploader
12336 * @param {Object} config The config object
12339 Roo.bootstrap.CardUploader = function(config){
12343 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12346 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12356 errorTimeout : 3000,
12360 fileCollection : false,
12363 getAutoCreate : function()
12367 cls :'form-group' ,
12372 //cls : 'input-group-addon',
12373 html : this.fieldLabel
12380 value : this.value,
12381 cls : 'd-none form-control'
12386 multiple : 'multiple',
12388 cls : 'd-none roo-card-upload-selector'
12392 cls : 'roo-card-uploader-button-container w-100 mb-2'
12395 cls : 'card-columns roo-card-uploader-container'
12405 getChildContainer : function() /// what children are added to.
12407 return this.containerEl;
12410 getButtonContainer : function() /// what children are added to.
12412 return this.el.select(".roo-card-uploader-button-container").first();
12415 initEvents : function()
12418 Roo.bootstrap.Input.prototype.initEvents.call(this);
12422 xns: Roo.bootstrap,
12425 container_method : 'getButtonContainer' ,
12426 html : this.html, // fix changable?
12429 'click' : function(btn, e) {
12438 this.urlAPI = (window.createObjectURL && window) ||
12439 (window.URL && URL.revokeObjectURL && URL) ||
12440 (window.webkitURL && webkitURL);
12445 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12447 this.selectorEl.on('change', this.onFileSelected, this);
12450 this.images.forEach(function(img) {
12453 this.images = false;
12455 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12461 onClick : function(e)
12463 e.preventDefault();
12465 this.selectorEl.dom.click();
12469 onFileSelected : function(e)
12471 e.preventDefault();
12473 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12477 Roo.each(this.selectorEl.dom.files, function(file){
12478 this.addFile(file);
12487 addFile : function(file)
12490 if(typeof(file) === 'string'){
12491 throw "Add file by name?"; // should not happen
12495 if(!file || !this.urlAPI){
12505 var url = _this.urlAPI.createObjectURL( file);
12508 id : Roo.bootstrap.CardUploader.ID--,
12509 is_uploaded : false,
12512 mimetype : file.type,
12519 addCard : function (data)
12521 // hidden input element?
12522 // if the file is not an image...
12523 //then we need to use something other that and header_image
12528 xns : Roo.bootstrap,
12529 xtype : 'CardFooter',
12532 xns : Roo.bootstrap,
12538 xns : Roo.bootstrap,
12540 html : String.format("<small>{0}</small>", data.title),
12541 cls : 'col-11 text-left',
12546 click : function() {
12547 this.downloadCard(data.id)
12553 xns : Roo.bootstrap,
12561 click : function() {
12562 t.removeCard(data.id)
12574 var cn = this.addxtype(
12577 xns : Roo.bootstrap,
12580 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12581 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12582 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12587 initEvents : function() {
12588 Roo.bootstrap.Card.prototype.initEvents.call(this);
12589 this.imgEl = this.el.select('.card-img-top').first();
12591 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12592 this.imgEl.set({ 'pointer' : 'cursor' });
12601 // dont' really need ot update items.
12602 // this.items.push(cn);
12603 this.fileCollection.add(cn);
12604 this.updateInput();
12607 removeCard : function(id)
12610 var card = this.fileCollection.get(id);
12611 card.data.is_deleted = 1;
12612 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12613 this.fileCollection.remove(card);
12614 //this.items = this.items.filter(function(e) { return e != card });
12615 // dont' really need ot update items.
12616 card.el.dom.parentNode.removeChild(card.el.dom);
12621 this.fileCollection.each(function(card) {
12622 card.el.dom.parentNode.removeChild(card.el.dom);
12624 this.fileCollection.clear();
12625 this.updateInput();
12628 updateInput : function()
12631 this.fileCollection.each(function(e) {
12635 this.inputEl().dom.value = JSON.stringify(data);
12642 Roo.bootstrap.CardUploader.ID = -1;/*
12644 * Ext JS Library 1.1.1
12645 * Copyright(c) 2006-2007, Ext JS, LLC.
12647 * Originally Released Under LGPL - original licence link has changed is not relivant.
12650 * <script type="text/javascript">
12655 * @class Roo.data.SortTypes
12657 * Defines the default sorting (casting?) comparison functions used when sorting data.
12659 Roo.data.SortTypes = {
12661 * Default sort that does nothing
12662 * @param {Mixed} s The value being converted
12663 * @return {Mixed} The comparison value
12665 none : function(s){
12670 * The regular expression used to strip tags
12674 stripTagsRE : /<\/?[^>]+>/gi,
12677 * Strips all HTML tags to sort on text only
12678 * @param {Mixed} s The value being converted
12679 * @return {String} The comparison value
12681 asText : function(s){
12682 return String(s).replace(this.stripTagsRE, "");
12686 * Strips all HTML tags to sort on text only - Case insensitive
12687 * @param {Mixed} s The value being converted
12688 * @return {String} The comparison value
12690 asUCText : function(s){
12691 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12695 * Case insensitive string
12696 * @param {Mixed} s The value being converted
12697 * @return {String} The comparison value
12699 asUCString : function(s) {
12700 return String(s).toUpperCase();
12705 * @param {Mixed} s The value being converted
12706 * @return {Number} The comparison value
12708 asDate : function(s) {
12712 if(s instanceof Date){
12713 return s.getTime();
12715 return Date.parse(String(s));
12720 * @param {Mixed} s The value being converted
12721 * @return {Float} The comparison value
12723 asFloat : function(s) {
12724 var val = parseFloat(String(s).replace(/,/g, ""));
12733 * @param {Mixed} s The value being converted
12734 * @return {Number} The comparison value
12736 asInt : function(s) {
12737 var val = parseInt(String(s).replace(/,/g, ""));
12745 * Ext JS Library 1.1.1
12746 * Copyright(c) 2006-2007, Ext JS, LLC.
12748 * Originally Released Under LGPL - original licence link has changed is not relivant.
12751 * <script type="text/javascript">
12755 * @class Roo.data.Record
12756 * Instances of this class encapsulate both record <em>definition</em> information, and record
12757 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12758 * to access Records cached in an {@link Roo.data.Store} object.<br>
12760 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12761 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12764 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12766 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12767 * {@link #create}. The parameters are the same.
12768 * @param {Array} data An associative Array of data values keyed by the field name.
12769 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12770 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12771 * not specified an integer id is generated.
12773 Roo.data.Record = function(data, id){
12774 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12779 * Generate a constructor for a specific record layout.
12780 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12781 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12782 * Each field definition object may contain the following properties: <ul>
12783 * <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,
12784 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12785 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12786 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12787 * is being used, then this is a string containing the javascript expression to reference the data relative to
12788 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12789 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12790 * this may be omitted.</p></li>
12791 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12792 * <ul><li>auto (Default, implies no conversion)</li>
12797 * <li>date</li></ul></p></li>
12798 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12799 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12800 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12801 * by the Reader into an object that will be stored in the Record. It is passed the
12802 * following parameters:<ul>
12803 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12805 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12807 * <br>usage:<br><pre><code>
12808 var TopicRecord = Roo.data.Record.create(
12809 {name: 'title', mapping: 'topic_title'},
12810 {name: 'author', mapping: 'username'},
12811 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12812 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12813 {name: 'lastPoster', mapping: 'user2'},
12814 {name: 'excerpt', mapping: 'post_text'}
12817 var myNewRecord = new TopicRecord({
12818 title: 'Do my job please',
12821 lastPost: new Date(),
12822 lastPoster: 'Animal',
12823 excerpt: 'No way dude!'
12825 myStore.add(myNewRecord);
12830 Roo.data.Record.create = function(o){
12831 var f = function(){
12832 f.superclass.constructor.apply(this, arguments);
12834 Roo.extend(f, Roo.data.Record);
12835 var p = f.prototype;
12836 p.fields = new Roo.util.MixedCollection(false, function(field){
12839 for(var i = 0, len = o.length; i < len; i++){
12840 p.fields.add(new Roo.data.Field(o[i]));
12842 f.getField = function(name){
12843 return p.fields.get(name);
12848 Roo.data.Record.AUTO_ID = 1000;
12849 Roo.data.Record.EDIT = 'edit';
12850 Roo.data.Record.REJECT = 'reject';
12851 Roo.data.Record.COMMIT = 'commit';
12853 Roo.data.Record.prototype = {
12855 * Readonly flag - true if this record has been modified.
12864 join : function(store){
12865 this.store = store;
12869 * Set the named field to the specified value.
12870 * @param {String} name The name of the field to set.
12871 * @param {Object} value The value to set the field to.
12873 set : function(name, value){
12874 if(this.data[name] == value){
12878 if(!this.modified){
12879 this.modified = {};
12881 if(typeof this.modified[name] == 'undefined'){
12882 this.modified[name] = this.data[name];
12884 this.data[name] = value;
12885 if(!this.editing && this.store){
12886 this.store.afterEdit(this);
12891 * Get the value of the named field.
12892 * @param {String} name The name of the field to get the value of.
12893 * @return {Object} The value of the field.
12895 get : function(name){
12896 return this.data[name];
12900 beginEdit : function(){
12901 this.editing = true;
12902 this.modified = {};
12906 cancelEdit : function(){
12907 this.editing = false;
12908 delete this.modified;
12912 endEdit : function(){
12913 this.editing = false;
12914 if(this.dirty && this.store){
12915 this.store.afterEdit(this);
12920 * Usually called by the {@link Roo.data.Store} which owns the Record.
12921 * Rejects all changes made to the Record since either creation, or the last commit operation.
12922 * Modified fields are reverted to their original values.
12924 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12925 * of reject operations.
12927 reject : function(){
12928 var m = this.modified;
12930 if(typeof m[n] != "function"){
12931 this.data[n] = m[n];
12934 this.dirty = false;
12935 delete this.modified;
12936 this.editing = false;
12938 this.store.afterReject(this);
12943 * Usually called by the {@link Roo.data.Store} which owns the Record.
12944 * Commits all changes made to the Record since either creation, or the last commit operation.
12946 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12947 * of commit operations.
12949 commit : function(){
12950 this.dirty = false;
12951 delete this.modified;
12952 this.editing = false;
12954 this.store.afterCommit(this);
12959 hasError : function(){
12960 return this.error != null;
12964 clearError : function(){
12969 * Creates a copy of this record.
12970 * @param {String} id (optional) A new record id if you don't want to use this record's id
12973 copy : function(newId) {
12974 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12978 * Ext JS Library 1.1.1
12979 * Copyright(c) 2006-2007, Ext JS, LLC.
12981 * Originally Released Under LGPL - original licence link has changed is not relivant.
12984 * <script type="text/javascript">
12990 * @class Roo.data.Store
12991 * @extends Roo.util.Observable
12992 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12993 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12995 * 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
12996 * has no knowledge of the format of the data returned by the Proxy.<br>
12998 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12999 * instances from the data object. These records are cached and made available through accessor functions.
13001 * Creates a new Store.
13002 * @param {Object} config A config object containing the objects needed for the Store to access data,
13003 * and read the data into Records.
13005 Roo.data.Store = function(config){
13006 this.data = new Roo.util.MixedCollection(false);
13007 this.data.getKey = function(o){
13010 this.baseParams = {};
13012 this.paramNames = {
13017 "multisort" : "_multisort"
13020 if(config && config.data){
13021 this.inlineData = config.data;
13022 delete config.data;
13025 Roo.apply(this, config);
13027 if(this.reader){ // reader passed
13028 this.reader = Roo.factory(this.reader, Roo.data);
13029 this.reader.xmodule = this.xmodule || false;
13030 if(!this.recordType){
13031 this.recordType = this.reader.recordType;
13033 if(this.reader.onMetaChange){
13034 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13038 if(this.recordType){
13039 this.fields = this.recordType.prototype.fields;
13041 this.modified = [];
13045 * @event datachanged
13046 * Fires when the data cache has changed, and a widget which is using this Store
13047 * as a Record cache should refresh its view.
13048 * @param {Store} this
13050 datachanged : true,
13052 * @event metachange
13053 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13054 * @param {Store} this
13055 * @param {Object} meta The JSON metadata
13060 * Fires when Records have been added to the Store
13061 * @param {Store} this
13062 * @param {Roo.data.Record[]} records The array of Records added
13063 * @param {Number} index The index at which the record(s) were added
13068 * Fires when a Record has been removed from the Store
13069 * @param {Store} this
13070 * @param {Roo.data.Record} record The Record that was removed
13071 * @param {Number} index The index at which the record was removed
13076 * Fires when a Record has been updated
13077 * @param {Store} this
13078 * @param {Roo.data.Record} record The Record that was updated
13079 * @param {String} operation The update operation being performed. Value may be one of:
13081 Roo.data.Record.EDIT
13082 Roo.data.Record.REJECT
13083 Roo.data.Record.COMMIT
13089 * Fires when the data cache has been cleared.
13090 * @param {Store} this
13094 * @event beforeload
13095 * Fires before a request is made for a new data object. If the beforeload handler returns false
13096 * the load action will be canceled.
13097 * @param {Store} this
13098 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13102 * @event beforeloadadd
13103 * Fires after a new set of Records has been loaded.
13104 * @param {Store} this
13105 * @param {Roo.data.Record[]} records The Records that were loaded
13106 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13108 beforeloadadd : true,
13111 * Fires after a new set of Records has been loaded, before they are added to the store.
13112 * @param {Store} this
13113 * @param {Roo.data.Record[]} records The Records that were loaded
13114 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13115 * @params {Object} return from reader
13119 * @event loadexception
13120 * Fires if an exception occurs in the Proxy during loading.
13121 * Called with the signature of the Proxy's "loadexception" event.
13122 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13125 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13126 * @param {Object} load options
13127 * @param {Object} jsonData from your request (normally this contains the Exception)
13129 loadexception : true
13133 this.proxy = Roo.factory(this.proxy, Roo.data);
13134 this.proxy.xmodule = this.xmodule || false;
13135 this.relayEvents(this.proxy, ["loadexception"]);
13137 this.sortToggle = {};
13138 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13140 Roo.data.Store.superclass.constructor.call(this);
13142 if(this.inlineData){
13143 this.loadData(this.inlineData);
13144 delete this.inlineData;
13148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13150 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13151 * without a remote query - used by combo/forms at present.
13155 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13158 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13161 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13162 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13165 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13166 * on any HTTP request
13169 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13172 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13176 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13177 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13179 remoteSort : false,
13182 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13183 * loaded or when a record is removed. (defaults to false).
13185 pruneModifiedRecords : false,
13188 lastOptions : null,
13191 * Add Records to the Store and fires the add event.
13192 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13194 add : function(records){
13195 records = [].concat(records);
13196 for(var i = 0, len = records.length; i < len; i++){
13197 records[i].join(this);
13199 var index = this.data.length;
13200 this.data.addAll(records);
13201 this.fireEvent("add", this, records, index);
13205 * Remove a Record from the Store and fires the remove event.
13206 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13208 remove : function(record){
13209 var index = this.data.indexOf(record);
13210 this.data.removeAt(index);
13212 if(this.pruneModifiedRecords){
13213 this.modified.remove(record);
13215 this.fireEvent("remove", this, record, index);
13219 * Remove all Records from the Store and fires the clear event.
13221 removeAll : function(){
13223 if(this.pruneModifiedRecords){
13224 this.modified = [];
13226 this.fireEvent("clear", this);
13230 * Inserts Records to the Store at the given index and fires the add event.
13231 * @param {Number} index The start index at which to insert the passed Records.
13232 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13234 insert : function(index, records){
13235 records = [].concat(records);
13236 for(var i = 0, len = records.length; i < len; i++){
13237 this.data.insert(index, records[i]);
13238 records[i].join(this);
13240 this.fireEvent("add", this, records, index);
13244 * Get the index within the cache of the passed Record.
13245 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13246 * @return {Number} The index of the passed Record. Returns -1 if not found.
13248 indexOf : function(record){
13249 return this.data.indexOf(record);
13253 * Get the index within the cache of the Record with the passed id.
13254 * @param {String} id The id of the Record to find.
13255 * @return {Number} The index of the Record. Returns -1 if not found.
13257 indexOfId : function(id){
13258 return this.data.indexOfKey(id);
13262 * Get the Record with the specified id.
13263 * @param {String} id The id of the Record to find.
13264 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13266 getById : function(id){
13267 return this.data.key(id);
13271 * Get the Record at the specified index.
13272 * @param {Number} index The index of the Record to find.
13273 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13275 getAt : function(index){
13276 return this.data.itemAt(index);
13280 * Returns a range of Records between specified indices.
13281 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13282 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13283 * @return {Roo.data.Record[]} An array of Records
13285 getRange : function(start, end){
13286 return this.data.getRange(start, end);
13290 storeOptions : function(o){
13291 o = Roo.apply({}, o);
13294 this.lastOptions = o;
13298 * Loads the Record cache from the configured Proxy using the configured Reader.
13300 * If using remote paging, then the first load call must specify the <em>start</em>
13301 * and <em>limit</em> properties in the options.params property to establish the initial
13302 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13304 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13305 * and this call will return before the new data has been loaded. Perform any post-processing
13306 * in a callback function, or in a "load" event handler.</strong>
13308 * @param {Object} options An object containing properties which control loading options:<ul>
13309 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13310 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13311 * passed the following arguments:<ul>
13312 * <li>r : Roo.data.Record[]</li>
13313 * <li>options: Options object from the load call</li>
13314 * <li>success: Boolean success indicator</li></ul></li>
13315 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13316 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13319 load : function(options){
13320 options = options || {};
13321 if(this.fireEvent("beforeload", this, options) !== false){
13322 this.storeOptions(options);
13323 var p = Roo.apply(options.params || {}, this.baseParams);
13324 // if meta was not loaded from remote source.. try requesting it.
13325 if (!this.reader.metaFromRemote) {
13326 p._requestMeta = 1;
13328 if(this.sortInfo && this.remoteSort){
13329 var pn = this.paramNames;
13330 p[pn["sort"]] = this.sortInfo.field;
13331 p[pn["dir"]] = this.sortInfo.direction;
13333 if (this.multiSort) {
13334 var pn = this.paramNames;
13335 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13338 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13343 * Reloads the Record cache from the configured Proxy using the configured Reader and
13344 * the options from the last load operation performed.
13345 * @param {Object} options (optional) An object containing properties which may override the options
13346 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13347 * the most recently used options are reused).
13349 reload : function(options){
13350 this.load(Roo.applyIf(options||{}, this.lastOptions));
13354 // Called as a callback by the Reader during a load operation.
13355 loadRecords : function(o, options, success){
13356 if(!o || success === false){
13357 if(success !== false){
13358 this.fireEvent("load", this, [], options, o);
13360 if(options.callback){
13361 options.callback.call(options.scope || this, [], options, false);
13365 // if data returned failure - throw an exception.
13366 if (o.success === false) {
13367 // show a message if no listener is registered.
13368 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13369 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13371 // loadmask wil be hooked into this..
13372 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13375 var r = o.records, t = o.totalRecords || r.length;
13377 this.fireEvent("beforeloadadd", this, r, options, o);
13379 if(!options || options.add !== true){
13380 if(this.pruneModifiedRecords){
13381 this.modified = [];
13383 for(var i = 0, len = r.length; i < len; i++){
13387 this.data = this.snapshot;
13388 delete this.snapshot;
13391 this.data.addAll(r);
13392 this.totalLength = t;
13394 this.fireEvent("datachanged", this);
13396 this.totalLength = Math.max(t, this.data.length+r.length);
13400 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13402 var e = new Roo.data.Record({});
13404 e.set(this.parent.displayField, this.parent.emptyTitle);
13405 e.set(this.parent.valueField, '');
13410 this.fireEvent("load", this, r, options, o);
13411 if(options.callback){
13412 options.callback.call(options.scope || this, r, options, true);
13418 * Loads data from a passed data block. A Reader which understands the format of the data
13419 * must have been configured in the constructor.
13420 * @param {Object} data The data block from which to read the Records. The format of the data expected
13421 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13422 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13424 loadData : function(o, append){
13425 var r = this.reader.readRecords(o);
13426 this.loadRecords(r, {add: append}, true);
13430 * using 'cn' the nested child reader read the child array into it's child stores.
13431 * @param {Object} rec The record with a 'children array
13433 loadDataFromChildren : function(rec)
13435 this.loadData(this.reader.toLoadData(rec));
13440 * Gets the number of cached records.
13442 * <em>If using paging, this may not be the total size of the dataset. If the data object
13443 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13444 * the data set size</em>
13446 getCount : function(){
13447 return this.data.length || 0;
13451 * Gets the total number of records in the dataset as returned by the server.
13453 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13454 * the dataset size</em>
13456 getTotalCount : function(){
13457 return this.totalLength || 0;
13461 * Returns the sort state of the Store as an object with two properties:
13463 field {String} The name of the field by which the Records are sorted
13464 direction {String} The sort order, "ASC" or "DESC"
13467 getSortState : function(){
13468 return this.sortInfo;
13472 applySort : function(){
13473 if(this.sortInfo && !this.remoteSort){
13474 var s = this.sortInfo, f = s.field;
13475 var st = this.fields.get(f).sortType;
13476 var fn = function(r1, r2){
13477 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13478 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13480 this.data.sort(s.direction, fn);
13481 if(this.snapshot && this.snapshot != this.data){
13482 this.snapshot.sort(s.direction, fn);
13488 * Sets the default sort column and order to be used by the next load operation.
13489 * @param {String} fieldName The name of the field to sort by.
13490 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13492 setDefaultSort : function(field, dir){
13493 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13497 * Sort the Records.
13498 * If remote sorting is used, the sort is performed on the server, and the cache is
13499 * reloaded. If local sorting is used, the cache is sorted internally.
13500 * @param {String} fieldName The name of the field to sort by.
13501 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13503 sort : function(fieldName, dir){
13504 var f = this.fields.get(fieldName);
13506 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13508 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13509 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13514 this.sortToggle[f.name] = dir;
13515 this.sortInfo = {field: f.name, direction: dir};
13516 if(!this.remoteSort){
13518 this.fireEvent("datachanged", this);
13520 this.load(this.lastOptions);
13525 * Calls the specified function for each of the Records in the cache.
13526 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13527 * Returning <em>false</em> aborts and exits the iteration.
13528 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13530 each : function(fn, scope){
13531 this.data.each(fn, scope);
13535 * Gets all records modified since the last commit. Modified records are persisted across load operations
13536 * (e.g., during paging).
13537 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13539 getModifiedRecords : function(){
13540 return this.modified;
13544 createFilterFn : function(property, value, anyMatch){
13545 if(!value.exec){ // not a regex
13546 value = String(value);
13547 if(value.length == 0){
13550 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13552 return function(r){
13553 return value.test(r.data[property]);
13558 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13559 * @param {String} property A field on your records
13560 * @param {Number} start The record index to start at (defaults to 0)
13561 * @param {Number} end The last record index to include (defaults to length - 1)
13562 * @return {Number} The sum
13564 sum : function(property, start, end){
13565 var rs = this.data.items, v = 0;
13566 start = start || 0;
13567 end = (end || end === 0) ? end : rs.length-1;
13569 for(var i = start; i <= end; i++){
13570 v += (rs[i].data[property] || 0);
13576 * Filter the records by a specified property.
13577 * @param {String} field A field on your records
13578 * @param {String/RegExp} value Either a string that the field
13579 * should start with or a RegExp to test against the field
13580 * @param {Boolean} anyMatch True to match any part not just the beginning
13582 filter : function(property, value, anyMatch){
13583 var fn = this.createFilterFn(property, value, anyMatch);
13584 return fn ? this.filterBy(fn) : this.clearFilter();
13588 * Filter by a function. The specified function will be called with each
13589 * record in this data source. If the function returns true the record is included,
13590 * otherwise it is filtered.
13591 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13592 * @param {Object} scope (optional) The scope of the function (defaults to this)
13594 filterBy : function(fn, scope){
13595 this.snapshot = this.snapshot || this.data;
13596 this.data = this.queryBy(fn, scope||this);
13597 this.fireEvent("datachanged", this);
13601 * Query the records by a specified property.
13602 * @param {String} field A field on your records
13603 * @param {String/RegExp} value Either a string that the field
13604 * should start with or a RegExp to test against the field
13605 * @param {Boolean} anyMatch True to match any part not just the beginning
13606 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13608 query : function(property, value, anyMatch){
13609 var fn = this.createFilterFn(property, value, anyMatch);
13610 return fn ? this.queryBy(fn) : this.data.clone();
13614 * Query by a function. The specified function will be called with each
13615 * record in this data source. If the function returns true the record is included
13617 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13618 * @param {Object} scope (optional) The scope of the function (defaults to this)
13619 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13621 queryBy : function(fn, scope){
13622 var data = this.snapshot || this.data;
13623 return data.filterBy(fn, scope||this);
13627 * Collects unique values for a particular dataIndex from this store.
13628 * @param {String} dataIndex The property to collect
13629 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13630 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13631 * @return {Array} An array of the unique values
13633 collect : function(dataIndex, allowNull, bypassFilter){
13634 var d = (bypassFilter === true && this.snapshot) ?
13635 this.snapshot.items : this.data.items;
13636 var v, sv, r = [], l = {};
13637 for(var i = 0, len = d.length; i < len; i++){
13638 v = d[i].data[dataIndex];
13640 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13649 * Revert to a view of the Record cache with no filtering applied.
13650 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13652 clearFilter : function(suppressEvent){
13653 if(this.snapshot && this.snapshot != this.data){
13654 this.data = this.snapshot;
13655 delete this.snapshot;
13656 if(suppressEvent !== true){
13657 this.fireEvent("datachanged", this);
13663 afterEdit : function(record){
13664 if(this.modified.indexOf(record) == -1){
13665 this.modified.push(record);
13667 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13671 afterReject : function(record){
13672 this.modified.remove(record);
13673 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13677 afterCommit : function(record){
13678 this.modified.remove(record);
13679 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13683 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13684 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13686 commitChanges : function(){
13687 var m = this.modified.slice(0);
13688 this.modified = [];
13689 for(var i = 0, len = m.length; i < len; i++){
13695 * Cancel outstanding changes on all changed records.
13697 rejectChanges : function(){
13698 var m = this.modified.slice(0);
13699 this.modified = [];
13700 for(var i = 0, len = m.length; i < len; i++){
13705 onMetaChange : function(meta, rtype, o){
13706 this.recordType = rtype;
13707 this.fields = rtype.prototype.fields;
13708 delete this.snapshot;
13709 this.sortInfo = meta.sortInfo || this.sortInfo;
13710 this.modified = [];
13711 this.fireEvent('metachange', this, this.reader.meta);
13714 moveIndex : function(data, type)
13716 var index = this.indexOf(data);
13718 var newIndex = index + type;
13722 this.insert(newIndex, data);
13727 * Ext JS Library 1.1.1
13728 * Copyright(c) 2006-2007, Ext JS, LLC.
13730 * Originally Released Under LGPL - original licence link has changed is not relivant.
13733 * <script type="text/javascript">
13737 * @class Roo.data.SimpleStore
13738 * @extends Roo.data.Store
13739 * Small helper class to make creating Stores from Array data easier.
13740 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13741 * @cfg {Array} fields An array of field definition objects, or field name strings.
13742 * @cfg {Object} an existing reader (eg. copied from another store)
13743 * @cfg {Array} data The multi-dimensional array of data
13745 * @param {Object} config
13747 Roo.data.SimpleStore = function(config)
13749 Roo.data.SimpleStore.superclass.constructor.call(this, {
13751 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13754 Roo.data.Record.create(config.fields)
13756 proxy : new Roo.data.MemoryProxy(config.data)
13760 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13762 * Ext JS Library 1.1.1
13763 * Copyright(c) 2006-2007, Ext JS, LLC.
13765 * Originally Released Under LGPL - original licence link has changed is not relivant.
13768 * <script type="text/javascript">
13773 * @extends Roo.data.Store
13774 * @class Roo.data.JsonStore
13775 * Small helper class to make creating Stores for JSON data easier. <br/>
13777 var store = new Roo.data.JsonStore({
13778 url: 'get-images.php',
13780 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13783 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13784 * JsonReader and HttpProxy (unless inline data is provided).</b>
13785 * @cfg {Array} fields An array of field definition objects, or field name strings.
13787 * @param {Object} config
13789 Roo.data.JsonStore = function(c){
13790 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13791 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13792 reader: new Roo.data.JsonReader(c, c.fields)
13795 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13797 * Ext JS Library 1.1.1
13798 * Copyright(c) 2006-2007, Ext JS, LLC.
13800 * Originally Released Under LGPL - original licence link has changed is not relivant.
13803 * <script type="text/javascript">
13807 Roo.data.Field = function(config){
13808 if(typeof config == "string"){
13809 config = {name: config};
13811 Roo.apply(this, config);
13814 this.type = "auto";
13817 var st = Roo.data.SortTypes;
13818 // named sortTypes are supported, here we look them up
13819 if(typeof this.sortType == "string"){
13820 this.sortType = st[this.sortType];
13823 // set default sortType for strings and dates
13824 if(!this.sortType){
13827 this.sortType = st.asUCString;
13830 this.sortType = st.asDate;
13833 this.sortType = st.none;
13838 var stripRe = /[\$,%]/g;
13840 // prebuilt conversion function for this field, instead of
13841 // switching every time we're reading a value
13843 var cv, dateFormat = this.dateFormat;
13848 cv = function(v){ return v; };
13851 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13855 return v !== undefined && v !== null && v !== '' ?
13856 parseInt(String(v).replace(stripRe, ""), 10) : '';
13861 return v !== undefined && v !== null && v !== '' ?
13862 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13867 cv = function(v){ return v === true || v === "true" || v == 1; };
13874 if(v instanceof Date){
13878 if(dateFormat == "timestamp"){
13879 return new Date(v*1000);
13881 return Date.parseDate(v, dateFormat);
13883 var parsed = Date.parse(v);
13884 return parsed ? new Date(parsed) : null;
13893 Roo.data.Field.prototype = {
13901 * Ext JS Library 1.1.1
13902 * Copyright(c) 2006-2007, Ext JS, LLC.
13904 * Originally Released Under LGPL - original licence link has changed is not relivant.
13907 * <script type="text/javascript">
13910 // Base class for reading structured data from a data source. This class is intended to be
13911 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13914 * @class Roo.data.DataReader
13915 * Base class for reading structured data from a data source. This class is intended to be
13916 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13919 Roo.data.DataReader = function(meta, recordType){
13923 this.recordType = recordType instanceof Array ?
13924 Roo.data.Record.create(recordType) : recordType;
13927 Roo.data.DataReader.prototype = {
13930 readerType : 'Data',
13932 * Create an empty record
13933 * @param {Object} data (optional) - overlay some values
13934 * @return {Roo.data.Record} record created.
13936 newRow : function(d) {
13938 this.recordType.prototype.fields.each(function(c) {
13940 case 'int' : da[c.name] = 0; break;
13941 case 'date' : da[c.name] = new Date(); break;
13942 case 'float' : da[c.name] = 0.0; break;
13943 case 'boolean' : da[c.name] = false; break;
13944 default : da[c.name] = ""; break;
13948 return new this.recordType(Roo.apply(da, d));
13954 * Ext JS Library 1.1.1
13955 * Copyright(c) 2006-2007, Ext JS, LLC.
13957 * Originally Released Under LGPL - original licence link has changed is not relivant.
13960 * <script type="text/javascript">
13964 * @class Roo.data.DataProxy
13965 * @extends Roo.data.Observable
13966 * This class is an abstract base class for implementations which provide retrieval of
13967 * unformatted data objects.<br>
13969 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13970 * (of the appropriate type which knows how to parse the data object) to provide a block of
13971 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13973 * Custom implementations must implement the load method as described in
13974 * {@link Roo.data.HttpProxy#load}.
13976 Roo.data.DataProxy = function(){
13979 * @event beforeload
13980 * Fires before a network request is made to retrieve a data object.
13981 * @param {Object} This DataProxy object.
13982 * @param {Object} params The params parameter to the load function.
13987 * Fires before the load method's callback is called.
13988 * @param {Object} This DataProxy object.
13989 * @param {Object} o The data object.
13990 * @param {Object} arg The callback argument object passed to the load function.
13994 * @event loadexception
13995 * Fires if an Exception occurs during data retrieval.
13996 * @param {Object} This DataProxy object.
13997 * @param {Object} o The data object.
13998 * @param {Object} arg The callback argument object passed to the load function.
13999 * @param {Object} e The Exception.
14001 loadexception : true
14003 Roo.data.DataProxy.superclass.constructor.call(this);
14006 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14009 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14013 * Ext JS Library 1.1.1
14014 * Copyright(c) 2006-2007, Ext JS, LLC.
14016 * Originally Released Under LGPL - original licence link has changed is not relivant.
14019 * <script type="text/javascript">
14022 * @class Roo.data.MemoryProxy
14023 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14024 * to the Reader when its load method is called.
14026 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14028 Roo.data.MemoryProxy = function(data){
14032 Roo.data.MemoryProxy.superclass.constructor.call(this);
14036 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14039 * Load data from the requested source (in this case an in-memory
14040 * data object passed to the constructor), read the data object into
14041 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14042 * process that block using the passed callback.
14043 * @param {Object} params This parameter is not used by the MemoryProxy class.
14044 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14045 * object into a block of Roo.data.Records.
14046 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14047 * The function must be passed <ul>
14048 * <li>The Record block object</li>
14049 * <li>The "arg" argument from the load function</li>
14050 * <li>A boolean success indicator</li>
14052 * @param {Object} scope The scope in which to call the callback
14053 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14055 load : function(params, reader, callback, scope, arg){
14056 params = params || {};
14059 result = reader.readRecords(params.data ? params.data :this.data);
14061 this.fireEvent("loadexception", this, arg, null, e);
14062 callback.call(scope, null, arg, false);
14065 callback.call(scope, result, arg, true);
14069 update : function(params, records){
14074 * Ext JS Library 1.1.1
14075 * Copyright(c) 2006-2007, Ext JS, LLC.
14077 * Originally Released Under LGPL - original licence link has changed is not relivant.
14080 * <script type="text/javascript">
14083 * @class Roo.data.HttpProxy
14084 * @extends Roo.data.DataProxy
14085 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14086 * configured to reference a certain URL.<br><br>
14088 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14089 * from which the running page was served.<br><br>
14091 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14093 * Be aware that to enable the browser to parse an XML document, the server must set
14094 * the Content-Type header in the HTTP response to "text/xml".
14096 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14097 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14098 * will be used to make the request.
14100 Roo.data.HttpProxy = function(conn){
14101 Roo.data.HttpProxy.superclass.constructor.call(this);
14102 // is conn a conn config or a real conn?
14104 this.useAjax = !conn || !conn.events;
14108 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14109 // thse are take from connection...
14112 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14115 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14116 * extra parameters to each request made by this object. (defaults to undefined)
14119 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14120 * to each request made by this object. (defaults to undefined)
14123 * @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)
14126 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14129 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14135 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14139 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14140 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14141 * a finer-grained basis than the DataProxy events.
14143 getConnection : function(){
14144 return this.useAjax ? Roo.Ajax : this.conn;
14148 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14149 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14150 * process that block using the passed callback.
14151 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14152 * for the request to the remote server.
14153 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14154 * object into a block of Roo.data.Records.
14155 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14156 * The function must be passed <ul>
14157 * <li>The Record block object</li>
14158 * <li>The "arg" argument from the load function</li>
14159 * <li>A boolean success indicator</li>
14161 * @param {Object} scope The scope in which to call the callback
14162 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14164 load : function(params, reader, callback, scope, arg){
14165 if(this.fireEvent("beforeload", this, params) !== false){
14167 params : params || {},
14169 callback : callback,
14174 callback : this.loadResponse,
14178 Roo.applyIf(o, this.conn);
14179 if(this.activeRequest){
14180 Roo.Ajax.abort(this.activeRequest);
14182 this.activeRequest = Roo.Ajax.request(o);
14184 this.conn.request(o);
14187 callback.call(scope||this, null, arg, false);
14192 loadResponse : function(o, success, response){
14193 delete this.activeRequest;
14195 this.fireEvent("loadexception", this, o, response);
14196 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14201 result = o.reader.read(response);
14203 this.fireEvent("loadexception", this, o, response, e);
14204 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14208 this.fireEvent("load", this, o, o.request.arg);
14209 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14213 update : function(dataSet){
14218 updateResponse : function(dataSet){
14223 * Ext JS Library 1.1.1
14224 * Copyright(c) 2006-2007, Ext JS, LLC.
14226 * Originally Released Under LGPL - original licence link has changed is not relivant.
14229 * <script type="text/javascript">
14233 * @class Roo.data.ScriptTagProxy
14234 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14235 * other than the originating domain of the running page.<br><br>
14237 * <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
14238 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14240 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14241 * source code that is used as the source inside a <script> tag.<br><br>
14243 * In order for the browser to process the returned data, the server must wrap the data object
14244 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14245 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14246 * depending on whether the callback name was passed:
14249 boolean scriptTag = false;
14250 String cb = request.getParameter("callback");
14253 response.setContentType("text/javascript");
14255 response.setContentType("application/x-json");
14257 Writer out = response.getWriter();
14259 out.write(cb + "(");
14261 out.print(dataBlock.toJsonString());
14268 * @param {Object} config A configuration object.
14270 Roo.data.ScriptTagProxy = function(config){
14271 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14272 Roo.apply(this, config);
14273 this.head = document.getElementsByTagName("head")[0];
14276 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14278 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14280 * @cfg {String} url The URL from which to request the data object.
14283 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14287 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14288 * the server the name of the callback function set up by the load call to process the returned data object.
14289 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14290 * javascript output which calls this named function passing the data object as its only parameter.
14292 callbackParam : "callback",
14294 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14295 * name to the request.
14300 * Load data from the configured URL, read the data object into
14301 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14302 * process that block using the passed callback.
14303 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14304 * for the request to the remote server.
14305 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14306 * object into a block of Roo.data.Records.
14307 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14308 * The function must be passed <ul>
14309 * <li>The Record block object</li>
14310 * <li>The "arg" argument from the load function</li>
14311 * <li>A boolean success indicator</li>
14313 * @param {Object} scope The scope in which to call the callback
14314 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14316 load : function(params, reader, callback, scope, arg){
14317 if(this.fireEvent("beforeload", this, params) !== false){
14319 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14321 var url = this.url;
14322 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14324 url += "&_dc=" + (new Date().getTime());
14326 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14329 cb : "stcCallback"+transId,
14330 scriptId : "stcScript"+transId,
14334 callback : callback,
14340 window[trans.cb] = function(o){
14341 conn.handleResponse(o, trans);
14344 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14346 if(this.autoAbort !== false){
14350 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14352 var script = document.createElement("script");
14353 script.setAttribute("src", url);
14354 script.setAttribute("type", "text/javascript");
14355 script.setAttribute("id", trans.scriptId);
14356 this.head.appendChild(script);
14358 this.trans = trans;
14360 callback.call(scope||this, null, arg, false);
14365 isLoading : function(){
14366 return this.trans ? true : false;
14370 * Abort the current server request.
14372 abort : function(){
14373 if(this.isLoading()){
14374 this.destroyTrans(this.trans);
14379 destroyTrans : function(trans, isLoaded){
14380 this.head.removeChild(document.getElementById(trans.scriptId));
14381 clearTimeout(trans.timeoutId);
14383 window[trans.cb] = undefined;
14385 delete window[trans.cb];
14388 // if hasn't been loaded, wait for load to remove it to prevent script error
14389 window[trans.cb] = function(){
14390 window[trans.cb] = undefined;
14392 delete window[trans.cb];
14399 handleResponse : function(o, trans){
14400 this.trans = false;
14401 this.destroyTrans(trans, true);
14404 result = trans.reader.readRecords(o);
14406 this.fireEvent("loadexception", this, o, trans.arg, e);
14407 trans.callback.call(trans.scope||window, null, trans.arg, false);
14410 this.fireEvent("load", this, o, trans.arg);
14411 trans.callback.call(trans.scope||window, result, trans.arg, true);
14415 handleFailure : function(trans){
14416 this.trans = false;
14417 this.destroyTrans(trans, false);
14418 this.fireEvent("loadexception", this, null, trans.arg);
14419 trans.callback.call(trans.scope||window, null, trans.arg, false);
14423 * Ext JS Library 1.1.1
14424 * Copyright(c) 2006-2007, Ext JS, LLC.
14426 * Originally Released Under LGPL - original licence link has changed is not relivant.
14429 * <script type="text/javascript">
14433 * @class Roo.data.JsonReader
14434 * @extends Roo.data.DataReader
14435 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14436 * based on mappings in a provided Roo.data.Record constructor.
14438 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14439 * in the reply previously.
14444 var RecordDef = Roo.data.Record.create([
14445 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14446 {name: 'occupation'} // This field will use "occupation" as the mapping.
14448 var myReader = new Roo.data.JsonReader({
14449 totalProperty: "results", // The property which contains the total dataset size (optional)
14450 root: "rows", // The property which contains an Array of row objects
14451 id: "id" // The property within each row object that provides an ID for the record (optional)
14455 * This would consume a JSON file like this:
14457 { 'results': 2, 'rows': [
14458 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14459 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14462 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14463 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14464 * paged from the remote server.
14465 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14466 * @cfg {String} root name of the property which contains the Array of row objects.
14467 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14468 * @cfg {Array} fields Array of field definition objects
14470 * Create a new JsonReader
14471 * @param {Object} meta Metadata configuration options
14472 * @param {Object} recordType Either an Array of field definition objects,
14473 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14475 Roo.data.JsonReader = function(meta, recordType){
14478 // set some defaults:
14479 Roo.applyIf(meta, {
14480 totalProperty: 'total',
14481 successProperty : 'success',
14486 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14488 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14490 readerType : 'Json',
14493 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14494 * Used by Store query builder to append _requestMeta to params.
14497 metaFromRemote : false,
14499 * This method is only used by a DataProxy which has retrieved data from a remote server.
14500 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14501 * @return {Object} data A data block which is used by an Roo.data.Store object as
14502 * a cache of Roo.data.Records.
14504 read : function(response){
14505 var json = response.responseText;
14507 var o = /* eval:var:o */ eval("("+json+")");
14509 throw {message: "JsonReader.read: Json object not found"};
14515 this.metaFromRemote = true;
14516 this.meta = o.metaData;
14517 this.recordType = Roo.data.Record.create(o.metaData.fields);
14518 this.onMetaChange(this.meta, this.recordType, o);
14520 return this.readRecords(o);
14523 // private function a store will implement
14524 onMetaChange : function(meta, recordType, o){
14531 simpleAccess: function(obj, subsc) {
14538 getJsonAccessor: function(){
14540 return function(expr) {
14542 return(re.test(expr))
14543 ? new Function("obj", "return obj." + expr)
14548 return Roo.emptyFn;
14553 * Create a data block containing Roo.data.Records from an XML document.
14554 * @param {Object} o An object which contains an Array of row objects in the property specified
14555 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14556 * which contains the total size of the dataset.
14557 * @return {Object} data A data block which is used by an Roo.data.Store object as
14558 * a cache of Roo.data.Records.
14560 readRecords : function(o){
14562 * After any data loads, the raw JSON data is available for further custom processing.
14566 var s = this.meta, Record = this.recordType,
14567 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14569 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14571 if(s.totalProperty) {
14572 this.getTotal = this.getJsonAccessor(s.totalProperty);
14574 if(s.successProperty) {
14575 this.getSuccess = this.getJsonAccessor(s.successProperty);
14577 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14579 var g = this.getJsonAccessor(s.id);
14580 this.getId = function(rec) {
14582 return (r === undefined || r === "") ? null : r;
14585 this.getId = function(){return null;};
14588 for(var jj = 0; jj < fl; jj++){
14590 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14591 this.ef[jj] = this.getJsonAccessor(map);
14595 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14596 if(s.totalProperty){
14597 var vt = parseInt(this.getTotal(o), 10);
14602 if(s.successProperty){
14603 var vs = this.getSuccess(o);
14604 if(vs === false || vs === 'false'){
14609 for(var i = 0; i < c; i++){
14612 var id = this.getId(n);
14613 for(var j = 0; j < fl; j++){
14615 var v = this.ef[j](n);
14617 Roo.log('missing convert for ' + f.name);
14621 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14623 var record = new Record(values, id);
14625 records[i] = record;
14631 totalRecords : totalRecords
14634 // used when loading children.. @see loadDataFromChildren
14635 toLoadData: function(rec)
14637 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14638 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14639 return { data : data, total : data.length };
14644 * Ext JS Library 1.1.1
14645 * Copyright(c) 2006-2007, Ext JS, LLC.
14647 * Originally Released Under LGPL - original licence link has changed is not relivant.
14650 * <script type="text/javascript">
14654 * @class Roo.data.ArrayReader
14655 * @extends Roo.data.DataReader
14656 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14657 * Each element of that Array represents a row of data fields. The
14658 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14659 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14663 var RecordDef = Roo.data.Record.create([
14664 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14665 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14667 var myReader = new Roo.data.ArrayReader({
14668 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14672 * This would consume an Array like this:
14674 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14678 * Create a new JsonReader
14679 * @param {Object} meta Metadata configuration options.
14680 * @param {Object|Array} recordType Either an Array of field definition objects
14682 * @cfg {Array} fields Array of field definition objects
14683 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14684 * as specified to {@link Roo.data.Record#create},
14685 * or an {@link Roo.data.Record} object
14688 * created using {@link Roo.data.Record#create}.
14690 Roo.data.ArrayReader = function(meta, recordType)
14692 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14698 * Create a data block containing Roo.data.Records from an XML document.
14699 * @param {Object} o An Array of row objects which represents the dataset.
14700 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14701 * a cache of Roo.data.Records.
14703 readRecords : function(o)
14705 var sid = this.meta ? this.meta.id : null;
14706 var recordType = this.recordType, fields = recordType.prototype.fields;
14709 for(var i = 0; i < root.length; i++){
14712 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14713 for(var j = 0, jlen = fields.length; j < jlen; j++){
14714 var f = fields.items[j];
14715 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14716 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14718 values[f.name] = v;
14720 var record = new recordType(values, id);
14722 records[records.length] = record;
14726 totalRecords : records.length
14729 // used when loading children.. @see loadDataFromChildren
14730 toLoadData: function(rec)
14732 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14733 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14744 * @class Roo.bootstrap.ComboBox
14745 * @extends Roo.bootstrap.TriggerField
14746 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14747 * @cfg {Boolean} append (true|false) default false
14748 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14749 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14750 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14751 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14752 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14753 * @cfg {Boolean} animate default true
14754 * @cfg {Boolean} emptyResultText only for touch device
14755 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14756 * @cfg {String} emptyTitle default ''
14757 * @cfg {Number} width fixed with? experimental
14759 * Create a new ComboBox.
14760 * @param {Object} config Configuration options
14762 Roo.bootstrap.ComboBox = function(config){
14763 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14767 * Fires when the dropdown list is expanded
14768 * @param {Roo.bootstrap.ComboBox} combo This combo box
14773 * Fires when the dropdown list is collapsed
14774 * @param {Roo.bootstrap.ComboBox} combo This combo box
14778 * @event beforeselect
14779 * Fires before a list item is selected. Return false to cancel the selection.
14780 * @param {Roo.bootstrap.ComboBox} combo This combo box
14781 * @param {Roo.data.Record} record The data record returned from the underlying store
14782 * @param {Number} index The index of the selected item in the dropdown list
14784 'beforeselect' : true,
14787 * Fires when a list item is selected
14788 * @param {Roo.bootstrap.ComboBox} combo This combo box
14789 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14790 * @param {Number} index The index of the selected item in the dropdown list
14794 * @event beforequery
14795 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14796 * The event object passed has these properties:
14797 * @param {Roo.bootstrap.ComboBox} combo This combo box
14798 * @param {String} query The query
14799 * @param {Boolean} forceAll true to force "all" query
14800 * @param {Boolean} cancel true to cancel the query
14801 * @param {Object} e The query event object
14803 'beforequery': true,
14806 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14807 * @param {Roo.bootstrap.ComboBox} combo This combo box
14812 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14813 * @param {Roo.bootstrap.ComboBox} combo This combo box
14814 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14819 * Fires when the remove value from the combobox array
14820 * @param {Roo.bootstrap.ComboBox} combo This combo box
14824 * @event afterremove
14825 * Fires when the remove value from the combobox array
14826 * @param {Roo.bootstrap.ComboBox} combo This combo box
14828 'afterremove' : true,
14830 * @event specialfilter
14831 * Fires when specialfilter
14832 * @param {Roo.bootstrap.ComboBox} combo This combo box
14834 'specialfilter' : true,
14837 * Fires when tick the element
14838 * @param {Roo.bootstrap.ComboBox} combo This combo box
14842 * @event touchviewdisplay
14843 * Fires when touch view require special display (default is using displayField)
14844 * @param {Roo.bootstrap.ComboBox} combo This combo box
14845 * @param {Object} cfg set html .
14847 'touchviewdisplay' : true
14852 this.tickItems = [];
14854 this.selectedIndex = -1;
14855 if(this.mode == 'local'){
14856 if(config.queryDelay === undefined){
14857 this.queryDelay = 10;
14859 if(config.minChars === undefined){
14865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14868 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14869 * rendering into an Roo.Editor, defaults to false)
14872 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14873 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14876 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14879 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14880 * the dropdown list (defaults to undefined, with no header element)
14884 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14888 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14890 listWidth: undefined,
14892 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14893 * mode = 'remote' or 'text' if mode = 'local')
14895 displayField: undefined,
14898 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14899 * mode = 'remote' or 'value' if mode = 'local').
14900 * Note: use of a valueField requires the user make a selection
14901 * in order for a value to be mapped.
14903 valueField: undefined,
14905 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14910 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14911 * field's data value (defaults to the underlying DOM element's name)
14913 hiddenName: undefined,
14915 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14919 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14921 selectedClass: 'active',
14924 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14928 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14929 * anchor positions (defaults to 'tl-bl')
14931 listAlign: 'tl-bl?',
14933 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14937 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14938 * query specified by the allQuery config option (defaults to 'query')
14940 triggerAction: 'query',
14942 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14943 * (defaults to 4, does not apply if editable = false)
14947 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14948 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14952 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14953 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14957 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14958 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14962 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14963 * when editable = true (defaults to false)
14965 selectOnFocus:false,
14967 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14969 queryParam: 'query',
14971 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14972 * when mode = 'remote' (defaults to 'Loading...')
14974 loadingText: 'Loading...',
14976 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14980 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14984 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14985 * traditional select (defaults to true)
14989 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14993 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14997 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14998 * listWidth has a higher value)
15002 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15003 * allow the user to set arbitrary text into the field (defaults to false)
15005 forceSelection:false,
15007 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15008 * if typeAhead = true (defaults to 250)
15010 typeAheadDelay : 250,
15012 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15013 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15015 valueNotFoundText : undefined,
15017 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15019 blockFocus : false,
15022 * @cfg {Boolean} disableClear Disable showing of clear button.
15024 disableClear : false,
15026 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15028 alwaysQuery : false,
15031 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15036 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15038 invalidClass : "has-warning",
15041 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15043 validClass : "has-success",
15046 * @cfg {Boolean} specialFilter (true|false) special filter default false
15048 specialFilter : false,
15051 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15053 mobileTouchView : true,
15056 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15058 useNativeIOS : false,
15061 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15063 mobile_restrict_height : false,
15065 ios_options : false,
15077 btnPosition : 'right',
15078 triggerList : true,
15079 showToggleBtn : true,
15081 emptyResultText: 'Empty',
15082 triggerText : 'Select',
15086 // element that contains real text value.. (when hidden is used..)
15088 getAutoCreate : function()
15093 * Render classic select for iso
15096 if(Roo.isIOS && this.useNativeIOS){
15097 cfg = this.getAutoCreateNativeIOS();
15105 if(Roo.isTouch && this.mobileTouchView){
15106 cfg = this.getAutoCreateTouchView();
15113 if(!this.tickable){
15114 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15119 * ComboBox with tickable selections
15122 var align = this.labelAlign || this.parentLabelAlign();
15125 cls : 'form-group roo-combobox-tickable' //input-group
15128 var btn_text_select = '';
15129 var btn_text_done = '';
15130 var btn_text_cancel = '';
15132 if (this.btn_text_show) {
15133 btn_text_select = 'Select';
15134 btn_text_done = 'Done';
15135 btn_text_cancel = 'Cancel';
15140 cls : 'tickable-buttons',
15145 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15146 //html : this.triggerText
15147 html: btn_text_select
15153 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15155 html: btn_text_done
15161 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15163 html: btn_text_cancel
15169 buttons.cn.unshift({
15171 cls: 'roo-select2-search-field-input'
15177 Roo.each(buttons.cn, function(c){
15179 c.cls += ' btn-' + _this.size;
15182 if (_this.disabled) {
15189 style : 'display: contents',
15194 cls: 'form-hidden-field'
15198 cls: 'roo-select2-choices',
15202 cls: 'roo-select2-search-field',
15213 cls: 'roo-select2-container input-group roo-select2-container-multi',
15219 // cls: 'typeahead typeahead-long dropdown-menu',
15220 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15225 if(this.hasFeedback && !this.allowBlank){
15229 cls: 'glyphicon form-control-feedback'
15232 combobox.cn.push(feedback);
15239 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15240 tooltip : 'This field is required'
15242 if (Roo.bootstrap.version == 4) {
15245 style : 'display:none'
15248 if (align ==='left' && this.fieldLabel.length) {
15250 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15257 cls : 'control-label col-form-label',
15258 html : this.fieldLabel
15270 var labelCfg = cfg.cn[1];
15271 var contentCfg = cfg.cn[2];
15274 if(this.indicatorpos == 'right'){
15280 cls : 'control-label col-form-label',
15284 html : this.fieldLabel
15300 labelCfg = cfg.cn[0];
15301 contentCfg = cfg.cn[1];
15305 if(this.labelWidth > 12){
15306 labelCfg.style = "width: " + this.labelWidth + 'px';
15308 if(this.width * 1 > 0){
15309 contentCfg.style = "width: " + this.width + 'px';
15311 if(this.labelWidth < 13 && this.labelmd == 0){
15312 this.labelmd = this.labelWidth;
15315 if(this.labellg > 0){
15316 labelCfg.cls += ' col-lg-' + this.labellg;
15317 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320 if(this.labelmd > 0){
15321 labelCfg.cls += ' col-md-' + this.labelmd;
15322 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325 if(this.labelsm > 0){
15326 labelCfg.cls += ' col-sm-' + this.labelsm;
15327 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330 if(this.labelxs > 0){
15331 labelCfg.cls += ' col-xs-' + this.labelxs;
15332 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15336 } else if ( this.fieldLabel.length) {
15337 // Roo.log(" label");
15342 //cls : 'input-group-addon',
15343 html : this.fieldLabel
15348 if(this.indicatorpos == 'right'){
15352 //cls : 'input-group-addon',
15353 html : this.fieldLabel
15363 // Roo.log(" no label && no align");
15370 ['xs','sm','md','lg'].map(function(size){
15371 if (settings[size]) {
15372 cfg.cls += ' col-' + size + '-' + settings[size];
15380 _initEventsCalled : false,
15383 initEvents: function()
15385 if (this._initEventsCalled) { // as we call render... prevent looping...
15388 this._initEventsCalled = true;
15391 throw "can not find store for combo";
15394 this.indicator = this.indicatorEl();
15396 this.store = Roo.factory(this.store, Roo.data);
15397 this.store.parent = this;
15399 // if we are building from html. then this element is so complex, that we can not really
15400 // use the rendered HTML.
15401 // so we have to trash and replace the previous code.
15402 if (Roo.XComponent.build_from_html) {
15403 // remove this element....
15404 var e = this.el.dom, k=0;
15405 while (e ) { e = e.previousSibling; ++k;}
15410 this.rendered = false;
15412 this.render(this.parent().getChildContainer(true), k);
15415 if(Roo.isIOS && this.useNativeIOS){
15416 this.initIOSView();
15424 if(Roo.isTouch && this.mobileTouchView){
15425 this.initTouchView();
15430 this.initTickableEvents();
15434 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15436 if(this.hiddenName){
15438 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15440 this.hiddenField.dom.value =
15441 this.hiddenValue !== undefined ? this.hiddenValue :
15442 this.value !== undefined ? this.value : '';
15444 // prevent input submission
15445 this.el.dom.removeAttribute('name');
15446 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15451 // this.el.dom.setAttribute('autocomplete', 'off');
15454 var cls = 'x-combo-list';
15456 //this.list = new Roo.Layer({
15457 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15463 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15464 _this.list.setWidth(lw);
15467 this.list.on('mouseover', this.onViewOver, this);
15468 this.list.on('mousemove', this.onViewMove, this);
15469 this.list.on('scroll', this.onViewScroll, this);
15472 this.list.swallowEvent('mousewheel');
15473 this.assetHeight = 0;
15476 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15477 this.assetHeight += this.header.getHeight();
15480 this.innerList = this.list.createChild({cls:cls+'-inner'});
15481 this.innerList.on('mouseover', this.onViewOver, this);
15482 this.innerList.on('mousemove', this.onViewMove, this);
15483 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15485 if(this.allowBlank && !this.pageSize && !this.disableClear){
15486 this.footer = this.list.createChild({cls:cls+'-ft'});
15487 this.pageTb = new Roo.Toolbar(this.footer);
15491 this.footer = this.list.createChild({cls:cls+'-ft'});
15492 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15493 {pageSize: this.pageSize});
15497 if (this.pageTb && this.allowBlank && !this.disableClear) {
15499 this.pageTb.add(new Roo.Toolbar.Fill(), {
15500 cls: 'x-btn-icon x-btn-clear',
15502 handler: function()
15505 _this.clearValue();
15506 _this.onSelect(false, -1);
15511 this.assetHeight += this.footer.getHeight();
15516 this.tpl = Roo.bootstrap.version == 4 ?
15517 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15518 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15521 this.view = new Roo.View(this.list, this.tpl, {
15522 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15524 //this.view.wrapEl.setDisplayed(false);
15525 this.view.on('click', this.onViewClick, this);
15528 this.store.on('beforeload', this.onBeforeLoad, this);
15529 this.store.on('load', this.onLoad, this);
15530 this.store.on('loadexception', this.onLoadException, this);
15532 if(this.resizable){
15533 this.resizer = new Roo.Resizable(this.list, {
15534 pinned:true, handles:'se'
15536 this.resizer.on('resize', function(r, w, h){
15537 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15538 this.listWidth = w;
15539 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15540 this.restrictHeight();
15542 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15545 if(!this.editable){
15546 this.editable = true;
15547 this.setEditable(false);
15552 if (typeof(this.events.add.listeners) != 'undefined') {
15554 this.addicon = this.wrap.createChild(
15555 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15557 this.addicon.on('click', function(e) {
15558 this.fireEvent('add', this);
15561 if (typeof(this.events.edit.listeners) != 'undefined') {
15563 this.editicon = this.wrap.createChild(
15564 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15565 if (this.addicon) {
15566 this.editicon.setStyle('margin-left', '40px');
15568 this.editicon.on('click', function(e) {
15570 // we fire even if inothing is selected..
15571 this.fireEvent('edit', this, this.lastData );
15577 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15578 "up" : function(e){
15579 this.inKeyMode = true;
15583 "down" : function(e){
15584 if(!this.isExpanded()){
15585 this.onTriggerClick();
15587 this.inKeyMode = true;
15592 "enter" : function(e){
15593 // this.onViewClick();
15597 if(this.fireEvent("specialkey", this, e)){
15598 this.onViewClick(false);
15604 "esc" : function(e){
15608 "tab" : function(e){
15611 if(this.fireEvent("specialkey", this, e)){
15612 this.onViewClick(false);
15620 doRelay : function(foo, bar, hname){
15621 if(hname == 'down' || this.scope.isExpanded()){
15622 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15631 this.queryDelay = Math.max(this.queryDelay || 10,
15632 this.mode == 'local' ? 10 : 250);
15635 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15637 if(this.typeAhead){
15638 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15640 if(this.editable !== false){
15641 this.inputEl().on("keyup", this.onKeyUp, this);
15643 if(this.forceSelection){
15644 this.inputEl().on('blur', this.doForce, this);
15648 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15649 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15653 initTickableEvents: function()
15657 if(this.hiddenName){
15659 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15661 this.hiddenField.dom.value =
15662 this.hiddenValue !== undefined ? this.hiddenValue :
15663 this.value !== undefined ? this.value : '';
15665 // prevent input submission
15666 this.el.dom.removeAttribute('name');
15667 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15672 // this.list = this.el.select('ul.dropdown-menu',true).first();
15674 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15675 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15676 if(this.triggerList){
15677 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15680 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15681 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15683 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15684 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15686 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15687 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15689 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15690 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15691 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15694 this.cancelBtn.hide();
15699 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15700 _this.list.setWidth(lw);
15703 this.list.on('mouseover', this.onViewOver, this);
15704 this.list.on('mousemove', this.onViewMove, this);
15706 this.list.on('scroll', this.onViewScroll, this);
15709 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15710 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15713 this.view = new Roo.View(this.list, this.tpl, {
15718 selectedClass: this.selectedClass
15721 //this.view.wrapEl.setDisplayed(false);
15722 this.view.on('click', this.onViewClick, this);
15726 this.store.on('beforeload', this.onBeforeLoad, this);
15727 this.store.on('load', this.onLoad, this);
15728 this.store.on('loadexception', this.onLoadException, this);
15731 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15732 "up" : function(e){
15733 this.inKeyMode = true;
15737 "down" : function(e){
15738 this.inKeyMode = true;
15742 "enter" : function(e){
15743 if(this.fireEvent("specialkey", this, e)){
15744 this.onViewClick(false);
15750 "esc" : function(e){
15751 this.onTickableFooterButtonClick(e, false, false);
15754 "tab" : function(e){
15755 this.fireEvent("specialkey", this, e);
15757 this.onTickableFooterButtonClick(e, false, false);
15764 doRelay : function(e, fn, key){
15765 if(this.scope.isExpanded()){
15766 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15775 this.queryDelay = Math.max(this.queryDelay || 10,
15776 this.mode == 'local' ? 10 : 250);
15779 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15781 if(this.typeAhead){
15782 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785 if(this.editable !== false){
15786 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15789 this.indicator = this.indicatorEl();
15791 if(this.indicator){
15792 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15793 this.indicator.hide();
15798 onDestroy : function(){
15800 this.view.setStore(null);
15801 this.view.el.removeAllListeners();
15802 this.view.el.remove();
15803 this.view.purgeListeners();
15806 this.list.dom.innerHTML = '';
15810 this.store.un('beforeload', this.onBeforeLoad, this);
15811 this.store.un('load', this.onLoad, this);
15812 this.store.un('loadexception', this.onLoadException, this);
15814 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15818 fireKey : function(e){
15819 if(e.isNavKeyPress() && !this.list.isVisible()){
15820 this.fireEvent("specialkey", this, e);
15825 onResize: function(w, h)
15829 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15831 // if(typeof w != 'number'){
15832 // // we do not handle it!?!?
15835 // var tw = this.trigger.getWidth();
15836 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15837 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15839 // this.inputEl().setWidth( this.adjustWidth('input', x));
15841 // //this.trigger.setStyle('left', x+'px');
15843 // if(this.list && this.listWidth === undefined){
15844 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15845 // this.list.setWidth(lw);
15846 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15854 * Allow or prevent the user from directly editing the field text. If false is passed,
15855 * the user will only be able to select from the items defined in the dropdown list. This method
15856 * is the runtime equivalent of setting the 'editable' config option at config time.
15857 * @param {Boolean} value True to allow the user to directly edit the field text
15859 setEditable : function(value){
15860 if(value == this.editable){
15863 this.editable = value;
15865 this.inputEl().dom.setAttribute('readOnly', true);
15866 this.inputEl().on('mousedown', this.onTriggerClick, this);
15867 this.inputEl().addClass('x-combo-noedit');
15869 this.inputEl().dom.setAttribute('readOnly', false);
15870 this.inputEl().un('mousedown', this.onTriggerClick, this);
15871 this.inputEl().removeClass('x-combo-noedit');
15877 onBeforeLoad : function(combo,opts){
15878 if(!this.hasFocus){
15882 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15884 this.restrictHeight();
15885 this.selectedIndex = -1;
15889 onLoad : function(){
15891 this.hasQuery = false;
15893 if(!this.hasFocus){
15897 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15898 this.loading.hide();
15901 if(this.store.getCount() > 0){
15904 this.restrictHeight();
15905 if(this.lastQuery == this.allQuery){
15906 if(this.editable && !this.tickable){
15907 this.inputEl().dom.select();
15911 !this.selectByValue(this.value, true) &&
15914 !this.store.lastOptions ||
15915 typeof(this.store.lastOptions.add) == 'undefined' ||
15916 this.store.lastOptions.add != true
15919 this.select(0, true);
15922 if(this.autoFocus){
15925 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15926 this.taTask.delay(this.typeAheadDelay);
15930 this.onEmptyResults();
15936 onLoadException : function()
15938 this.hasQuery = false;
15940 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15941 this.loading.hide();
15944 if(this.tickable && this.editable){
15949 // only causes errors at present
15950 //Roo.log(this.store.reader.jsonData);
15951 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15953 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15959 onTypeAhead : function(){
15960 if(this.store.getCount() > 0){
15961 var r = this.store.getAt(0);
15962 var newValue = r.data[this.displayField];
15963 var len = newValue.length;
15964 var selStart = this.getRawValue().length;
15966 if(selStart != len){
15967 this.setRawValue(newValue);
15968 this.selectText(selStart, newValue.length);
15974 onSelect : function(record, index){
15976 if(this.fireEvent('beforeselect', this, record, index) !== false){
15978 this.setFromData(index > -1 ? record.data : false);
15981 this.fireEvent('select', this, record, index);
15986 * Returns the currently selected field value or empty string if no value is set.
15987 * @return {String} value The selected value
15989 getValue : function()
15991 if(Roo.isIOS && this.useNativeIOS){
15992 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15996 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15999 if(this.valueField){
16000 return typeof this.value != 'undefined' ? this.value : '';
16002 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16006 getRawValue : function()
16008 if(Roo.isIOS && this.useNativeIOS){
16009 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16012 var v = this.inputEl().getValue();
16018 * Clears any text/value currently set in the field
16020 clearValue : function(){
16022 if(this.hiddenField){
16023 this.hiddenField.dom.value = '';
16026 this.setRawValue('');
16027 this.lastSelectionText = '';
16028 this.lastData = false;
16030 var close = this.closeTriggerEl();
16041 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16042 * will be displayed in the field. If the value does not match the data value of an existing item,
16043 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16044 * Otherwise the field will be blank (although the value will still be set).
16045 * @param {String} value The value to match
16047 setValue : function(v)
16049 if(Roo.isIOS && this.useNativeIOS){
16050 this.setIOSValue(v);
16060 if(this.valueField){
16061 var r = this.findRecord(this.valueField, v);
16063 text = r.data[this.displayField];
16064 }else if(this.valueNotFoundText !== undefined){
16065 text = this.valueNotFoundText;
16068 this.lastSelectionText = text;
16069 if(this.hiddenField){
16070 this.hiddenField.dom.value = v;
16072 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16075 var close = this.closeTriggerEl();
16078 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16084 * @property {Object} the last set data for the element
16089 * Sets the value of the field based on a object which is related to the record format for the store.
16090 * @param {Object} value the value to set as. or false on reset?
16092 setFromData : function(o){
16099 var dv = ''; // display value
16100 var vv = ''; // value value..
16102 if (this.displayField) {
16103 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16105 // this is an error condition!!!
16106 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16109 if(this.valueField){
16110 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16113 var close = this.closeTriggerEl();
16116 if(dv.length || vv * 1 > 0){
16118 this.blockFocus=true;
16124 if(this.hiddenField){
16125 this.hiddenField.dom.value = vv;
16127 this.lastSelectionText = dv;
16128 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16132 // no hidden field.. - we store the value in 'value', but still display
16133 // display field!!!!
16134 this.lastSelectionText = dv;
16135 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16142 reset : function(){
16143 // overridden so that last data is reset..
16150 this.setValue(this.originalValue);
16151 //this.clearInvalid();
16152 this.lastData = false;
16154 this.view.clearSelections();
16160 findRecord : function(prop, value){
16162 if(this.store.getCount() > 0){
16163 this.store.each(function(r){
16164 if(r.data[prop] == value){
16174 getName: function()
16176 // returns hidden if it's set..
16177 if (!this.rendered) {return ''};
16178 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16182 onViewMove : function(e, t){
16183 this.inKeyMode = false;
16187 onViewOver : function(e, t){
16188 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16191 var item = this.view.findItemFromChild(t);
16194 var index = this.view.indexOf(item);
16195 this.select(index, false);
16200 onViewClick : function(view, doFocus, el, e)
16202 var index = this.view.getSelectedIndexes()[0];
16204 var r = this.store.getAt(index);
16208 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16215 Roo.each(this.tickItems, function(v,k){
16217 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16219 _this.tickItems.splice(k, 1);
16221 if(typeof(e) == 'undefined' && view == false){
16222 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16234 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16235 this.tickItems.push(r.data);
16238 if(typeof(e) == 'undefined' && view == false){
16239 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16246 this.onSelect(r, index);
16248 if(doFocus !== false && !this.blockFocus){
16249 this.inputEl().focus();
16254 restrictHeight : function(){
16255 //this.innerList.dom.style.height = '';
16256 //var inner = this.innerList.dom;
16257 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16258 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16259 //this.list.beginUpdate();
16260 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16261 this.list.alignTo(this.inputEl(), this.listAlign);
16262 this.list.alignTo(this.inputEl(), this.listAlign);
16263 //this.list.endUpdate();
16267 onEmptyResults : function(){
16269 if(this.tickable && this.editable){
16270 this.hasFocus = false;
16271 this.restrictHeight();
16279 * Returns true if the dropdown list is expanded, else false.
16281 isExpanded : function(){
16282 return this.list.isVisible();
16286 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16287 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16288 * @param {String} value The data value of the item to select
16289 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16290 * selected item if it is not currently in view (defaults to true)
16291 * @return {Boolean} True if the value matched an item in the list, else false
16293 selectByValue : function(v, scrollIntoView){
16294 if(v !== undefined && v !== null){
16295 var r = this.findRecord(this.valueField || this.displayField, v);
16297 this.select(this.store.indexOf(r), scrollIntoView);
16305 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16306 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16307 * @param {Number} index The zero-based index of the list item to select
16308 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16309 * selected item if it is not currently in view (defaults to true)
16311 select : function(index, scrollIntoView){
16312 this.selectedIndex = index;
16313 this.view.select(index);
16314 if(scrollIntoView !== false){
16315 var el = this.view.getNode(index);
16317 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16320 this.list.scrollChildIntoView(el, false);
16326 selectNext : function(){
16327 var ct = this.store.getCount();
16329 if(this.selectedIndex == -1){
16331 }else if(this.selectedIndex < ct-1){
16332 this.select(this.selectedIndex+1);
16338 selectPrev : function(){
16339 var ct = this.store.getCount();
16341 if(this.selectedIndex == -1){
16343 }else if(this.selectedIndex != 0){
16344 this.select(this.selectedIndex-1);
16350 onKeyUp : function(e){
16351 if(this.editable !== false && !e.isSpecialKey()){
16352 this.lastKey = e.getKey();
16353 this.dqTask.delay(this.queryDelay);
16358 validateBlur : function(){
16359 return !this.list || !this.list.isVisible();
16363 initQuery : function(){
16365 var v = this.getRawValue();
16367 if(this.tickable && this.editable){
16368 v = this.tickableInputEl().getValue();
16375 doForce : function(){
16376 if(this.inputEl().dom.value.length > 0){
16377 this.inputEl().dom.value =
16378 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16384 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16385 * query allowing the query action to be canceled if needed.
16386 * @param {String} query The SQL query to execute
16387 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16388 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16389 * saved in the current store (defaults to false)
16391 doQuery : function(q, forceAll){
16393 if(q === undefined || q === null){
16398 forceAll: forceAll,
16402 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16407 forceAll = qe.forceAll;
16408 if(forceAll === true || (q.length >= this.minChars)){
16410 this.hasQuery = true;
16412 if(this.lastQuery != q || this.alwaysQuery){
16413 this.lastQuery = q;
16414 if(this.mode == 'local'){
16415 this.selectedIndex = -1;
16417 this.store.clearFilter();
16420 if(this.specialFilter){
16421 this.fireEvent('specialfilter', this);
16426 this.store.filter(this.displayField, q);
16429 this.store.fireEvent("datachanged", this.store);
16436 this.store.baseParams[this.queryParam] = q;
16438 var options = {params : this.getParams(q)};
16441 options.add = true;
16442 options.params.start = this.page * this.pageSize;
16445 this.store.load(options);
16448 * this code will make the page width larger, at the beginning, the list not align correctly,
16449 * we should expand the list on onLoad
16450 * so command out it
16455 this.selectedIndex = -1;
16460 this.loadNext = false;
16464 getParams : function(q){
16466 //p[this.queryParam] = q;
16470 p.limit = this.pageSize;
16476 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16478 collapse : function(){
16479 if(!this.isExpanded()){
16485 this.hasFocus = false;
16489 this.cancelBtn.hide();
16490 this.trigger.show();
16493 this.tickableInputEl().dom.value = '';
16494 this.tickableInputEl().blur();
16499 Roo.get(document).un('mousedown', this.collapseIf, this);
16500 Roo.get(document).un('mousewheel', this.collapseIf, this);
16501 if (!this.editable) {
16502 Roo.get(document).un('keydown', this.listKeyPress, this);
16504 this.fireEvent('collapse', this);
16510 collapseIf : function(e){
16511 var in_combo = e.within(this.el);
16512 var in_list = e.within(this.list);
16513 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16515 if (in_combo || in_list || is_list) {
16516 //e.stopPropagation();
16521 this.onTickableFooterButtonClick(e, false, false);
16529 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16531 expand : function(){
16533 if(this.isExpanded() || !this.hasFocus){
16537 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16538 this.list.setWidth(lw);
16544 this.restrictHeight();
16548 this.tickItems = Roo.apply([], this.item);
16551 this.cancelBtn.show();
16552 this.trigger.hide();
16555 this.tickableInputEl().focus();
16560 Roo.get(document).on('mousedown', this.collapseIf, this);
16561 Roo.get(document).on('mousewheel', this.collapseIf, this);
16562 if (!this.editable) {
16563 Roo.get(document).on('keydown', this.listKeyPress, this);
16566 this.fireEvent('expand', this);
16570 // Implements the default empty TriggerField.onTriggerClick function
16571 onTriggerClick : function(e)
16573 Roo.log('trigger click');
16575 if(this.disabled || !this.triggerList){
16580 this.loadNext = false;
16582 if(this.isExpanded()){
16584 if (!this.blockFocus) {
16585 this.inputEl().focus();
16589 this.hasFocus = true;
16590 if(this.triggerAction == 'all') {
16591 this.doQuery(this.allQuery, true);
16593 this.doQuery(this.getRawValue());
16595 if (!this.blockFocus) {
16596 this.inputEl().focus();
16601 onTickableTriggerClick : function(e)
16608 this.loadNext = false;
16609 this.hasFocus = true;
16611 if(this.triggerAction == 'all') {
16612 this.doQuery(this.allQuery, true);
16614 this.doQuery(this.getRawValue());
16618 onSearchFieldClick : function(e)
16620 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16621 this.onTickableFooterButtonClick(e, false, false);
16625 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16630 this.loadNext = false;
16631 this.hasFocus = true;
16633 if(this.triggerAction == 'all') {
16634 this.doQuery(this.allQuery, true);
16636 this.doQuery(this.getRawValue());
16640 listKeyPress : function(e)
16642 //Roo.log('listkeypress');
16643 // scroll to first matching element based on key pres..
16644 if (e.isSpecialKey()) {
16647 var k = String.fromCharCode(e.getKey()).toUpperCase();
16650 var csel = this.view.getSelectedNodes();
16651 var cselitem = false;
16653 var ix = this.view.indexOf(csel[0]);
16654 cselitem = this.store.getAt(ix);
16655 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16661 this.store.each(function(v) {
16663 // start at existing selection.
16664 if (cselitem.id == v.id) {
16670 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16671 match = this.store.indexOf(v);
16677 if (match === false) {
16678 return true; // no more action?
16681 this.view.select(match);
16682 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16683 sn.scrollIntoView(sn.dom.parentNode, false);
16686 onViewScroll : function(e, t){
16688 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){
16692 this.hasQuery = true;
16694 this.loading = this.list.select('.loading', true).first();
16696 if(this.loading === null){
16697 this.list.createChild({
16699 cls: 'loading roo-select2-more-results roo-select2-active',
16700 html: 'Loading more results...'
16703 this.loading = this.list.select('.loading', true).first();
16705 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16707 this.loading.hide();
16710 this.loading.show();
16715 this.loadNext = true;
16717 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16722 addItem : function(o)
16724 var dv = ''; // display value
16726 if (this.displayField) {
16727 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16729 // this is an error condition!!!
16730 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16737 var choice = this.choices.createChild({
16739 cls: 'roo-select2-search-choice',
16748 cls: 'roo-select2-search-choice-close fa fa-times',
16753 }, this.searchField);
16755 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16757 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16765 this.inputEl().dom.value = '';
16770 onRemoveItem : function(e, _self, o)
16772 e.preventDefault();
16774 this.lastItem = Roo.apply([], this.item);
16776 var index = this.item.indexOf(o.data) * 1;
16779 Roo.log('not this item?!');
16783 this.item.splice(index, 1);
16788 this.fireEvent('remove', this, e);
16794 syncValue : function()
16796 if(!this.item.length){
16803 Roo.each(this.item, function(i){
16804 if(_this.valueField){
16805 value.push(i[_this.valueField]);
16812 this.value = value.join(',');
16814 if(this.hiddenField){
16815 this.hiddenField.dom.value = this.value;
16818 this.store.fireEvent("datachanged", this.store);
16823 clearItem : function()
16825 if(!this.multiple){
16831 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16839 if(this.tickable && !Roo.isTouch){
16840 this.view.refresh();
16844 inputEl: function ()
16846 if(Roo.isIOS && this.useNativeIOS){
16847 return this.el.select('select.roo-ios-select', true).first();
16850 if(Roo.isTouch && this.mobileTouchView){
16851 return this.el.select('input.form-control',true).first();
16855 return this.searchField;
16858 return this.el.select('input.form-control',true).first();
16861 onTickableFooterButtonClick : function(e, btn, el)
16863 e.preventDefault();
16865 this.lastItem = Roo.apply([], this.item);
16867 if(btn && btn.name == 'cancel'){
16868 this.tickItems = Roo.apply([], this.item);
16877 Roo.each(this.tickItems, function(o){
16885 validate : function()
16887 if(this.getVisibilityEl().hasClass('hidden')){
16891 var v = this.getRawValue();
16894 v = this.getValue();
16897 if(this.disabled || this.allowBlank || v.length){
16902 this.markInvalid();
16906 tickableInputEl : function()
16908 if(!this.tickable || !this.editable){
16909 return this.inputEl();
16912 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16916 getAutoCreateTouchView : function()
16921 cls: 'form-group' //input-group
16927 type : this.inputType,
16928 cls : 'form-control x-combo-noedit',
16929 autocomplete: 'new-password',
16930 placeholder : this.placeholder || '',
16935 input.name = this.name;
16939 input.cls += ' input-' + this.size;
16942 if (this.disabled) {
16943 input.disabled = true;
16947 cls : 'roo-combobox-wrap',
16954 inputblock.cls += ' input-group';
16956 inputblock.cn.unshift({
16958 cls : 'input-group-addon input-group-prepend input-group-text',
16963 if(this.removable && !this.multiple){
16964 inputblock.cls += ' roo-removable';
16966 inputblock.cn.push({
16969 cls : 'roo-combo-removable-btn close'
16973 if(this.hasFeedback && !this.allowBlank){
16975 inputblock.cls += ' has-feedback';
16977 inputblock.cn.push({
16979 cls: 'glyphicon form-control-feedback'
16986 inputblock.cls += (this.before) ? '' : ' input-group';
16988 inputblock.cn.push({
16990 cls : 'input-group-addon input-group-append input-group-text',
16996 var ibwrap = inputblock;
17001 cls: 'roo-select2-choices',
17005 cls: 'roo-select2-search-field',
17018 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17023 cls: 'form-hidden-field'
17029 if(!this.multiple && this.showToggleBtn){
17035 if (this.caret != false) {
17038 cls: 'fa fa-' + this.caret
17045 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17047 Roo.bootstrap.version == 3 ? caret : '',
17050 cls: 'combobox-clear',
17064 combobox.cls += ' roo-select2-container-multi';
17067 var align = this.labelAlign || this.parentLabelAlign();
17069 if (align ==='left' && this.fieldLabel.length) {
17074 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17075 tooltip : 'This field is required'
17079 cls : 'control-label col-form-label',
17080 html : this.fieldLabel
17084 cls : 'roo-combobox-wrap ',
17091 var labelCfg = cfg.cn[1];
17092 var contentCfg = cfg.cn[2];
17095 if(this.indicatorpos == 'right'){
17100 cls : 'control-label col-form-label',
17104 html : this.fieldLabel
17108 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17109 tooltip : 'This field is required'
17114 cls : "roo-combobox-wrap ",
17122 labelCfg = cfg.cn[0];
17123 contentCfg = cfg.cn[1];
17128 if(this.labelWidth > 12){
17129 labelCfg.style = "width: " + this.labelWidth + 'px';
17132 if(this.labelWidth < 13 && this.labelmd == 0){
17133 this.labelmd = this.labelWidth;
17136 if(this.labellg > 0){
17137 labelCfg.cls += ' col-lg-' + this.labellg;
17138 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17141 if(this.labelmd > 0){
17142 labelCfg.cls += ' col-md-' + this.labelmd;
17143 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17146 if(this.labelsm > 0){
17147 labelCfg.cls += ' col-sm-' + this.labelsm;
17148 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17151 if(this.labelxs > 0){
17152 labelCfg.cls += ' col-xs-' + this.labelxs;
17153 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17157 } else if ( this.fieldLabel.length) {
17161 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17162 tooltip : 'This field is required'
17166 cls : 'control-label',
17167 html : this.fieldLabel
17178 if(this.indicatorpos == 'right'){
17182 cls : 'control-label',
17183 html : this.fieldLabel,
17187 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17188 tooltip : 'This field is required'
17205 var settings = this;
17207 ['xs','sm','md','lg'].map(function(size){
17208 if (settings[size]) {
17209 cfg.cls += ' col-' + size + '-' + settings[size];
17216 initTouchView : function()
17218 this.renderTouchView();
17220 this.touchViewEl.on('scroll', function(){
17221 this.el.dom.scrollTop = 0;
17224 this.originalValue = this.getValue();
17226 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17228 this.inputEl().on("click", this.showTouchView, this);
17229 if (this.triggerEl) {
17230 this.triggerEl.on("click", this.showTouchView, this);
17234 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17235 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17237 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17239 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17240 this.store.on('load', this.onTouchViewLoad, this);
17241 this.store.on('loadexception', this.onTouchViewLoadException, this);
17243 if(this.hiddenName){
17245 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17247 this.hiddenField.dom.value =
17248 this.hiddenValue !== undefined ? this.hiddenValue :
17249 this.value !== undefined ? this.value : '';
17251 this.el.dom.removeAttribute('name');
17252 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17256 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17257 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17260 if(this.removable && !this.multiple){
17261 var close = this.closeTriggerEl();
17263 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17264 close.on('click', this.removeBtnClick, this, close);
17268 * fix the bug in Safari iOS8
17270 this.inputEl().on("focus", function(e){
17271 document.activeElement.blur();
17274 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17281 renderTouchView : function()
17283 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17284 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17286 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17287 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17289 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17290 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291 this.touchViewBodyEl.setStyle('overflow', 'auto');
17293 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17294 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17296 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17297 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17301 showTouchView : function()
17307 this.touchViewHeaderEl.hide();
17309 if(this.modalTitle.length){
17310 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17311 this.touchViewHeaderEl.show();
17314 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17315 this.touchViewEl.show();
17317 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17319 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17320 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17322 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17324 if(this.modalTitle.length){
17325 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17328 this.touchViewBodyEl.setHeight(bodyHeight);
17332 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17334 this.touchViewEl.addClass('in');
17337 if(this._touchViewMask){
17338 Roo.get(document.body).addClass("x-body-masked");
17339 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17340 this._touchViewMask.setStyle('z-index', 10000);
17341 this._touchViewMask.addClass('show');
17344 this.doTouchViewQuery();
17348 hideTouchView : function()
17350 this.touchViewEl.removeClass('in');
17354 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17356 this.touchViewEl.setStyle('display', 'none');
17359 if(this._touchViewMask){
17360 this._touchViewMask.removeClass('show');
17361 Roo.get(document.body).removeClass("x-body-masked");
17365 setTouchViewValue : function()
17372 Roo.each(this.tickItems, function(o){
17377 this.hideTouchView();
17380 doTouchViewQuery : function()
17389 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17393 if(!this.alwaysQuery || this.mode == 'local'){
17394 this.onTouchViewLoad();
17401 onTouchViewBeforeLoad : function(combo,opts)
17407 onTouchViewLoad : function()
17409 if(this.store.getCount() < 1){
17410 this.onTouchViewEmptyResults();
17414 this.clearTouchView();
17416 var rawValue = this.getRawValue();
17418 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17420 this.tickItems = [];
17422 this.store.data.each(function(d, rowIndex){
17423 var row = this.touchViewListGroup.createChild(template);
17425 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17426 row.addClass(d.data.cls);
17429 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17432 html : d.data[this.displayField]
17435 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17436 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17439 row.removeClass('selected');
17440 if(!this.multiple && this.valueField &&
17441 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17445 row.addClass('selected');
17448 if(this.multiple && this.valueField &&
17449 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17453 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17454 this.tickItems.push(d.data);
17457 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17461 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17463 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17465 if(this.modalTitle.length){
17466 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17469 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17471 if(this.mobile_restrict_height && listHeight < bodyHeight){
17472 this.touchViewBodyEl.setHeight(listHeight);
17477 if(firstChecked && listHeight > bodyHeight){
17478 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17483 onTouchViewLoadException : function()
17485 this.hideTouchView();
17488 onTouchViewEmptyResults : function()
17490 this.clearTouchView();
17492 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17494 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17498 clearTouchView : function()
17500 this.touchViewListGroup.dom.innerHTML = '';
17503 onTouchViewClick : function(e, el, o)
17505 e.preventDefault();
17508 var rowIndex = o.rowIndex;
17510 var r = this.store.getAt(rowIndex);
17512 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17514 if(!this.multiple){
17515 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17516 c.dom.removeAttribute('checked');
17519 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17521 this.setFromData(r.data);
17523 var close = this.closeTriggerEl();
17529 this.hideTouchView();
17531 this.fireEvent('select', this, r, rowIndex);
17536 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17537 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17538 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17542 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17543 this.addItem(r.data);
17544 this.tickItems.push(r.data);
17548 getAutoCreateNativeIOS : function()
17551 cls: 'form-group' //input-group,
17556 cls : 'roo-ios-select'
17560 combobox.name = this.name;
17563 if (this.disabled) {
17564 combobox.disabled = true;
17567 var settings = this;
17569 ['xs','sm','md','lg'].map(function(size){
17570 if (settings[size]) {
17571 cfg.cls += ' col-' + size + '-' + settings[size];
17581 initIOSView : function()
17583 this.store.on('load', this.onIOSViewLoad, this);
17588 onIOSViewLoad : function()
17590 if(this.store.getCount() < 1){
17594 this.clearIOSView();
17596 if(this.allowBlank) {
17598 var default_text = '-- SELECT --';
17600 if(this.placeholder.length){
17601 default_text = this.placeholder;
17604 if(this.emptyTitle.length){
17605 default_text += ' - ' + this.emptyTitle + ' -';
17608 var opt = this.inputEl().createChild({
17611 html : default_text
17615 o[this.valueField] = 0;
17616 o[this.displayField] = default_text;
17618 this.ios_options.push({
17625 this.store.data.each(function(d, rowIndex){
17629 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17630 html = d.data[this.displayField];
17635 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17636 value = d.data[this.valueField];
17645 if(this.value == d.data[this.valueField]){
17646 option['selected'] = true;
17649 var opt = this.inputEl().createChild(option);
17651 this.ios_options.push({
17658 this.inputEl().on('change', function(){
17659 this.fireEvent('select', this);
17664 clearIOSView: function()
17666 this.inputEl().dom.innerHTML = '';
17668 this.ios_options = [];
17671 setIOSValue: function(v)
17675 if(!this.ios_options){
17679 Roo.each(this.ios_options, function(opts){
17681 opts.el.dom.removeAttribute('selected');
17683 if(opts.data[this.valueField] != v){
17687 opts.el.dom.setAttribute('selected', true);
17693 * @cfg {Boolean} grow
17697 * @cfg {Number} growMin
17701 * @cfg {Number} growMax
17710 Roo.apply(Roo.bootstrap.ComboBox, {
17714 cls: 'modal-header',
17736 cls: 'list-group-item',
17740 cls: 'roo-combobox-list-group-item-value'
17744 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17758 listItemCheckbox : {
17760 cls: 'list-group-item',
17764 cls: 'roo-combobox-list-group-item-value'
17768 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17784 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17789 cls: 'modal-footer',
17797 cls: 'col-xs-6 text-left',
17800 cls: 'btn btn-danger roo-touch-view-cancel',
17806 cls: 'col-xs-6 text-right',
17809 cls: 'btn btn-success roo-touch-view-ok',
17820 Roo.apply(Roo.bootstrap.ComboBox, {
17822 touchViewTemplate : {
17824 cls: 'modal fade roo-combobox-touch-view',
17828 cls: 'modal-dialog',
17829 style : 'position:fixed', // we have to fix position....
17833 cls: 'modal-content',
17835 Roo.bootstrap.ComboBox.header,
17836 Roo.bootstrap.ComboBox.body,
17837 Roo.bootstrap.ComboBox.footer
17846 * Ext JS Library 1.1.1
17847 * Copyright(c) 2006-2007, Ext JS, LLC.
17849 * Originally Released Under LGPL - original licence link has changed is not relivant.
17852 * <script type="text/javascript">
17857 * @extends Roo.util.Observable
17858 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17859 * This class also supports single and multi selection modes. <br>
17860 * Create a data model bound view:
17862 var store = new Roo.data.Store(...);
17864 var view = new Roo.View({
17866 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17868 singleSelect: true,
17869 selectedClass: "ydataview-selected",
17873 // listen for node click?
17874 view.on("click", function(vw, index, node, e){
17875 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17879 dataModel.load("foobar.xml");
17881 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17883 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17884 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17886 * Note: old style constructor is still suported (container, template, config)
17889 * Create a new View
17890 * @param {Object} config The config object
17893 Roo.View = function(config, depreciated_tpl, depreciated_config){
17895 this.parent = false;
17897 if (typeof(depreciated_tpl) == 'undefined') {
17898 // new way.. - universal constructor.
17899 Roo.apply(this, config);
17900 this.el = Roo.get(this.el);
17903 this.el = Roo.get(config);
17904 this.tpl = depreciated_tpl;
17905 Roo.apply(this, depreciated_config);
17907 this.wrapEl = this.el.wrap().wrap();
17908 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17911 if(typeof(this.tpl) == "string"){
17912 this.tpl = new Roo.Template(this.tpl);
17914 // support xtype ctors..
17915 this.tpl = new Roo.factory(this.tpl, Roo);
17919 this.tpl.compile();
17924 * @event beforeclick
17925 * Fires before a click is processed. Returns false to cancel the default action.
17926 * @param {Roo.View} this
17927 * @param {Number} index The index of the target node
17928 * @param {HTMLElement} node The target node
17929 * @param {Roo.EventObject} e The raw event object
17931 "beforeclick" : true,
17934 * Fires when a template node is clicked.
17935 * @param {Roo.View} this
17936 * @param {Number} index The index of the target node
17937 * @param {HTMLElement} node The target node
17938 * @param {Roo.EventObject} e The raw event object
17943 * Fires when a template node is double clicked.
17944 * @param {Roo.View} this
17945 * @param {Number} index The index of the target node
17946 * @param {HTMLElement} node The target node
17947 * @param {Roo.EventObject} e The raw event object
17951 * @event contextmenu
17952 * Fires when a template node is right clicked.
17953 * @param {Roo.View} this
17954 * @param {Number} index The index of the target node
17955 * @param {HTMLElement} node The target node
17956 * @param {Roo.EventObject} e The raw event object
17958 "contextmenu" : true,
17960 * @event selectionchange
17961 * Fires when the selected nodes change.
17962 * @param {Roo.View} this
17963 * @param {Array} selections Array of the selected nodes
17965 "selectionchange" : true,
17968 * @event beforeselect
17969 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17970 * @param {Roo.View} this
17971 * @param {HTMLElement} node The node to be selected
17972 * @param {Array} selections Array of currently selected nodes
17974 "beforeselect" : true,
17976 * @event preparedata
17977 * Fires on every row to render, to allow you to change the data.
17978 * @param {Roo.View} this
17979 * @param {Object} data to be rendered (change this)
17981 "preparedata" : true
17989 "click": this.onClick,
17990 "dblclick": this.onDblClick,
17991 "contextmenu": this.onContextMenu,
17995 this.selections = [];
17997 this.cmp = new Roo.CompositeElementLite([]);
17999 this.store = Roo.factory(this.store, Roo.data);
18000 this.setStore(this.store, true);
18003 if ( this.footer && this.footer.xtype) {
18005 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18007 this.footer.dataSource = this.store;
18008 this.footer.container = fctr;
18009 this.footer = Roo.factory(this.footer, Roo);
18010 fctr.insertFirst(this.el);
18012 // this is a bit insane - as the paging toolbar seems to detach the el..
18013 // dom.parentNode.parentNode.parentNode
18014 // they get detached?
18018 Roo.View.superclass.constructor.call(this);
18023 Roo.extend(Roo.View, Roo.util.Observable, {
18026 * @cfg {Roo.data.Store} store Data store to load data from.
18031 * @cfg {String|Roo.Element} el The container element.
18036 * @cfg {String|Roo.Template} tpl The template used by this View
18040 * @cfg {String} dataName the named area of the template to use as the data area
18041 * Works with domtemplates roo-name="name"
18045 * @cfg {String} selectedClass The css class to add to selected nodes
18047 selectedClass : "x-view-selected",
18049 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18054 * @cfg {String} text to display on mask (default Loading)
18058 * @cfg {Boolean} multiSelect Allow multiple selection
18060 multiSelect : false,
18062 * @cfg {Boolean} singleSelect Allow single selection
18064 singleSelect: false,
18067 * @cfg {Boolean} toggleSelect - selecting
18069 toggleSelect : false,
18072 * @cfg {Boolean} tickable - selecting
18077 * Returns the element this view is bound to.
18078 * @return {Roo.Element}
18080 getEl : function(){
18081 return this.wrapEl;
18087 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18089 refresh : function(){
18090 //Roo.log('refresh');
18093 // if we are using something like 'domtemplate', then
18094 // the what gets used is:
18095 // t.applySubtemplate(NAME, data, wrapping data..)
18096 // the outer template then get' applied with
18097 // the store 'extra data'
18098 // and the body get's added to the
18099 // roo-name="data" node?
18100 // <span class='roo-tpl-{name}'></span> ?????
18104 this.clearSelections();
18105 this.el.update("");
18107 var records = this.store.getRange();
18108 if(records.length < 1) {
18110 // is this valid?? = should it render a template??
18112 this.el.update(this.emptyText);
18116 if (this.dataName) {
18117 this.el.update(t.apply(this.store.meta)); //????
18118 el = this.el.child('.roo-tpl-' + this.dataName);
18121 for(var i = 0, len = records.length; i < len; i++){
18122 var data = this.prepareData(records[i].data, i, records[i]);
18123 this.fireEvent("preparedata", this, data, i, records[i]);
18125 var d = Roo.apply({}, data);
18128 Roo.apply(d, {'roo-id' : Roo.id()});
18132 Roo.each(this.parent.item, function(item){
18133 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18136 Roo.apply(d, {'roo-data-checked' : 'checked'});
18140 html[html.length] = Roo.util.Format.trim(
18142 t.applySubtemplate(this.dataName, d, this.store.meta) :
18149 el.update(html.join(""));
18150 this.nodes = el.dom.childNodes;
18151 this.updateIndexes(0);
18156 * Function to override to reformat the data that is sent to
18157 * the template for each node.
18158 * DEPRICATED - use the preparedata event handler.
18159 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18160 * a JSON object for an UpdateManager bound view).
18162 prepareData : function(data, index, record)
18164 this.fireEvent("preparedata", this, data, index, record);
18168 onUpdate : function(ds, record){
18169 // Roo.log('on update');
18170 this.clearSelections();
18171 var index = this.store.indexOf(record);
18172 var n = this.nodes[index];
18173 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18174 n.parentNode.removeChild(n);
18175 this.updateIndexes(index, index);
18181 onAdd : function(ds, records, index)
18183 //Roo.log(['on Add', ds, records, index] );
18184 this.clearSelections();
18185 if(this.nodes.length == 0){
18189 var n = this.nodes[index];
18190 for(var i = 0, len = records.length; i < len; i++){
18191 var d = this.prepareData(records[i].data, i, records[i]);
18193 this.tpl.insertBefore(n, d);
18196 this.tpl.append(this.el, d);
18199 this.updateIndexes(index);
18202 onRemove : function(ds, record, index){
18203 // Roo.log('onRemove');
18204 this.clearSelections();
18205 var el = this.dataName ?
18206 this.el.child('.roo-tpl-' + this.dataName) :
18209 el.dom.removeChild(this.nodes[index]);
18210 this.updateIndexes(index);
18214 * Refresh an individual node.
18215 * @param {Number} index
18217 refreshNode : function(index){
18218 this.onUpdate(this.store, this.store.getAt(index));
18221 updateIndexes : function(startIndex, endIndex){
18222 var ns = this.nodes;
18223 startIndex = startIndex || 0;
18224 endIndex = endIndex || ns.length - 1;
18225 for(var i = startIndex; i <= endIndex; i++){
18226 ns[i].nodeIndex = i;
18231 * Changes the data store this view uses and refresh the view.
18232 * @param {Store} store
18234 setStore : function(store, initial){
18235 if(!initial && this.store){
18236 this.store.un("datachanged", this.refresh);
18237 this.store.un("add", this.onAdd);
18238 this.store.un("remove", this.onRemove);
18239 this.store.un("update", this.onUpdate);
18240 this.store.un("clear", this.refresh);
18241 this.store.un("beforeload", this.onBeforeLoad);
18242 this.store.un("load", this.onLoad);
18243 this.store.un("loadexception", this.onLoad);
18247 store.on("datachanged", this.refresh, this);
18248 store.on("add", this.onAdd, this);
18249 store.on("remove", this.onRemove, this);
18250 store.on("update", this.onUpdate, this);
18251 store.on("clear", this.refresh, this);
18252 store.on("beforeload", this.onBeforeLoad, this);
18253 store.on("load", this.onLoad, this);
18254 store.on("loadexception", this.onLoad, this);
18262 * onbeforeLoad - masks the loading area.
18265 onBeforeLoad : function(store,opts)
18267 //Roo.log('onBeforeLoad');
18269 this.el.update("");
18271 this.el.mask(this.mask ? this.mask : "Loading" );
18273 onLoad : function ()
18280 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18281 * @param {HTMLElement} node
18282 * @return {HTMLElement} The template node
18284 findItemFromChild : function(node){
18285 var el = this.dataName ?
18286 this.el.child('.roo-tpl-' + this.dataName,true) :
18289 if(!node || node.parentNode == el){
18292 var p = node.parentNode;
18293 while(p && p != el){
18294 if(p.parentNode == el){
18303 onClick : function(e){
18304 var item = this.findItemFromChild(e.getTarget());
18306 var index = this.indexOf(item);
18307 if(this.onItemClick(item, index, e) !== false){
18308 this.fireEvent("click", this, index, item, e);
18311 this.clearSelections();
18316 onContextMenu : function(e){
18317 var item = this.findItemFromChild(e.getTarget());
18319 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18324 onDblClick : function(e){
18325 var item = this.findItemFromChild(e.getTarget());
18327 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18331 onItemClick : function(item, index, e)
18333 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18336 if (this.toggleSelect) {
18337 var m = this.isSelected(item) ? 'unselect' : 'select';
18340 _t[m](item, true, false);
18343 if(this.multiSelect || this.singleSelect){
18344 if(this.multiSelect && e.shiftKey && this.lastSelection){
18345 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18347 this.select(item, this.multiSelect && e.ctrlKey);
18348 this.lastSelection = item;
18351 if(!this.tickable){
18352 e.preventDefault();
18360 * Get the number of selected nodes.
18363 getSelectionCount : function(){
18364 return this.selections.length;
18368 * Get the currently selected nodes.
18369 * @return {Array} An array of HTMLElements
18371 getSelectedNodes : function(){
18372 return this.selections;
18376 * Get the indexes of the selected nodes.
18379 getSelectedIndexes : function(){
18380 var indexes = [], s = this.selections;
18381 for(var i = 0, len = s.length; i < len; i++){
18382 indexes.push(s[i].nodeIndex);
18388 * Clear all selections
18389 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18391 clearSelections : function(suppressEvent){
18392 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18393 this.cmp.elements = this.selections;
18394 this.cmp.removeClass(this.selectedClass);
18395 this.selections = [];
18396 if(!suppressEvent){
18397 this.fireEvent("selectionchange", this, this.selections);
18403 * Returns true if the passed node is selected
18404 * @param {HTMLElement/Number} node The node or node index
18405 * @return {Boolean}
18407 isSelected : function(node){
18408 var s = this.selections;
18412 node = this.getNode(node);
18413 return s.indexOf(node) !== -1;
18418 * @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
18419 * @param {Boolean} keepExisting (optional) true to keep existing selections
18420 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18422 select : function(nodeInfo, keepExisting, suppressEvent){
18423 if(nodeInfo instanceof Array){
18425 this.clearSelections(true);
18427 for(var i = 0, len = nodeInfo.length; i < len; i++){
18428 this.select(nodeInfo[i], true, true);
18432 var node = this.getNode(nodeInfo);
18433 if(!node || this.isSelected(node)){
18434 return; // already selected.
18437 this.clearSelections(true);
18440 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18441 Roo.fly(node).addClass(this.selectedClass);
18442 this.selections.push(node);
18443 if(!suppressEvent){
18444 this.fireEvent("selectionchange", this, this.selections);
18452 * @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
18453 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18454 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18456 unselect : function(nodeInfo, keepExisting, suppressEvent)
18458 if(nodeInfo instanceof Array){
18459 Roo.each(this.selections, function(s) {
18460 this.unselect(s, nodeInfo);
18464 var node = this.getNode(nodeInfo);
18465 if(!node || !this.isSelected(node)){
18466 //Roo.log("not selected");
18467 return; // not selected.
18471 Roo.each(this.selections, function(s) {
18473 Roo.fly(node).removeClass(this.selectedClass);
18480 this.selections= ns;
18481 this.fireEvent("selectionchange", this, this.selections);
18485 * Gets a template node.
18486 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18487 * @return {HTMLElement} The node or null if it wasn't found
18489 getNode : function(nodeInfo){
18490 if(typeof nodeInfo == "string"){
18491 return document.getElementById(nodeInfo);
18492 }else if(typeof nodeInfo == "number"){
18493 return this.nodes[nodeInfo];
18499 * Gets a range template nodes.
18500 * @param {Number} startIndex
18501 * @param {Number} endIndex
18502 * @return {Array} An array of nodes
18504 getNodes : function(start, end){
18505 var ns = this.nodes;
18506 start = start || 0;
18507 end = typeof end == "undefined" ? ns.length - 1 : end;
18510 for(var i = start; i <= end; i++){
18514 for(var i = start; i >= end; i--){
18522 * Finds the index of the passed node
18523 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18524 * @return {Number} The index of the node or -1
18526 indexOf : function(node){
18527 node = this.getNode(node);
18528 if(typeof node.nodeIndex == "number"){
18529 return node.nodeIndex;
18531 var ns = this.nodes;
18532 for(var i = 0, len = ns.length; i < len; i++){
18543 * based on jquery fullcalendar
18547 Roo.bootstrap = Roo.bootstrap || {};
18549 * @class Roo.bootstrap.Calendar
18550 * @extends Roo.bootstrap.Component
18551 * Bootstrap Calendar class
18552 * @cfg {Boolean} loadMask (true|false) default false
18553 * @cfg {Object} header generate the user specific header of the calendar, default false
18556 * Create a new Container
18557 * @param {Object} config The config object
18562 Roo.bootstrap.Calendar = function(config){
18563 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18567 * Fires when a date is selected
18568 * @param {DatePicker} this
18569 * @param {Date} date The selected date
18573 * @event monthchange
18574 * Fires when the displayed month changes
18575 * @param {DatePicker} this
18576 * @param {Date} date The selected month
18578 'monthchange': true,
18580 * @event evententer
18581 * Fires when mouse over an event
18582 * @param {Calendar} this
18583 * @param {event} Event
18585 'evententer': true,
18587 * @event eventleave
18588 * Fires when the mouse leaves an
18589 * @param {Calendar} this
18592 'eventleave': true,
18594 * @event eventclick
18595 * Fires when the mouse click an
18596 * @param {Calendar} this
18605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18608 * @cfg {Number} startDay
18609 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18617 getAutoCreate : function(){
18620 var fc_button = function(name, corner, style, content ) {
18621 return Roo.apply({},{
18623 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18625 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18628 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18639 style : 'width:100%',
18646 cls : 'fc-header-left',
18648 fc_button('prev', 'left', 'arrow', '‹' ),
18649 fc_button('next', 'right', 'arrow', '›' ),
18650 { tag: 'span', cls: 'fc-header-space' },
18651 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18659 cls : 'fc-header-center',
18663 cls: 'fc-header-title',
18666 html : 'month / year'
18674 cls : 'fc-header-right',
18676 /* fc_button('month', 'left', '', 'month' ),
18677 fc_button('week', '', '', 'week' ),
18678 fc_button('day', 'right', '', 'day' )
18690 header = this.header;
18693 var cal_heads = function() {
18695 // fixme - handle this.
18697 for (var i =0; i < Date.dayNames.length; i++) {
18698 var d = Date.dayNames[i];
18701 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18702 html : d.substring(0,3)
18706 ret[0].cls += ' fc-first';
18707 ret[6].cls += ' fc-last';
18710 var cal_cell = function(n) {
18713 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18718 cls: 'fc-day-number',
18722 cls: 'fc-day-content',
18726 style: 'position: relative;' // height: 17px;
18738 var cal_rows = function() {
18741 for (var r = 0; r < 6; r++) {
18748 for (var i =0; i < Date.dayNames.length; i++) {
18749 var d = Date.dayNames[i];
18750 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18753 row.cn[0].cls+=' fc-first';
18754 row.cn[0].cn[0].style = 'min-height:90px';
18755 row.cn[6].cls+=' fc-last';
18759 ret[0].cls += ' fc-first';
18760 ret[4].cls += ' fc-prev-last';
18761 ret[5].cls += ' fc-last';
18768 cls: 'fc-border-separate',
18769 style : 'width:100%',
18777 cls : 'fc-first fc-last',
18795 cls : 'fc-content',
18796 style : "position: relative;",
18799 cls : 'fc-view fc-view-month fc-grid',
18800 style : 'position: relative',
18801 unselectable : 'on',
18804 cls : 'fc-event-container',
18805 style : 'position:absolute;z-index:8;top:0;left:0;'
18823 initEvents : function()
18826 throw "can not find store for calendar";
18832 style: "text-align:center",
18836 style: "background-color:white;width:50%;margin:250 auto",
18840 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18851 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18853 var size = this.el.select('.fc-content', true).first().getSize();
18854 this.maskEl.setSize(size.width, size.height);
18855 this.maskEl.enableDisplayMode("block");
18856 if(!this.loadMask){
18857 this.maskEl.hide();
18860 this.store = Roo.factory(this.store, Roo.data);
18861 this.store.on('load', this.onLoad, this);
18862 this.store.on('beforeload', this.onBeforeLoad, this);
18866 this.cells = this.el.select('.fc-day',true);
18867 //Roo.log(this.cells);
18868 this.textNodes = this.el.query('.fc-day-number');
18869 this.cells.addClassOnOver('fc-state-hover');
18871 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18872 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18873 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18874 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18876 this.on('monthchange', this.onMonthChange, this);
18878 this.update(new Date().clearTime());
18881 resize : function() {
18882 var sz = this.el.getSize();
18884 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18885 this.el.select('.fc-day-content div',true).setHeight(34);
18890 showPrevMonth : function(e){
18891 this.update(this.activeDate.add("mo", -1));
18893 showToday : function(e){
18894 this.update(new Date().clearTime());
18897 showNextMonth : function(e){
18898 this.update(this.activeDate.add("mo", 1));
18902 showPrevYear : function(){
18903 this.update(this.activeDate.add("y", -1));
18907 showNextYear : function(){
18908 this.update(this.activeDate.add("y", 1));
18913 update : function(date)
18915 var vd = this.activeDate;
18916 this.activeDate = date;
18917 // if(vd && this.el){
18918 // var t = date.getTime();
18919 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18920 // Roo.log('using add remove');
18922 // this.fireEvent('monthchange', this, date);
18924 // this.cells.removeClass("fc-state-highlight");
18925 // this.cells.each(function(c){
18926 // if(c.dateValue == t){
18927 // c.addClass("fc-state-highlight");
18928 // setTimeout(function(){
18929 // try{c.dom.firstChild.focus();}catch(e){}
18939 var days = date.getDaysInMonth();
18941 var firstOfMonth = date.getFirstDateOfMonth();
18942 var startingPos = firstOfMonth.getDay()-this.startDay;
18944 if(startingPos < this.startDay){
18948 var pm = date.add(Date.MONTH, -1);
18949 var prevStart = pm.getDaysInMonth()-startingPos;
18951 this.cells = this.el.select('.fc-day',true);
18952 this.textNodes = this.el.query('.fc-day-number');
18953 this.cells.addClassOnOver('fc-state-hover');
18955 var cells = this.cells.elements;
18956 var textEls = this.textNodes;
18958 Roo.each(cells, function(cell){
18959 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18962 days += startingPos;
18964 // convert everything to numbers so it's fast
18965 var day = 86400000;
18966 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18969 //Roo.log(prevStart);
18971 var today = new Date().clearTime().getTime();
18972 var sel = date.clearTime().getTime();
18973 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18974 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18975 var ddMatch = this.disabledDatesRE;
18976 var ddText = this.disabledDatesText;
18977 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18978 var ddaysText = this.disabledDaysText;
18979 var format = this.format;
18981 var setCellClass = function(cal, cell){
18985 //Roo.log('set Cell Class');
18987 var t = d.getTime();
18991 cell.dateValue = t;
18993 cell.className += " fc-today";
18994 cell.className += " fc-state-highlight";
18995 cell.title = cal.todayText;
18998 // disable highlight in other month..
18999 //cell.className += " fc-state-highlight";
19004 cell.className = " fc-state-disabled";
19005 cell.title = cal.minText;
19009 cell.className = " fc-state-disabled";
19010 cell.title = cal.maxText;
19014 if(ddays.indexOf(d.getDay()) != -1){
19015 cell.title = ddaysText;
19016 cell.className = " fc-state-disabled";
19019 if(ddMatch && format){
19020 var fvalue = d.dateFormat(format);
19021 if(ddMatch.test(fvalue)){
19022 cell.title = ddText.replace("%0", fvalue);
19023 cell.className = " fc-state-disabled";
19027 if (!cell.initialClassName) {
19028 cell.initialClassName = cell.dom.className;
19031 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19036 for(; i < startingPos; i++) {
19037 textEls[i].innerHTML = (++prevStart);
19038 d.setDate(d.getDate()+1);
19040 cells[i].className = "fc-past fc-other-month";
19041 setCellClass(this, cells[i]);
19046 for(; i < days; i++){
19047 intDay = i - startingPos + 1;
19048 textEls[i].innerHTML = (intDay);
19049 d.setDate(d.getDate()+1);
19051 cells[i].className = ''; // "x-date-active";
19052 setCellClass(this, cells[i]);
19056 for(; i < 42; i++) {
19057 textEls[i].innerHTML = (++extraDays);
19058 d.setDate(d.getDate()+1);
19060 cells[i].className = "fc-future fc-other-month";
19061 setCellClass(this, cells[i]);
19064 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19066 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19068 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19069 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19071 if(totalRows != 6){
19072 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19073 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19076 this.fireEvent('monthchange', this, date);
19080 if(!this.internalRender){
19081 var main = this.el.dom.firstChild;
19082 var w = main.offsetWidth;
19083 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19084 Roo.fly(main).setWidth(w);
19085 this.internalRender = true;
19086 // opera does not respect the auto grow header center column
19087 // then, after it gets a width opera refuses to recalculate
19088 // without a second pass
19089 if(Roo.isOpera && !this.secondPass){
19090 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19091 this.secondPass = true;
19092 this.update.defer(10, this, [date]);
19099 findCell : function(dt) {
19100 dt = dt.clearTime().getTime();
19102 this.cells.each(function(c){
19103 //Roo.log("check " +c.dateValue + '?=' + dt);
19104 if(c.dateValue == dt){
19114 findCells : function(ev) {
19115 var s = ev.start.clone().clearTime().getTime();
19117 var e= ev.end.clone().clearTime().getTime();
19120 this.cells.each(function(c){
19121 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19123 if(c.dateValue > e){
19126 if(c.dateValue < s){
19135 // findBestRow: function(cells)
19139 // for (var i =0 ; i < cells.length;i++) {
19140 // ret = Math.max(cells[i].rows || 0,ret);
19147 addItem : function(ev)
19149 // look for vertical location slot in
19150 var cells = this.findCells(ev);
19152 // ev.row = this.findBestRow(cells);
19154 // work out the location.
19158 for(var i =0; i < cells.length; i++) {
19160 cells[i].row = cells[0].row;
19163 cells[i].row = cells[i].row + 1;
19173 if (crow.start.getY() == cells[i].getY()) {
19175 crow.end = cells[i];
19192 cells[0].events.push(ev);
19194 this.calevents.push(ev);
19197 clearEvents: function() {
19199 if(!this.calevents){
19203 Roo.each(this.cells.elements, function(c){
19209 Roo.each(this.calevents, function(e) {
19210 Roo.each(e.els, function(el) {
19211 el.un('mouseenter' ,this.onEventEnter, this);
19212 el.un('mouseleave' ,this.onEventLeave, this);
19217 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19223 renderEvents: function()
19227 this.cells.each(function(c) {
19236 if(c.row != c.events.length){
19237 r = 4 - (4 - (c.row - c.events.length));
19240 c.events = ev.slice(0, r);
19241 c.more = ev.slice(r);
19243 if(c.more.length && c.more.length == 1){
19244 c.events.push(c.more.pop());
19247 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19251 this.cells.each(function(c) {
19253 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19256 for (var e = 0; e < c.events.length; e++){
19257 var ev = c.events[e];
19258 var rows = ev.rows;
19260 for(var i = 0; i < rows.length; i++) {
19262 // how many rows should it span..
19265 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19266 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19268 unselectable : "on",
19271 cls: 'fc-event-inner',
19275 // cls: 'fc-event-time',
19276 // html : cells.length > 1 ? '' : ev.time
19280 cls: 'fc-event-title',
19281 html : String.format('{0}', ev.title)
19288 cls: 'ui-resizable-handle ui-resizable-e',
19289 html : '  '
19296 cfg.cls += ' fc-event-start';
19298 if ((i+1) == rows.length) {
19299 cfg.cls += ' fc-event-end';
19302 var ctr = _this.el.select('.fc-event-container',true).first();
19303 var cg = ctr.createChild(cfg);
19305 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19306 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19308 var r = (c.more.length) ? 1 : 0;
19309 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19310 cg.setWidth(ebox.right - sbox.x -2);
19312 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19313 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19314 cg.on('click', _this.onEventClick, _this, ev);
19325 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19326 style : 'position: absolute',
19327 unselectable : "on",
19330 cls: 'fc-event-inner',
19334 cls: 'fc-event-title',
19342 cls: 'ui-resizable-handle ui-resizable-e',
19343 html : '  '
19349 var ctr = _this.el.select('.fc-event-container',true).first();
19350 var cg = ctr.createChild(cfg);
19352 var sbox = c.select('.fc-day-content',true).first().getBox();
19353 var ebox = c.select('.fc-day-content',true).first().getBox();
19355 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19356 cg.setWidth(ebox.right - sbox.x -2);
19358 cg.on('click', _this.onMoreEventClick, _this, c.more);
19368 onEventEnter: function (e, el,event,d) {
19369 this.fireEvent('evententer', this, el, event);
19372 onEventLeave: function (e, el,event,d) {
19373 this.fireEvent('eventleave', this, el, event);
19376 onEventClick: function (e, el,event,d) {
19377 this.fireEvent('eventclick', this, el, event);
19380 onMonthChange: function () {
19384 onMoreEventClick: function(e, el, more)
19388 this.calpopover.placement = 'right';
19389 this.calpopover.setTitle('More');
19391 this.calpopover.setContent('');
19393 var ctr = this.calpopover.el.select('.popover-content', true).first();
19395 Roo.each(more, function(m){
19397 cls : 'fc-event-hori fc-event-draggable',
19400 var cg = ctr.createChild(cfg);
19402 cg.on('click', _this.onEventClick, _this, m);
19405 this.calpopover.show(el);
19410 onLoad: function ()
19412 this.calevents = [];
19415 if(this.store.getCount() > 0){
19416 this.store.data.each(function(d){
19419 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19420 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19421 time : d.data.start_time,
19422 title : d.data.title,
19423 description : d.data.description,
19424 venue : d.data.venue
19429 this.renderEvents();
19431 if(this.calevents.length && this.loadMask){
19432 this.maskEl.hide();
19436 onBeforeLoad: function()
19438 this.clearEvents();
19440 this.maskEl.show();
19454 * @class Roo.bootstrap.Popover
19455 * @extends Roo.bootstrap.Component
19456 * Bootstrap Popover class
19457 * @cfg {String} html contents of the popover (or false to use children..)
19458 * @cfg {String} title of popover (or false to hide)
19459 * @cfg {String} placement how it is placed
19460 * @cfg {String} trigger click || hover (or false to trigger manually)
19461 * @cfg {String} over what (parent or false to trigger manually.)
19462 * @cfg {Number} delay - delay before showing
19465 * Create a new Popover
19466 * @param {Object} config The config object
19469 Roo.bootstrap.Popover = function(config){
19470 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19476 * After the popover show
19478 * @param {Roo.bootstrap.Popover} this
19483 * After the popover hide
19485 * @param {Roo.bootstrap.Popover} this
19491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19493 title: 'Fill in a title',
19496 placement : 'right',
19497 trigger : 'hover', // hover
19503 can_build_overlaid : false,
19505 getChildContainer : function()
19507 return this.el.select('.popover-content',true).first();
19510 getAutoCreate : function(){
19513 cls : 'popover roo-dynamic',
19514 style: 'display:block',
19520 cls : 'popover-inner',
19524 cls: 'popover-title popover-header',
19528 cls : 'popover-content popover-body',
19539 setTitle: function(str)
19542 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19544 setContent: function(str)
19547 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19549 // as it get's added to the bottom of the page.
19550 onRender : function(ct, position)
19552 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19554 var cfg = Roo.apply({}, this.getAutoCreate());
19558 cfg.cls += ' ' + this.cls;
19561 cfg.style = this.style;
19563 //Roo.log("adding to ");
19564 this.el = Roo.get(document.body).createChild(cfg, position);
19565 // Roo.log(this.el);
19570 initEvents : function()
19572 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19573 this.el.enableDisplayMode('block');
19575 if (this.over === false) {
19578 if (this.triggers === false) {
19581 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19582 var triggers = this.trigger ? this.trigger.split(' ') : [];
19583 Roo.each(triggers, function(trigger) {
19585 if (trigger == 'click') {
19586 on_el.on('click', this.toggle, this);
19587 } else if (trigger != 'manual') {
19588 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19589 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19591 on_el.on(eventIn ,this.enter, this);
19592 on_el.on(eventOut, this.leave, this);
19603 toggle : function () {
19604 this.hoverState == 'in' ? this.leave() : this.enter();
19607 enter : function () {
19609 clearTimeout(this.timeout);
19611 this.hoverState = 'in';
19613 if (!this.delay || !this.delay.show) {
19618 this.timeout = setTimeout(function () {
19619 if (_t.hoverState == 'in') {
19622 }, this.delay.show)
19625 leave : function() {
19626 clearTimeout(this.timeout);
19628 this.hoverState = 'out';
19630 if (!this.delay || !this.delay.hide) {
19635 this.timeout = setTimeout(function () {
19636 if (_t.hoverState == 'out') {
19639 }, this.delay.hide)
19642 show : function (on_el)
19645 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19649 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19650 if (this.html !== false) {
19651 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19653 this.el.removeClass([
19654 'fade','top','bottom', 'left', 'right','in',
19655 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19657 if (!this.title.length) {
19658 this.el.select('.popover-title',true).hide();
19661 var placement = typeof this.placement == 'function' ?
19662 this.placement.call(this, this.el, on_el) :
19665 var autoToken = /\s?auto?\s?/i;
19666 var autoPlace = autoToken.test(placement);
19668 placement = placement.replace(autoToken, '') || 'top';
19672 //this.el.setXY([0,0]);
19674 this.el.dom.style.display='block';
19675 this.el.addClass(placement);
19677 //this.el.appendTo(on_el);
19679 var p = this.getPosition();
19680 var box = this.el.getBox();
19685 var align = Roo.bootstrap.Popover.alignment[placement];
19688 this.el.alignTo(on_el, align[0],align[1]);
19689 //var arrow = this.el.select('.arrow',true).first();
19690 //arrow.set(align[2],
19692 this.el.addClass('in');
19695 if (this.el.hasClass('fade')) {
19699 this.hoverState = 'in';
19701 this.fireEvent('show', this);
19706 this.el.setXY([0,0]);
19707 this.el.removeClass('in');
19709 this.hoverState = null;
19711 this.fireEvent('hide', this);
19716 Roo.bootstrap.Popover.alignment = {
19717 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19718 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19719 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19720 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19731 * @class Roo.bootstrap.Progress
19732 * @extends Roo.bootstrap.Component
19733 * Bootstrap Progress class
19734 * @cfg {Boolean} striped striped of the progress bar
19735 * @cfg {Boolean} active animated of the progress bar
19739 * Create a new Progress
19740 * @param {Object} config The config object
19743 Roo.bootstrap.Progress = function(config){
19744 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19752 getAutoCreate : function(){
19760 cfg.cls += ' progress-striped';
19764 cfg.cls += ' active';
19783 * @class Roo.bootstrap.ProgressBar
19784 * @extends Roo.bootstrap.Component
19785 * Bootstrap ProgressBar class
19786 * @cfg {Number} aria_valuenow aria-value now
19787 * @cfg {Number} aria_valuemin aria-value min
19788 * @cfg {Number} aria_valuemax aria-value max
19789 * @cfg {String} label label for the progress bar
19790 * @cfg {String} panel (success | info | warning | danger )
19791 * @cfg {String} role role of the progress bar
19792 * @cfg {String} sr_only text
19796 * Create a new ProgressBar
19797 * @param {Object} config The config object
19800 Roo.bootstrap.ProgressBar = function(config){
19801 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19808 aria_valuemax : 100,
19814 getAutoCreate : function()
19819 cls: 'progress-bar',
19820 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19832 cfg.role = this.role;
19835 if(this.aria_valuenow){
19836 cfg['aria-valuenow'] = this.aria_valuenow;
19839 if(this.aria_valuemin){
19840 cfg['aria-valuemin'] = this.aria_valuemin;
19843 if(this.aria_valuemax){
19844 cfg['aria-valuemax'] = this.aria_valuemax;
19847 if(this.label && !this.sr_only){
19848 cfg.html = this.label;
19852 cfg.cls += ' progress-bar-' + this.panel;
19858 update : function(aria_valuenow)
19860 this.aria_valuenow = aria_valuenow;
19862 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19877 * @class Roo.bootstrap.TabGroup
19878 * @extends Roo.bootstrap.Column
19879 * Bootstrap Column class
19880 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19881 * @cfg {Boolean} carousel true to make the group behave like a carousel
19882 * @cfg {Boolean} bullets show bullets for the panels
19883 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19884 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19885 * @cfg {Boolean} showarrow (true|false) show arrow default true
19888 * Create a new TabGroup
19889 * @param {Object} config The config object
19892 Roo.bootstrap.TabGroup = function(config){
19893 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19895 this.navId = Roo.id();
19898 Roo.bootstrap.TabGroup.register(this);
19902 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19905 transition : false,
19910 slideOnTouch : false,
19913 getAutoCreate : function()
19915 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19917 cfg.cls += ' tab-content';
19919 if (this.carousel) {
19920 cfg.cls += ' carousel slide';
19923 cls : 'carousel-inner',
19927 if(this.bullets && !Roo.isTouch){
19930 cls : 'carousel-bullets',
19934 if(this.bullets_cls){
19935 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19942 cfg.cn[0].cn.push(bullets);
19945 if(this.showarrow){
19946 cfg.cn[0].cn.push({
19948 class : 'carousel-arrow',
19952 class : 'carousel-prev',
19956 class : 'fa fa-chevron-left'
19962 class : 'carousel-next',
19966 class : 'fa fa-chevron-right'
19979 initEvents: function()
19981 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19982 // this.el.on("touchstart", this.onTouchStart, this);
19985 if(this.autoslide){
19988 this.slideFn = window.setInterval(function() {
19989 _this.showPanelNext();
19993 if(this.showarrow){
19994 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19995 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20001 // onTouchStart : function(e, el, o)
20003 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20007 // this.showPanelNext();
20011 getChildContainer : function()
20013 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20017 * register a Navigation item
20018 * @param {Roo.bootstrap.NavItem} the navitem to add
20020 register : function(item)
20022 this.tabs.push( item);
20023 item.navId = this.navId; // not really needed..
20028 getActivePanel : function()
20031 Roo.each(this.tabs, function(t) {
20041 getPanelByName : function(n)
20044 Roo.each(this.tabs, function(t) {
20045 if (t.tabId == n) {
20053 indexOfPanel : function(p)
20056 Roo.each(this.tabs, function(t,i) {
20057 if (t.tabId == p.tabId) {
20066 * show a specific panel
20067 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20068 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20070 showPanel : function (pan)
20072 if(this.transition || typeof(pan) == 'undefined'){
20073 Roo.log("waiting for the transitionend");
20077 if (typeof(pan) == 'number') {
20078 pan = this.tabs[pan];
20081 if (typeof(pan) == 'string') {
20082 pan = this.getPanelByName(pan);
20085 var cur = this.getActivePanel();
20088 Roo.log('pan or acitve pan is undefined');
20092 if (pan.tabId == this.getActivePanel().tabId) {
20096 if (false === cur.fireEvent('beforedeactivate')) {
20100 if(this.bullets > 0 && !Roo.isTouch){
20101 this.setActiveBullet(this.indexOfPanel(pan));
20104 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20106 //class="carousel-item carousel-item-next carousel-item-left"
20108 this.transition = true;
20109 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20110 var lr = dir == 'next' ? 'left' : 'right';
20111 pan.el.addClass(dir); // or prev
20112 pan.el.addClass('carousel-item-' + dir); // or prev
20113 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20114 cur.el.addClass(lr); // or right
20115 pan.el.addClass(lr);
20116 cur.el.addClass('carousel-item-' +lr); // or right
20117 pan.el.addClass('carousel-item-' +lr);
20121 cur.el.on('transitionend', function() {
20122 Roo.log("trans end?");
20124 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20125 pan.setActive(true);
20127 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20128 cur.setActive(false);
20130 _this.transition = false;
20132 }, this, { single: true } );
20137 cur.setActive(false);
20138 pan.setActive(true);
20143 showPanelNext : function()
20145 var i = this.indexOfPanel(this.getActivePanel());
20147 if (i >= this.tabs.length - 1 && !this.autoslide) {
20151 if (i >= this.tabs.length - 1 && this.autoslide) {
20155 this.showPanel(this.tabs[i+1]);
20158 showPanelPrev : function()
20160 var i = this.indexOfPanel(this.getActivePanel());
20162 if (i < 1 && !this.autoslide) {
20166 if (i < 1 && this.autoslide) {
20167 i = this.tabs.length;
20170 this.showPanel(this.tabs[i-1]);
20174 addBullet: function()
20176 if(!this.bullets || Roo.isTouch){
20179 var ctr = this.el.select('.carousel-bullets',true).first();
20180 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20181 var bullet = ctr.createChild({
20182 cls : 'bullet bullet-' + i
20183 },ctr.dom.lastChild);
20188 bullet.on('click', (function(e, el, o, ii, t){
20190 e.preventDefault();
20192 this.showPanel(ii);
20194 if(this.autoslide && this.slideFn){
20195 clearInterval(this.slideFn);
20196 this.slideFn = window.setInterval(function() {
20197 _this.showPanelNext();
20201 }).createDelegate(this, [i, bullet], true));
20206 setActiveBullet : function(i)
20212 Roo.each(this.el.select('.bullet', true).elements, function(el){
20213 el.removeClass('selected');
20216 var bullet = this.el.select('.bullet-' + i, true).first();
20222 bullet.addClass('selected');
20233 Roo.apply(Roo.bootstrap.TabGroup, {
20237 * register a Navigation Group
20238 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20240 register : function(navgrp)
20242 this.groups[navgrp.navId] = navgrp;
20246 * fetch a Navigation Group based on the navigation ID
20247 * if one does not exist , it will get created.
20248 * @param {string} the navgroup to add
20249 * @returns {Roo.bootstrap.NavGroup} the navgroup
20251 get: function(navId) {
20252 if (typeof(this.groups[navId]) == 'undefined') {
20253 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20255 return this.groups[navId] ;
20270 * @class Roo.bootstrap.TabPanel
20271 * @extends Roo.bootstrap.Component
20272 * Bootstrap TabPanel class
20273 * @cfg {Boolean} active panel active
20274 * @cfg {String} html panel content
20275 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20276 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20277 * @cfg {String} href click to link..
20281 * Create a new TabPanel
20282 * @param {Object} config The config object
20285 Roo.bootstrap.TabPanel = function(config){
20286 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20290 * Fires when the active status changes
20291 * @param {Roo.bootstrap.TabPanel} this
20292 * @param {Boolean} state the new state
20297 * @event beforedeactivate
20298 * Fires before a tab is de-activated - can be used to do validation on a form.
20299 * @param {Roo.bootstrap.TabPanel} this
20300 * @return {Boolean} false if there is an error
20303 'beforedeactivate': true
20306 this.tabId = this.tabId || Roo.id();
20310 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20318 getAutoCreate : function(){
20323 // item is needed for carousel - not sure if it has any effect otherwise
20324 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20325 html: this.html || ''
20329 cfg.cls += ' active';
20333 cfg.tabId = this.tabId;
20341 initEvents: function()
20343 var p = this.parent();
20345 this.navId = this.navId || p.navId;
20347 if (typeof(this.navId) != 'undefined') {
20348 // not really needed.. but just in case.. parent should be a NavGroup.
20349 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20353 var i = tg.tabs.length - 1;
20355 if(this.active && tg.bullets > 0 && i < tg.bullets){
20356 tg.setActiveBullet(i);
20360 this.el.on('click', this.onClick, this);
20363 this.el.on("touchstart", this.onTouchStart, this);
20364 this.el.on("touchmove", this.onTouchMove, this);
20365 this.el.on("touchend", this.onTouchEnd, this);
20370 onRender : function(ct, position)
20372 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20375 setActive : function(state)
20377 Roo.log("panel - set active " + this.tabId + "=" + state);
20379 this.active = state;
20381 this.el.removeClass('active');
20383 } else if (!this.el.hasClass('active')) {
20384 this.el.addClass('active');
20387 this.fireEvent('changed', this, state);
20390 onClick : function(e)
20392 e.preventDefault();
20394 if(!this.href.length){
20398 window.location.href = this.href;
20407 onTouchStart : function(e)
20409 this.swiping = false;
20411 this.startX = e.browserEvent.touches[0].clientX;
20412 this.startY = e.browserEvent.touches[0].clientY;
20415 onTouchMove : function(e)
20417 this.swiping = true;
20419 this.endX = e.browserEvent.touches[0].clientX;
20420 this.endY = e.browserEvent.touches[0].clientY;
20423 onTouchEnd : function(e)
20430 var tabGroup = this.parent();
20432 if(this.endX > this.startX){ // swiping right
20433 tabGroup.showPanelPrev();
20437 if(this.startX > this.endX){ // swiping left
20438 tabGroup.showPanelNext();
20457 * @class Roo.bootstrap.DateField
20458 * @extends Roo.bootstrap.Input
20459 * Bootstrap DateField class
20460 * @cfg {Number} weekStart default 0
20461 * @cfg {String} viewMode default empty, (months|years)
20462 * @cfg {String} minViewMode default empty, (months|years)
20463 * @cfg {Number} startDate default -Infinity
20464 * @cfg {Number} endDate default Infinity
20465 * @cfg {Boolean} todayHighlight default false
20466 * @cfg {Boolean} todayBtn default false
20467 * @cfg {Boolean} calendarWeeks default false
20468 * @cfg {Object} daysOfWeekDisabled default empty
20469 * @cfg {Boolean} singleMode default false (true | false)
20471 * @cfg {Boolean} keyboardNavigation default true
20472 * @cfg {String} language default en
20475 * Create a new DateField
20476 * @param {Object} config The config object
20479 Roo.bootstrap.DateField = function(config){
20480 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20484 * Fires when this field show.
20485 * @param {Roo.bootstrap.DateField} this
20486 * @param {Mixed} date The date value
20491 * Fires when this field hide.
20492 * @param {Roo.bootstrap.DateField} this
20493 * @param {Mixed} date The date value
20498 * Fires when select a date.
20499 * @param {Roo.bootstrap.DateField} this
20500 * @param {Mixed} date The date value
20504 * @event beforeselect
20505 * Fires when before select a date.
20506 * @param {Roo.bootstrap.DateField} this
20507 * @param {Mixed} date The date value
20509 beforeselect : true
20513 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20516 * @cfg {String} format
20517 * The default date format string which can be overriden for localization support. The format must be
20518 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20522 * @cfg {String} altFormats
20523 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20524 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20526 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20534 todayHighlight : false,
20540 keyboardNavigation: true,
20542 calendarWeeks: false,
20544 startDate: -Infinity,
20548 daysOfWeekDisabled: [],
20552 singleMode : false,
20554 UTCDate: function()
20556 return new Date(Date.UTC.apply(Date, arguments));
20559 UTCToday: function()
20561 var today = new Date();
20562 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20565 getDate: function() {
20566 var d = this.getUTCDate();
20567 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20570 getUTCDate: function() {
20574 setDate: function(d) {
20575 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20578 setUTCDate: function(d) {
20580 this.setValue(this.formatDate(this.date));
20583 onRender: function(ct, position)
20586 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20588 this.language = this.language || 'en';
20589 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20590 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20592 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20593 this.format = this.format || 'm/d/y';
20594 this.isInline = false;
20595 this.isInput = true;
20596 this.component = this.el.select('.add-on', true).first() || false;
20597 this.component = (this.component && this.component.length === 0) ? false : this.component;
20598 this.hasInput = this.component && this.inputEl().length;
20600 if (typeof(this.minViewMode === 'string')) {
20601 switch (this.minViewMode) {
20603 this.minViewMode = 1;
20606 this.minViewMode = 2;
20609 this.minViewMode = 0;
20614 if (typeof(this.viewMode === 'string')) {
20615 switch (this.viewMode) {
20628 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20630 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20632 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20634 this.picker().on('mousedown', this.onMousedown, this);
20635 this.picker().on('click', this.onClick, this);
20637 this.picker().addClass('datepicker-dropdown');
20639 this.startViewMode = this.viewMode;
20641 if(this.singleMode){
20642 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20643 v.setVisibilityMode(Roo.Element.DISPLAY);
20647 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20648 v.setStyle('width', '189px');
20652 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20653 if(!this.calendarWeeks){
20658 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20659 v.attr('colspan', function(i, val){
20660 return parseInt(val) + 1;
20665 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20667 this.setStartDate(this.startDate);
20668 this.setEndDate(this.endDate);
20670 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20677 if(this.isInline) {
20682 picker : function()
20684 return this.pickerEl;
20685 // return this.el.select('.datepicker', true).first();
20688 fillDow: function()
20690 var dowCnt = this.weekStart;
20699 if(this.calendarWeeks){
20707 while (dowCnt < this.weekStart + 7) {
20711 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20715 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20718 fillMonths: function()
20721 var months = this.picker().select('>.datepicker-months td', true).first();
20723 months.dom.innerHTML = '';
20729 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20732 months.createChild(month);
20739 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;
20741 if (this.date < this.startDate) {
20742 this.viewDate = new Date(this.startDate);
20743 } else if (this.date > this.endDate) {
20744 this.viewDate = new Date(this.endDate);
20746 this.viewDate = new Date(this.date);
20754 var d = new Date(this.viewDate),
20755 year = d.getUTCFullYear(),
20756 month = d.getUTCMonth(),
20757 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20758 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20759 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20760 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20761 currentDate = this.date && this.date.valueOf(),
20762 today = this.UTCToday();
20764 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20766 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20768 // this.picker.select('>tfoot th.today').
20769 // .text(dates[this.language].today)
20770 // .toggle(this.todayBtn !== false);
20772 this.updateNavArrows();
20775 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20777 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20779 prevMonth.setUTCDate(day);
20781 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20783 var nextMonth = new Date(prevMonth);
20785 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20787 nextMonth = nextMonth.valueOf();
20789 var fillMonths = false;
20791 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20793 while(prevMonth.valueOf() <= nextMonth) {
20796 if (prevMonth.getUTCDay() === this.weekStart) {
20798 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20806 if(this.calendarWeeks){
20807 // ISO 8601: First week contains first thursday.
20808 // ISO also states week starts on Monday, but we can be more abstract here.
20810 // Start of current week: based on weekstart/current date
20811 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20812 // Thursday of this week
20813 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20814 // First Thursday of year, year from thursday
20815 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20816 // Calendar week: ms between thursdays, div ms per day, div 7 days
20817 calWeek = (th - yth) / 864e5 / 7 + 1;
20819 fillMonths.cn.push({
20827 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20829 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20832 if (this.todayHighlight &&
20833 prevMonth.getUTCFullYear() == today.getFullYear() &&
20834 prevMonth.getUTCMonth() == today.getMonth() &&
20835 prevMonth.getUTCDate() == today.getDate()) {
20836 clsName += ' today';
20839 if (currentDate && prevMonth.valueOf() === currentDate) {
20840 clsName += ' active';
20843 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20844 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20845 clsName += ' disabled';
20848 fillMonths.cn.push({
20850 cls: 'day ' + clsName,
20851 html: prevMonth.getDate()
20854 prevMonth.setDate(prevMonth.getDate()+1);
20857 var currentYear = this.date && this.date.getUTCFullYear();
20858 var currentMonth = this.date && this.date.getUTCMonth();
20860 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20862 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20863 v.removeClass('active');
20865 if(currentYear === year && k === currentMonth){
20866 v.addClass('active');
20869 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20870 v.addClass('disabled');
20876 year = parseInt(year/10, 10) * 10;
20878 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20880 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20883 for (var i = -1; i < 11; i++) {
20884 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20886 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20894 showMode: function(dir)
20897 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20900 Roo.each(this.picker().select('>div',true).elements, function(v){
20901 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20904 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20909 if(this.isInline) {
20913 this.picker().removeClass(['bottom', 'top']);
20915 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20917 * place to the top of element!
20921 this.picker().addClass('top');
20922 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20927 this.picker().addClass('bottom');
20929 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20932 parseDate : function(value)
20934 if(!value || value instanceof Date){
20937 var v = Date.parseDate(value, this.format);
20938 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20939 v = Date.parseDate(value, 'Y-m-d');
20941 if(!v && this.altFormats){
20942 if(!this.altFormatsArray){
20943 this.altFormatsArray = this.altFormats.split("|");
20945 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20946 v = Date.parseDate(value, this.altFormatsArray[i]);
20952 formatDate : function(date, fmt)
20954 return (!date || !(date instanceof Date)) ?
20955 date : date.dateFormat(fmt || this.format);
20958 onFocus : function()
20960 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20964 onBlur : function()
20966 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20968 var d = this.inputEl().getValue();
20975 showPopup : function()
20977 this.picker().show();
20981 this.fireEvent('showpopup', this, this.date);
20984 hidePopup : function()
20986 if(this.isInline) {
20989 this.picker().hide();
20990 this.viewMode = this.startViewMode;
20993 this.fireEvent('hidepopup', this, this.date);
20997 onMousedown: function(e)
20999 e.stopPropagation();
21000 e.preventDefault();
21005 Roo.bootstrap.DateField.superclass.keyup.call(this);
21009 setValue: function(v)
21011 if(this.fireEvent('beforeselect', this, v) !== false){
21012 var d = new Date(this.parseDate(v) ).clearTime();
21014 if(isNaN(d.getTime())){
21015 this.date = this.viewDate = '';
21016 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21020 v = this.formatDate(d);
21022 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21024 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21028 this.fireEvent('select', this, this.date);
21032 getValue: function()
21034 return this.formatDate(this.date);
21037 fireKey: function(e)
21039 if (!this.picker().isVisible()){
21040 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21046 var dateChanged = false,
21048 newDate, newViewDate;
21053 e.preventDefault();
21057 if (!this.keyboardNavigation) {
21060 dir = e.keyCode == 37 ? -1 : 1;
21063 newDate = this.moveYear(this.date, dir);
21064 newViewDate = this.moveYear(this.viewDate, dir);
21065 } else if (e.shiftKey){
21066 newDate = this.moveMonth(this.date, dir);
21067 newViewDate = this.moveMonth(this.viewDate, dir);
21069 newDate = new Date(this.date);
21070 newDate.setUTCDate(this.date.getUTCDate() + dir);
21071 newViewDate = new Date(this.viewDate);
21072 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21074 if (this.dateWithinRange(newDate)){
21075 this.date = newDate;
21076 this.viewDate = newViewDate;
21077 this.setValue(this.formatDate(this.date));
21079 e.preventDefault();
21080 dateChanged = true;
21085 if (!this.keyboardNavigation) {
21088 dir = e.keyCode == 38 ? -1 : 1;
21090 newDate = this.moveYear(this.date, dir);
21091 newViewDate = this.moveYear(this.viewDate, dir);
21092 } else if (e.shiftKey){
21093 newDate = this.moveMonth(this.date, dir);
21094 newViewDate = this.moveMonth(this.viewDate, dir);
21096 newDate = new Date(this.date);
21097 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21098 newViewDate = new Date(this.viewDate);
21099 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21101 if (this.dateWithinRange(newDate)){
21102 this.date = newDate;
21103 this.viewDate = newViewDate;
21104 this.setValue(this.formatDate(this.date));
21106 e.preventDefault();
21107 dateChanged = true;
21111 this.setValue(this.formatDate(this.date));
21113 e.preventDefault();
21116 this.setValue(this.formatDate(this.date));
21130 onClick: function(e)
21132 e.stopPropagation();
21133 e.preventDefault();
21135 var target = e.getTarget();
21137 if(target.nodeName.toLowerCase() === 'i'){
21138 target = Roo.get(target).dom.parentNode;
21141 var nodeName = target.nodeName;
21142 var className = target.className;
21143 var html = target.innerHTML;
21144 //Roo.log(nodeName);
21146 switch(nodeName.toLowerCase()) {
21148 switch(className) {
21154 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21155 switch(this.viewMode){
21157 this.viewDate = this.moveMonth(this.viewDate, dir);
21161 this.viewDate = this.moveYear(this.viewDate, dir);
21167 var date = new Date();
21168 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21170 this.setValue(this.formatDate(this.date));
21177 if (className.indexOf('disabled') < 0) {
21178 this.viewDate.setUTCDate(1);
21179 if (className.indexOf('month') > -1) {
21180 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21182 var year = parseInt(html, 10) || 0;
21183 this.viewDate.setUTCFullYear(year);
21187 if(this.singleMode){
21188 this.setValue(this.formatDate(this.viewDate));
21199 //Roo.log(className);
21200 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21201 var day = parseInt(html, 10) || 1;
21202 var year = this.viewDate.getUTCFullYear(),
21203 month = this.viewDate.getUTCMonth();
21205 if (className.indexOf('old') > -1) {
21212 } else if (className.indexOf('new') > -1) {
21220 //Roo.log([year,month,day]);
21221 this.date = this.UTCDate(year, month, day,0,0,0,0);
21222 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21224 //Roo.log(this.formatDate(this.date));
21225 this.setValue(this.formatDate(this.date));
21232 setStartDate: function(startDate)
21234 this.startDate = startDate || -Infinity;
21235 if (this.startDate !== -Infinity) {
21236 this.startDate = this.parseDate(this.startDate);
21239 this.updateNavArrows();
21242 setEndDate: function(endDate)
21244 this.endDate = endDate || Infinity;
21245 if (this.endDate !== Infinity) {
21246 this.endDate = this.parseDate(this.endDate);
21249 this.updateNavArrows();
21252 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21254 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21255 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21256 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21258 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21259 return parseInt(d, 10);
21262 this.updateNavArrows();
21265 updateNavArrows: function()
21267 if(this.singleMode){
21271 var d = new Date(this.viewDate),
21272 year = d.getUTCFullYear(),
21273 month = d.getUTCMonth();
21275 Roo.each(this.picker().select('.prev', true).elements, function(v){
21277 switch (this.viewMode) {
21280 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21286 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21293 Roo.each(this.picker().select('.next', true).elements, function(v){
21295 switch (this.viewMode) {
21298 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21304 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21312 moveMonth: function(date, dir)
21317 var new_date = new Date(date.valueOf()),
21318 day = new_date.getUTCDate(),
21319 month = new_date.getUTCMonth(),
21320 mag = Math.abs(dir),
21322 dir = dir > 0 ? 1 : -1;
21325 // If going back one month, make sure month is not current month
21326 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21328 return new_date.getUTCMonth() == month;
21330 // If going forward one month, make sure month is as expected
21331 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21333 return new_date.getUTCMonth() != new_month;
21335 new_month = month + dir;
21336 new_date.setUTCMonth(new_month);
21337 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21338 if (new_month < 0 || new_month > 11) {
21339 new_month = (new_month + 12) % 12;
21342 // For magnitudes >1, move one month at a time...
21343 for (var i=0; i<mag; i++) {
21344 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21345 new_date = this.moveMonth(new_date, dir);
21347 // ...then reset the day, keeping it in the new month
21348 new_month = new_date.getUTCMonth();
21349 new_date.setUTCDate(day);
21351 return new_month != new_date.getUTCMonth();
21354 // Common date-resetting loop -- if date is beyond end of month, make it
21357 new_date.setUTCDate(--day);
21358 new_date.setUTCMonth(new_month);
21363 moveYear: function(date, dir)
21365 return this.moveMonth(date, dir*12);
21368 dateWithinRange: function(date)
21370 return date >= this.startDate && date <= this.endDate;
21376 this.picker().remove();
21379 validateValue : function(value)
21381 if(this.getVisibilityEl().hasClass('hidden')){
21385 if(value.length < 1) {
21386 if(this.allowBlank){
21392 if(value.length < this.minLength){
21395 if(value.length > this.maxLength){
21399 var vt = Roo.form.VTypes;
21400 if(!vt[this.vtype](value, this)){
21404 if(typeof this.validator == "function"){
21405 var msg = this.validator(value);
21411 if(this.regex && !this.regex.test(value)){
21415 if(typeof(this.parseDate(value)) == 'undefined'){
21419 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21423 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21433 this.date = this.viewDate = '';
21435 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21440 Roo.apply(Roo.bootstrap.DateField, {
21451 html: '<i class="fa fa-arrow-left"/>'
21461 html: '<i class="fa fa-arrow-right"/>'
21503 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21504 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21505 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21506 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21507 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21520 navFnc: 'FullYear',
21525 navFnc: 'FullYear',
21530 Roo.apply(Roo.bootstrap.DateField, {
21534 cls: 'datepicker dropdown-menu roo-dynamic',
21538 cls: 'datepicker-days',
21542 cls: 'table-condensed',
21544 Roo.bootstrap.DateField.head,
21548 Roo.bootstrap.DateField.footer
21555 cls: 'datepicker-months',
21559 cls: 'table-condensed',
21561 Roo.bootstrap.DateField.head,
21562 Roo.bootstrap.DateField.content,
21563 Roo.bootstrap.DateField.footer
21570 cls: 'datepicker-years',
21574 cls: 'table-condensed',
21576 Roo.bootstrap.DateField.head,
21577 Roo.bootstrap.DateField.content,
21578 Roo.bootstrap.DateField.footer
21597 * @class Roo.bootstrap.TimeField
21598 * @extends Roo.bootstrap.Input
21599 * Bootstrap DateField class
21603 * Create a new TimeField
21604 * @param {Object} config The config object
21607 Roo.bootstrap.TimeField = function(config){
21608 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21612 * Fires when this field show.
21613 * @param {Roo.bootstrap.DateField} thisthis
21614 * @param {Mixed} date The date value
21619 * Fires when this field hide.
21620 * @param {Roo.bootstrap.DateField} this
21621 * @param {Mixed} date The date value
21626 * Fires when select a date.
21627 * @param {Roo.bootstrap.DateField} this
21628 * @param {Mixed} date The date value
21634 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21637 * @cfg {String} format
21638 * The default time format string which can be overriden for localization support. The format must be
21639 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21643 onRender: function(ct, position)
21646 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21648 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21650 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21652 this.pop = this.picker().select('>.datepicker-time',true).first();
21653 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21655 this.picker().on('mousedown', this.onMousedown, this);
21656 this.picker().on('click', this.onClick, this);
21658 this.picker().addClass('datepicker-dropdown');
21663 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21664 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21665 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21666 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21667 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21668 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21672 fireKey: function(e){
21673 if (!this.picker().isVisible()){
21674 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21680 e.preventDefault();
21688 this.onTogglePeriod();
21691 this.onIncrementMinutes();
21694 this.onDecrementMinutes();
21703 onClick: function(e) {
21704 e.stopPropagation();
21705 e.preventDefault();
21708 picker : function()
21710 return this.el.select('.datepicker', true).first();
21713 fillTime: function()
21715 var time = this.pop.select('tbody', true).first();
21717 time.dom.innerHTML = '';
21732 cls: 'hours-up glyphicon glyphicon-chevron-up'
21752 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21773 cls: 'timepicker-hour',
21788 cls: 'timepicker-minute',
21803 cls: 'btn btn-primary period',
21825 cls: 'hours-down glyphicon glyphicon-chevron-down'
21845 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21863 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21870 var hours = this.time.getHours();
21871 var minutes = this.time.getMinutes();
21884 hours = hours - 12;
21888 hours = '0' + hours;
21892 minutes = '0' + minutes;
21895 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21896 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21897 this.pop.select('button', true).first().dom.innerHTML = period;
21903 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21905 var cls = ['bottom'];
21907 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21914 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21919 this.picker().addClass(cls.join('-'));
21923 Roo.each(cls, function(c){
21925 _this.picker().setTop(_this.inputEl().getHeight());
21929 _this.picker().setTop(0 - _this.picker().getHeight());
21934 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21938 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21945 onFocus : function()
21947 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21951 onBlur : function()
21953 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21959 this.picker().show();
21964 this.fireEvent('show', this, this.date);
21969 this.picker().hide();
21972 this.fireEvent('hide', this, this.date);
21975 setTime : function()
21978 this.setValue(this.time.format(this.format));
21980 this.fireEvent('select', this, this.date);
21985 onMousedown: function(e){
21986 e.stopPropagation();
21987 e.preventDefault();
21990 onIncrementHours: function()
21992 Roo.log('onIncrementHours');
21993 this.time = this.time.add(Date.HOUR, 1);
21998 onDecrementHours: function()
22000 Roo.log('onDecrementHours');
22001 this.time = this.time.add(Date.HOUR, -1);
22005 onIncrementMinutes: function()
22007 Roo.log('onIncrementMinutes');
22008 this.time = this.time.add(Date.MINUTE, 1);
22012 onDecrementMinutes: function()
22014 Roo.log('onDecrementMinutes');
22015 this.time = this.time.add(Date.MINUTE, -1);
22019 onTogglePeriod: function()
22021 Roo.log('onTogglePeriod');
22022 this.time = this.time.add(Date.HOUR, 12);
22029 Roo.apply(Roo.bootstrap.TimeField, {
22059 cls: 'btn btn-info ok',
22071 Roo.apply(Roo.bootstrap.TimeField, {
22075 cls: 'datepicker dropdown-menu',
22079 cls: 'datepicker-time',
22083 cls: 'table-condensed',
22085 Roo.bootstrap.TimeField.content,
22086 Roo.bootstrap.TimeField.footer
22105 * @class Roo.bootstrap.MonthField
22106 * @extends Roo.bootstrap.Input
22107 * Bootstrap MonthField class
22109 * @cfg {String} language default en
22112 * Create a new MonthField
22113 * @param {Object} config The config object
22116 Roo.bootstrap.MonthField = function(config){
22117 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22122 * Fires when this field show.
22123 * @param {Roo.bootstrap.MonthField} this
22124 * @param {Mixed} date The date value
22129 * Fires when this field hide.
22130 * @param {Roo.bootstrap.MonthField} this
22131 * @param {Mixed} date The date value
22136 * Fires when select a date.
22137 * @param {Roo.bootstrap.MonthField} this
22138 * @param {String} oldvalue The old value
22139 * @param {String} newvalue The new value
22145 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22147 onRender: function(ct, position)
22150 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22152 this.language = this.language || 'en';
22153 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22154 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22156 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22157 this.isInline = false;
22158 this.isInput = true;
22159 this.component = this.el.select('.add-on', true).first() || false;
22160 this.component = (this.component && this.component.length === 0) ? false : this.component;
22161 this.hasInput = this.component && this.inputEL().length;
22163 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22165 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22167 this.picker().on('mousedown', this.onMousedown, this);
22168 this.picker().on('click', this.onClick, this);
22170 this.picker().addClass('datepicker-dropdown');
22172 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22173 v.setStyle('width', '189px');
22180 if(this.isInline) {
22186 setValue: function(v, suppressEvent)
22188 var o = this.getValue();
22190 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22194 if(suppressEvent !== true){
22195 this.fireEvent('select', this, o, v);
22200 getValue: function()
22205 onClick: function(e)
22207 e.stopPropagation();
22208 e.preventDefault();
22210 var target = e.getTarget();
22212 if(target.nodeName.toLowerCase() === 'i'){
22213 target = Roo.get(target).dom.parentNode;
22216 var nodeName = target.nodeName;
22217 var className = target.className;
22218 var html = target.innerHTML;
22220 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22224 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22226 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22232 picker : function()
22234 return this.pickerEl;
22237 fillMonths: function()
22240 var months = this.picker().select('>.datepicker-months td', true).first();
22242 months.dom.innerHTML = '';
22248 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22251 months.createChild(month);
22260 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22261 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22264 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22265 e.removeClass('active');
22267 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22268 e.addClass('active');
22275 if(this.isInline) {
22279 this.picker().removeClass(['bottom', 'top']);
22281 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22283 * place to the top of element!
22287 this.picker().addClass('top');
22288 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22293 this.picker().addClass('bottom');
22295 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22298 onFocus : function()
22300 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22304 onBlur : function()
22306 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22308 var d = this.inputEl().getValue();
22317 this.picker().show();
22318 this.picker().select('>.datepicker-months', true).first().show();
22322 this.fireEvent('show', this, this.date);
22327 if(this.isInline) {
22330 this.picker().hide();
22331 this.fireEvent('hide', this, this.date);
22335 onMousedown: function(e)
22337 e.stopPropagation();
22338 e.preventDefault();
22343 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22347 fireKey: function(e)
22349 if (!this.picker().isVisible()){
22350 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22361 e.preventDefault();
22365 dir = e.keyCode == 37 ? -1 : 1;
22367 this.vIndex = this.vIndex + dir;
22369 if(this.vIndex < 0){
22373 if(this.vIndex > 11){
22377 if(isNaN(this.vIndex)){
22381 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22387 dir = e.keyCode == 38 ? -1 : 1;
22389 this.vIndex = this.vIndex + dir * 4;
22391 if(this.vIndex < 0){
22395 if(this.vIndex > 11){
22399 if(isNaN(this.vIndex)){
22403 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22408 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22409 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22413 e.preventDefault();
22416 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22417 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22433 this.picker().remove();
22438 Roo.apply(Roo.bootstrap.MonthField, {
22457 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22458 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22463 Roo.apply(Roo.bootstrap.MonthField, {
22467 cls: 'datepicker dropdown-menu roo-dynamic',
22471 cls: 'datepicker-months',
22475 cls: 'table-condensed',
22477 Roo.bootstrap.DateField.content
22497 * @class Roo.bootstrap.CheckBox
22498 * @extends Roo.bootstrap.Input
22499 * Bootstrap CheckBox class
22501 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22502 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22503 * @cfg {String} boxLabel The text that appears beside the checkbox
22504 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22505 * @cfg {Boolean} checked initnal the element
22506 * @cfg {Boolean} inline inline the element (default false)
22507 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22508 * @cfg {String} tooltip label tooltip
22511 * Create a new CheckBox
22512 * @param {Object} config The config object
22515 Roo.bootstrap.CheckBox = function(config){
22516 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22521 * Fires when the element is checked or unchecked.
22522 * @param {Roo.bootstrap.CheckBox} this This input
22523 * @param {Boolean} checked The new checked value
22528 * Fires when the element is click.
22529 * @param {Roo.bootstrap.CheckBox} this This input
22536 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22538 inputType: 'checkbox',
22547 // checkbox success does not make any sense really..
22552 getAutoCreate : function()
22554 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22560 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22563 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22569 type : this.inputType,
22570 value : this.inputValue,
22571 cls : 'roo-' + this.inputType, //'form-box',
22572 placeholder : this.placeholder || ''
22576 if(this.inputType != 'radio'){
22580 cls : 'roo-hidden-value',
22581 value : this.checked ? this.inputValue : this.valueOff
22586 if (this.weight) { // Validity check?
22587 cfg.cls += " " + this.inputType + "-" + this.weight;
22590 if (this.disabled) {
22591 input.disabled=true;
22595 input.checked = this.checked;
22600 input.name = this.name;
22602 if(this.inputType != 'radio'){
22603 hidden.name = this.name;
22604 input.name = '_hidden_' + this.name;
22609 input.cls += ' input-' + this.size;
22614 ['xs','sm','md','lg'].map(function(size){
22615 if (settings[size]) {
22616 cfg.cls += ' col-' + size + '-' + settings[size];
22620 var inputblock = input;
22622 if (this.before || this.after) {
22625 cls : 'input-group',
22630 inputblock.cn.push({
22632 cls : 'input-group-addon',
22637 inputblock.cn.push(input);
22639 if(this.inputType != 'radio'){
22640 inputblock.cn.push(hidden);
22644 inputblock.cn.push({
22646 cls : 'input-group-addon',
22652 var boxLabelCfg = false;
22658 //'for': id, // box label is handled by onclick - so no for...
22660 html: this.boxLabel
22663 boxLabelCfg.tooltip = this.tooltip;
22669 if (align ==='left' && this.fieldLabel.length) {
22670 // Roo.log("left and has label");
22675 cls : 'control-label',
22676 html : this.fieldLabel
22687 cfg.cn[1].cn.push(boxLabelCfg);
22690 if(this.labelWidth > 12){
22691 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22694 if(this.labelWidth < 13 && this.labelmd == 0){
22695 this.labelmd = this.labelWidth;
22698 if(this.labellg > 0){
22699 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22700 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22703 if(this.labelmd > 0){
22704 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22705 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22708 if(this.labelsm > 0){
22709 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22710 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22713 if(this.labelxs > 0){
22714 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22715 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22718 } else if ( this.fieldLabel.length) {
22719 // Roo.log(" label");
22723 tag: this.boxLabel ? 'span' : 'label',
22725 cls: 'control-label box-input-label',
22726 //cls : 'input-group-addon',
22727 html : this.fieldLabel
22734 cfg.cn.push(boxLabelCfg);
22739 // Roo.log(" no label && no align");
22740 cfg.cn = [ inputblock ] ;
22742 cfg.cn.push(boxLabelCfg);
22750 if(this.inputType != 'radio'){
22751 cfg.cn.push(hidden);
22759 * return the real input element.
22761 inputEl: function ()
22763 return this.el.select('input.roo-' + this.inputType,true).first();
22765 hiddenEl: function ()
22767 return this.el.select('input.roo-hidden-value',true).first();
22770 labelEl: function()
22772 return this.el.select('label.control-label',true).first();
22774 /* depricated... */
22778 return this.labelEl();
22781 boxLabelEl: function()
22783 return this.el.select('label.box-label',true).first();
22786 initEvents : function()
22788 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22790 this.inputEl().on('click', this.onClick, this);
22792 if (this.boxLabel) {
22793 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22796 this.startValue = this.getValue();
22799 Roo.bootstrap.CheckBox.register(this);
22803 onClick : function(e)
22805 if(this.fireEvent('click', this, e) !== false){
22806 this.setChecked(!this.checked);
22811 setChecked : function(state,suppressEvent)
22813 this.startValue = this.getValue();
22815 if(this.inputType == 'radio'){
22817 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22818 e.dom.checked = false;
22821 this.inputEl().dom.checked = true;
22823 this.inputEl().dom.value = this.inputValue;
22825 if(suppressEvent !== true){
22826 this.fireEvent('check', this, true);
22834 this.checked = state;
22836 this.inputEl().dom.checked = state;
22839 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22841 if(suppressEvent !== true){
22842 this.fireEvent('check', this, state);
22848 getValue : function()
22850 if(this.inputType == 'radio'){
22851 return this.getGroupValue();
22854 return this.hiddenEl().dom.value;
22858 getGroupValue : function()
22860 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22864 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22867 setValue : function(v,suppressEvent)
22869 if(this.inputType == 'radio'){
22870 this.setGroupValue(v, suppressEvent);
22874 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22879 setGroupValue : function(v, suppressEvent)
22881 this.startValue = this.getValue();
22883 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22884 e.dom.checked = false;
22886 if(e.dom.value == v){
22887 e.dom.checked = true;
22891 if(suppressEvent !== true){
22892 this.fireEvent('check', this, true);
22900 validate : function()
22902 if(this.getVisibilityEl().hasClass('hidden')){
22908 (this.inputType == 'radio' && this.validateRadio()) ||
22909 (this.inputType == 'checkbox' && this.validateCheckbox())
22915 this.markInvalid();
22919 validateRadio : function()
22921 if(this.getVisibilityEl().hasClass('hidden')){
22925 if(this.allowBlank){
22931 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22932 if(!e.dom.checked){
22944 validateCheckbox : function()
22947 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22948 //return (this.getValue() == this.inputValue) ? true : false;
22951 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22959 for(var i in group){
22960 if(group[i].el.isVisible(true)){
22968 for(var i in group){
22973 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22980 * Mark this field as valid
22982 markValid : function()
22986 this.fireEvent('valid', this);
22988 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22991 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22998 if(this.inputType == 'radio'){
22999 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23000 var fg = e.findParent('.form-group', false, true);
23001 if (Roo.bootstrap.version == 3) {
23002 fg.removeClass([_this.invalidClass, _this.validClass]);
23003 fg.addClass(_this.validClass);
23005 fg.removeClass(['is-valid', 'is-invalid']);
23006 fg.addClass('is-valid');
23014 var fg = this.el.findParent('.form-group', false, true);
23015 if (Roo.bootstrap.version == 3) {
23016 fg.removeClass([this.invalidClass, this.validClass]);
23017 fg.addClass(this.validClass);
23019 fg.removeClass(['is-valid', 'is-invalid']);
23020 fg.addClass('is-valid');
23025 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23031 for(var i in group){
23032 var fg = group[i].el.findParent('.form-group', false, true);
23033 if (Roo.bootstrap.version == 3) {
23034 fg.removeClass([this.invalidClass, this.validClass]);
23035 fg.addClass(this.validClass);
23037 fg.removeClass(['is-valid', 'is-invalid']);
23038 fg.addClass('is-valid');
23044 * Mark this field as invalid
23045 * @param {String} msg The validation message
23047 markInvalid : function(msg)
23049 if(this.allowBlank){
23055 this.fireEvent('invalid', this, msg);
23057 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23060 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23064 label.markInvalid();
23067 if(this.inputType == 'radio'){
23069 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23070 var fg = e.findParent('.form-group', false, true);
23071 if (Roo.bootstrap.version == 3) {
23072 fg.removeClass([_this.invalidClass, _this.validClass]);
23073 fg.addClass(_this.invalidClass);
23075 fg.removeClass(['is-invalid', 'is-valid']);
23076 fg.addClass('is-invalid');
23084 var fg = this.el.findParent('.form-group', false, true);
23085 if (Roo.bootstrap.version == 3) {
23086 fg.removeClass([_this.invalidClass, _this.validClass]);
23087 fg.addClass(_this.invalidClass);
23089 fg.removeClass(['is-invalid', 'is-valid']);
23090 fg.addClass('is-invalid');
23095 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23101 for(var i in group){
23102 var fg = group[i].el.findParent('.form-group', false, true);
23103 if (Roo.bootstrap.version == 3) {
23104 fg.removeClass([_this.invalidClass, _this.validClass]);
23105 fg.addClass(_this.invalidClass);
23107 fg.removeClass(['is-invalid', 'is-valid']);
23108 fg.addClass('is-invalid');
23114 clearInvalid : function()
23116 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23118 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23120 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23122 if (label && label.iconEl) {
23123 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23124 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23128 disable : function()
23130 if(this.inputType != 'radio'){
23131 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23138 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23139 _this.getActionEl().addClass(this.disabledClass);
23140 e.dom.disabled = true;
23144 this.disabled = true;
23145 this.fireEvent("disable", this);
23149 enable : function()
23151 if(this.inputType != 'radio'){
23152 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23159 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23160 _this.getActionEl().removeClass(this.disabledClass);
23161 e.dom.disabled = false;
23165 this.disabled = false;
23166 this.fireEvent("enable", this);
23170 setBoxLabel : function(v)
23175 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23181 Roo.apply(Roo.bootstrap.CheckBox, {
23186 * register a CheckBox Group
23187 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23189 register : function(checkbox)
23191 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23192 this.groups[checkbox.groupId] = {};
23195 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23199 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23203 * fetch a CheckBox Group based on the group ID
23204 * @param {string} the group ID
23205 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23207 get: function(groupId) {
23208 if (typeof(this.groups[groupId]) == 'undefined') {
23212 return this.groups[groupId] ;
23225 * @class Roo.bootstrap.Radio
23226 * @extends Roo.bootstrap.Component
23227 * Bootstrap Radio class
23228 * @cfg {String} boxLabel - the label associated
23229 * @cfg {String} value - the value of radio
23232 * Create a new Radio
23233 * @param {Object} config The config object
23235 Roo.bootstrap.Radio = function(config){
23236 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23240 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23246 getAutoCreate : function()
23250 cls : 'form-group radio',
23255 html : this.boxLabel
23263 initEvents : function()
23265 this.parent().register(this);
23267 this.el.on('click', this.onClick, this);
23271 onClick : function(e)
23273 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23274 this.setChecked(true);
23278 setChecked : function(state, suppressEvent)
23280 this.parent().setValue(this.value, suppressEvent);
23284 setBoxLabel : function(v)
23289 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23304 * @class Roo.bootstrap.SecurePass
23305 * @extends Roo.bootstrap.Input
23306 * Bootstrap SecurePass class
23310 * Create a new SecurePass
23311 * @param {Object} config The config object
23314 Roo.bootstrap.SecurePass = function (config) {
23315 // these go here, so the translation tool can replace them..
23317 PwdEmpty: "Please type a password, and then retype it to confirm.",
23318 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23319 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23320 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23321 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23322 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23323 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23324 TooWeak: "Your password is Too Weak."
23326 this.meterLabel = "Password strength:";
23327 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23328 this.meterClass = [
23329 "roo-password-meter-tooweak",
23330 "roo-password-meter-weak",
23331 "roo-password-meter-medium",
23332 "roo-password-meter-strong",
23333 "roo-password-meter-grey"
23338 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23341 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23343 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23345 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23346 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23347 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23348 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23349 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23350 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23351 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23361 * @cfg {String/Object} Label for the strength meter (defaults to
23362 * 'Password strength:')
23367 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23368 * ['Weak', 'Medium', 'Strong'])
23371 pwdStrengths: false,
23384 initEvents: function ()
23386 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23388 if (this.el.is('input[type=password]') && Roo.isSafari) {
23389 this.el.on('keydown', this.SafariOnKeyDown, this);
23392 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23395 onRender: function (ct, position)
23397 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23398 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23399 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23401 this.trigger.createChild({
23406 cls: 'roo-password-meter-grey col-xs-12',
23409 //width: this.meterWidth + 'px'
23413 cls: 'roo-password-meter-text'
23419 if (this.hideTrigger) {
23420 this.trigger.setDisplayed(false);
23422 this.setSize(this.width || '', this.height || '');
23425 onDestroy: function ()
23427 if (this.trigger) {
23428 this.trigger.removeAllListeners();
23429 this.trigger.remove();
23432 this.wrap.remove();
23434 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23437 checkStrength: function ()
23439 var pwd = this.inputEl().getValue();
23440 if (pwd == this._lastPwd) {
23445 if (this.ClientSideStrongPassword(pwd)) {
23447 } else if (this.ClientSideMediumPassword(pwd)) {
23449 } else if (this.ClientSideWeakPassword(pwd)) {
23455 Roo.log('strength1: ' + strength);
23457 //var pm = this.trigger.child('div/div/div').dom;
23458 var pm = this.trigger.child('div/div');
23459 pm.removeClass(this.meterClass);
23460 pm.addClass(this.meterClass[strength]);
23463 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23465 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23467 this._lastPwd = pwd;
23471 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23473 this._lastPwd = '';
23475 var pm = this.trigger.child('div/div');
23476 pm.removeClass(this.meterClass);
23477 pm.addClass('roo-password-meter-grey');
23480 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23483 this.inputEl().dom.type='password';
23486 validateValue: function (value)
23489 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23492 if (value.length == 0) {
23493 if (this.allowBlank) {
23494 this.clearInvalid();
23498 this.markInvalid(this.errors.PwdEmpty);
23499 this.errorMsg = this.errors.PwdEmpty;
23507 if ('[\x21-\x7e]*'.match(value)) {
23508 this.markInvalid(this.errors.PwdBadChar);
23509 this.errorMsg = this.errors.PwdBadChar;
23512 if (value.length < 6) {
23513 this.markInvalid(this.errors.PwdShort);
23514 this.errorMsg = this.errors.PwdShort;
23517 if (value.length > 16) {
23518 this.markInvalid(this.errors.PwdLong);
23519 this.errorMsg = this.errors.PwdLong;
23523 if (this.ClientSideStrongPassword(value)) {
23525 } else if (this.ClientSideMediumPassword(value)) {
23527 } else if (this.ClientSideWeakPassword(value)) {
23534 if (strength < 2) {
23535 //this.markInvalid(this.errors.TooWeak);
23536 this.errorMsg = this.errors.TooWeak;
23541 console.log('strength2: ' + strength);
23543 //var pm = this.trigger.child('div/div/div').dom;
23545 var pm = this.trigger.child('div/div');
23546 pm.removeClass(this.meterClass);
23547 pm.addClass(this.meterClass[strength]);
23549 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23551 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23553 this.errorMsg = '';
23557 CharacterSetChecks: function (type)
23560 this.fResult = false;
23563 isctype: function (character, type)
23566 case this.kCapitalLetter:
23567 if (character >= 'A' && character <= 'Z') {
23572 case this.kSmallLetter:
23573 if (character >= 'a' && character <= 'z') {
23579 if (character >= '0' && character <= '9') {
23584 case this.kPunctuation:
23585 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23596 IsLongEnough: function (pwd, size)
23598 return !(pwd == null || isNaN(size) || pwd.length < size);
23601 SpansEnoughCharacterSets: function (word, nb)
23603 if (!this.IsLongEnough(word, nb))
23608 var characterSetChecks = new Array(
23609 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23610 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23613 for (var index = 0; index < word.length; ++index) {
23614 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23615 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23616 characterSetChecks[nCharSet].fResult = true;
23623 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23624 if (characterSetChecks[nCharSet].fResult) {
23629 if (nCharSets < nb) {
23635 ClientSideStrongPassword: function (pwd)
23637 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23640 ClientSideMediumPassword: function (pwd)
23642 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23645 ClientSideWeakPassword: function (pwd)
23647 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23650 })//<script type="text/javascript">
23653 * Based Ext JS Library 1.1.1
23654 * Copyright(c) 2006-2007, Ext JS, LLC.
23660 * @class Roo.HtmlEditorCore
23661 * @extends Roo.Component
23662 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23664 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23667 Roo.HtmlEditorCore = function(config){
23670 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23675 * @event initialize
23676 * Fires when the editor is fully initialized (including the iframe)
23677 * @param {Roo.HtmlEditorCore} this
23682 * Fires when the editor is first receives the focus. Any insertion must wait
23683 * until after this event.
23684 * @param {Roo.HtmlEditorCore} this
23688 * @event beforesync
23689 * Fires before the textarea is updated with content from the editor iframe. Return false
23690 * to cancel the sync.
23691 * @param {Roo.HtmlEditorCore} this
23692 * @param {String} html
23696 * @event beforepush
23697 * Fires before the iframe editor is updated with content from the textarea. Return false
23698 * to cancel the push.
23699 * @param {Roo.HtmlEditorCore} this
23700 * @param {String} html
23705 * Fires when the textarea is updated with content from the editor iframe.
23706 * @param {Roo.HtmlEditorCore} this
23707 * @param {String} html
23712 * Fires when the iframe editor is updated with content from the textarea.
23713 * @param {Roo.HtmlEditorCore} this
23714 * @param {String} html
23719 * @event editorevent
23720 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23721 * @param {Roo.HtmlEditorCore} this
23727 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23729 // defaults : white / black...
23730 this.applyBlacklists();
23737 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23741 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23747 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23752 * @cfg {Number} height (in pixels)
23756 * @cfg {Number} width (in pixels)
23761 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23764 stylesheets: false,
23769 // private properties
23770 validationEvent : false,
23772 initialized : false,
23774 sourceEditMode : false,
23775 onFocus : Roo.emptyFn,
23777 hideMode:'offsets',
23781 // blacklist + whitelisted elements..
23788 * Protected method that will not generally be called directly. It
23789 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23790 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23792 getDocMarkup : function(){
23796 // inherit styels from page...??
23797 if (this.stylesheets === false) {
23799 Roo.get(document.head).select('style').each(function(node) {
23800 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23803 Roo.get(document.head).select('link').each(function(node) {
23804 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23807 } else if (!this.stylesheets.length) {
23809 st = '<style type="text/css">' +
23810 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23813 st = '<style type="text/css">' +
23818 st += '<style type="text/css">' +
23819 'IMG { cursor: pointer } ' +
23822 var cls = 'roo-htmleditor-body';
23824 if(this.bodyCls.length){
23825 cls += ' ' + this.bodyCls;
23828 return '<html><head>' + st +
23829 //<style type="text/css">' +
23830 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23832 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23836 onRender : function(ct, position)
23839 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23840 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23843 this.el.dom.style.border = '0 none';
23844 this.el.dom.setAttribute('tabIndex', -1);
23845 this.el.addClass('x-hidden hide');
23849 if(Roo.isIE){ // fix IE 1px bogus margin
23850 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23854 this.frameId = Roo.id();
23858 var iframe = this.owner.wrap.createChild({
23860 cls: 'form-control', // bootstrap..
23862 name: this.frameId,
23863 frameBorder : 'no',
23864 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23869 this.iframe = iframe.dom;
23871 this.assignDocWin();
23873 this.doc.designMode = 'on';
23876 this.doc.write(this.getDocMarkup());
23880 var task = { // must defer to wait for browser to be ready
23882 //console.log("run task?" + this.doc.readyState);
23883 this.assignDocWin();
23884 if(this.doc.body || this.doc.readyState == 'complete'){
23886 this.doc.designMode="on";
23890 Roo.TaskMgr.stop(task);
23891 this.initEditor.defer(10, this);
23898 Roo.TaskMgr.start(task);
23903 onResize : function(w, h)
23905 Roo.log('resize: ' +w + ',' + h );
23906 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23910 if(typeof w == 'number'){
23912 this.iframe.style.width = w + 'px';
23914 if(typeof h == 'number'){
23916 this.iframe.style.height = h + 'px';
23918 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23925 * Toggles the editor between standard and source edit mode.
23926 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23928 toggleSourceEdit : function(sourceEditMode){
23930 this.sourceEditMode = sourceEditMode === true;
23932 if(this.sourceEditMode){
23934 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23937 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23938 //this.iframe.className = '';
23941 //this.setSize(this.owner.wrap.getSize());
23942 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23949 * Protected method that will not generally be called directly. If you need/want
23950 * custom HTML cleanup, this is the method you should override.
23951 * @param {String} html The HTML to be cleaned
23952 * return {String} The cleaned HTML
23954 cleanHtml : function(html){
23955 html = String(html);
23956 if(html.length > 5){
23957 if(Roo.isSafari){ // strip safari nonsense
23958 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23961 if(html == ' '){
23968 * HTML Editor -> Textarea
23969 * Protected method that will not generally be called directly. Syncs the contents
23970 * of the editor iframe with the textarea.
23972 syncValue : function(){
23973 if(this.initialized){
23974 var bd = (this.doc.body || this.doc.documentElement);
23975 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23976 var html = bd.innerHTML;
23978 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23979 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23981 html = '<div style="'+m[0]+'">' + html + '</div>';
23984 html = this.cleanHtml(html);
23985 // fix up the special chars.. normaly like back quotes in word...
23986 // however we do not want to do this with chinese..
23987 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23989 var cc = match.charCodeAt();
23991 // Get the character value, handling surrogate pairs
23992 if (match.length == 2) {
23993 // It's a surrogate pair, calculate the Unicode code point
23994 var high = match.charCodeAt(0) - 0xD800;
23995 var low = match.charCodeAt(1) - 0xDC00;
23996 cc = (high * 0x400) + low + 0x10000;
23998 (cc >= 0x4E00 && cc < 0xA000 ) ||
23999 (cc >= 0x3400 && cc < 0x4E00 ) ||
24000 (cc >= 0xf900 && cc < 0xfb00 )
24005 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24006 return "&#" + cc + ";";
24013 if(this.owner.fireEvent('beforesync', this, html) !== false){
24014 this.el.dom.value = html;
24015 this.owner.fireEvent('sync', this, html);
24021 * Protected method that will not generally be called directly. Pushes the value of the textarea
24022 * into the iframe editor.
24024 pushValue : function(){
24025 if(this.initialized){
24026 var v = this.el.dom.value.trim();
24028 // if(v.length < 1){
24032 if(this.owner.fireEvent('beforepush', this, v) !== false){
24033 var d = (this.doc.body || this.doc.documentElement);
24035 this.cleanUpPaste();
24036 this.el.dom.value = d.innerHTML;
24037 this.owner.fireEvent('push', this, v);
24043 deferFocus : function(){
24044 this.focus.defer(10, this);
24048 focus : function(){
24049 if(this.win && !this.sourceEditMode){
24056 assignDocWin: function()
24058 var iframe = this.iframe;
24061 this.doc = iframe.contentWindow.document;
24062 this.win = iframe.contentWindow;
24064 // if (!Roo.get(this.frameId)) {
24067 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24068 // this.win = Roo.get(this.frameId).dom.contentWindow;
24070 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24074 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24075 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24080 initEditor : function(){
24081 //console.log("INIT EDITOR");
24082 this.assignDocWin();
24086 this.doc.designMode="on";
24088 this.doc.write(this.getDocMarkup());
24091 var dbody = (this.doc.body || this.doc.documentElement);
24092 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24093 // this copies styles from the containing element into thsi one..
24094 // not sure why we need all of this..
24095 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24097 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24098 //ss['background-attachment'] = 'fixed'; // w3c
24099 dbody.bgProperties = 'fixed'; // ie
24100 //Roo.DomHelper.applyStyles(dbody, ss);
24101 Roo.EventManager.on(this.doc, {
24102 //'mousedown': this.onEditorEvent,
24103 'mouseup': this.onEditorEvent,
24104 'dblclick': this.onEditorEvent,
24105 'click': this.onEditorEvent,
24106 'keyup': this.onEditorEvent,
24111 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24113 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24114 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24116 this.initialized = true;
24118 this.owner.fireEvent('initialize', this);
24123 onDestroy : function(){
24129 //for (var i =0; i < this.toolbars.length;i++) {
24130 // // fixme - ask toolbars for heights?
24131 // this.toolbars[i].onDestroy();
24134 //this.wrap.dom.innerHTML = '';
24135 //this.wrap.remove();
24140 onFirstFocus : function(){
24142 this.assignDocWin();
24145 this.activated = true;
24148 if(Roo.isGecko){ // prevent silly gecko errors
24150 var s = this.win.getSelection();
24151 if(!s.focusNode || s.focusNode.nodeType != 3){
24152 var r = s.getRangeAt(0);
24153 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24158 this.execCmd('useCSS', true);
24159 this.execCmd('styleWithCSS', false);
24162 this.owner.fireEvent('activate', this);
24166 adjustFont: function(btn){
24167 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24168 //if(Roo.isSafari){ // safari
24171 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24172 if(Roo.isSafari){ // safari
24173 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24174 v = (v < 10) ? 10 : v;
24175 v = (v > 48) ? 48 : v;
24176 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24181 v = Math.max(1, v+adjust);
24183 this.execCmd('FontSize', v );
24186 onEditorEvent : function(e)
24188 this.owner.fireEvent('editorevent', this, e);
24189 // this.updateToolbar();
24190 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24193 insertTag : function(tg)
24195 // could be a bit smarter... -> wrap the current selected tRoo..
24196 if (tg.toLowerCase() == 'span' ||
24197 tg.toLowerCase() == 'code' ||
24198 tg.toLowerCase() == 'sup' ||
24199 tg.toLowerCase() == 'sub'
24202 range = this.createRange(this.getSelection());
24203 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24204 wrappingNode.appendChild(range.extractContents());
24205 range.insertNode(wrappingNode);
24212 this.execCmd("formatblock", tg);
24216 insertText : function(txt)
24220 var range = this.createRange();
24221 range.deleteContents();
24222 //alert(Sender.getAttribute('label'));
24224 range.insertNode(this.doc.createTextNode(txt));
24230 * Executes a Midas editor command on the editor document and performs necessary focus and
24231 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24232 * @param {String} cmd The Midas command
24233 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24235 relayCmd : function(cmd, value){
24237 this.execCmd(cmd, value);
24238 this.owner.fireEvent('editorevent', this);
24239 //this.updateToolbar();
24240 this.owner.deferFocus();
24244 * Executes a Midas editor command directly on the editor document.
24245 * For visual commands, you should use {@link #relayCmd} instead.
24246 * <b>This should only be called after the editor is initialized.</b>
24247 * @param {String} cmd The Midas command
24248 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24250 execCmd : function(cmd, value){
24251 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24258 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24260 * @param {String} text | dom node..
24262 insertAtCursor : function(text)
24265 if(!this.activated){
24271 var r = this.doc.selection.createRange();
24282 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24286 // from jquery ui (MIT licenced)
24288 var win = this.win;
24290 if (win.getSelection && win.getSelection().getRangeAt) {
24291 range = win.getSelection().getRangeAt(0);
24292 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24293 range.insertNode(node);
24294 } else if (win.document.selection && win.document.selection.createRange) {
24295 // no firefox support
24296 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24297 win.document.selection.createRange().pasteHTML(txt);
24299 // no firefox support
24300 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24301 this.execCmd('InsertHTML', txt);
24310 mozKeyPress : function(e){
24312 var c = e.getCharCode(), cmd;
24315 c = String.fromCharCode(c).toLowerCase();
24329 this.cleanUpPaste.defer(100, this);
24337 e.preventDefault();
24345 fixKeys : function(){ // load time branching for fastest keydown performance
24347 return function(e){
24348 var k = e.getKey(), r;
24351 r = this.doc.selection.createRange();
24354 r.pasteHTML('    ');
24361 r = this.doc.selection.createRange();
24363 var target = r.parentElement();
24364 if(!target || target.tagName.toLowerCase() != 'li'){
24366 r.pasteHTML('<br />');
24372 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24373 this.cleanUpPaste.defer(100, this);
24379 }else if(Roo.isOpera){
24380 return function(e){
24381 var k = e.getKey();
24385 this.execCmd('InsertHTML','    ');
24388 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24389 this.cleanUpPaste.defer(100, this);
24394 }else if(Roo.isSafari){
24395 return function(e){
24396 var k = e.getKey();
24400 this.execCmd('InsertText','\t');
24404 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24405 this.cleanUpPaste.defer(100, this);
24413 getAllAncestors: function()
24415 var p = this.getSelectedNode();
24418 a.push(p); // push blank onto stack..
24419 p = this.getParentElement();
24423 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24427 a.push(this.doc.body);
24431 lastSelNode : false,
24434 getSelection : function()
24436 this.assignDocWin();
24437 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24440 getSelectedNode: function()
24442 // this may only work on Gecko!!!
24444 // should we cache this!!!!
24449 var range = this.createRange(this.getSelection()).cloneRange();
24452 var parent = range.parentElement();
24454 var testRange = range.duplicate();
24455 testRange.moveToElementText(parent);
24456 if (testRange.inRange(range)) {
24459 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24462 parent = parent.parentElement;
24467 // is ancestor a text element.
24468 var ac = range.commonAncestorContainer;
24469 if (ac.nodeType == 3) {
24470 ac = ac.parentNode;
24473 var ar = ac.childNodes;
24476 var other_nodes = [];
24477 var has_other_nodes = false;
24478 for (var i=0;i<ar.length;i++) {
24479 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24482 // fullly contained node.
24484 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24489 // probably selected..
24490 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24491 other_nodes.push(ar[i]);
24495 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24500 has_other_nodes = true;
24502 if (!nodes.length && other_nodes.length) {
24503 nodes= other_nodes;
24505 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24511 createRange: function(sel)
24513 // this has strange effects when using with
24514 // top toolbar - not sure if it's a great idea.
24515 //this.editor.contentWindow.focus();
24516 if (typeof sel != "undefined") {
24518 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24520 return this.doc.createRange();
24523 return this.doc.createRange();
24526 getParentElement: function()
24529 this.assignDocWin();
24530 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24532 var range = this.createRange(sel);
24535 var p = range.commonAncestorContainer;
24536 while (p.nodeType == 3) { // text node
24547 * Range intersection.. the hard stuff...
24551 * [ -- selected range --- ]
24555 * if end is before start or hits it. fail.
24556 * if start is after end or hits it fail.
24558 * if either hits (but other is outside. - then it's not
24564 // @see http://www.thismuchiknow.co.uk/?p=64.
24565 rangeIntersectsNode : function(range, node)
24567 var nodeRange = node.ownerDocument.createRange();
24569 nodeRange.selectNode(node);
24571 nodeRange.selectNodeContents(node);
24574 var rangeStartRange = range.cloneRange();
24575 rangeStartRange.collapse(true);
24577 var rangeEndRange = range.cloneRange();
24578 rangeEndRange.collapse(false);
24580 var nodeStartRange = nodeRange.cloneRange();
24581 nodeStartRange.collapse(true);
24583 var nodeEndRange = nodeRange.cloneRange();
24584 nodeEndRange.collapse(false);
24586 return rangeStartRange.compareBoundaryPoints(
24587 Range.START_TO_START, nodeEndRange) == -1 &&
24588 rangeEndRange.compareBoundaryPoints(
24589 Range.START_TO_START, nodeStartRange) == 1;
24593 rangeCompareNode : function(range, node)
24595 var nodeRange = node.ownerDocument.createRange();
24597 nodeRange.selectNode(node);
24599 nodeRange.selectNodeContents(node);
24603 range.collapse(true);
24605 nodeRange.collapse(true);
24607 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24608 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24610 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24612 var nodeIsBefore = ss == 1;
24613 var nodeIsAfter = ee == -1;
24615 if (nodeIsBefore && nodeIsAfter) {
24618 if (!nodeIsBefore && nodeIsAfter) {
24619 return 1; //right trailed.
24622 if (nodeIsBefore && !nodeIsAfter) {
24623 return 2; // left trailed.
24629 // private? - in a new class?
24630 cleanUpPaste : function()
24632 // cleans up the whole document..
24633 Roo.log('cleanuppaste');
24635 this.cleanUpChildren(this.doc.body);
24636 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24637 if (clean != this.doc.body.innerHTML) {
24638 this.doc.body.innerHTML = clean;
24643 cleanWordChars : function(input) {// change the chars to hex code
24644 var he = Roo.HtmlEditorCore;
24646 var output = input;
24647 Roo.each(he.swapCodes, function(sw) {
24648 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24650 output = output.replace(swapper, sw[1]);
24657 cleanUpChildren : function (n)
24659 if (!n.childNodes.length) {
24662 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24663 this.cleanUpChild(n.childNodes[i]);
24670 cleanUpChild : function (node)
24673 //console.log(node);
24674 if (node.nodeName == "#text") {
24675 // clean up silly Windows -- stuff?
24678 if (node.nodeName == "#comment") {
24679 node.parentNode.removeChild(node);
24680 // clean up silly Windows -- stuff?
24683 var lcname = node.tagName.toLowerCase();
24684 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24685 // whitelist of tags..
24687 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24689 node.parentNode.removeChild(node);
24694 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24696 // spans with no attributes - just remove them..
24697 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24698 remove_keep_children = true;
24701 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24702 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24704 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24705 // remove_keep_children = true;
24708 if (remove_keep_children) {
24709 this.cleanUpChildren(node);
24710 // inserts everything just before this node...
24711 while (node.childNodes.length) {
24712 var cn = node.childNodes[0];
24713 node.removeChild(cn);
24714 node.parentNode.insertBefore(cn, node);
24716 node.parentNode.removeChild(node);
24720 if (!node.attributes || !node.attributes.length) {
24725 this.cleanUpChildren(node);
24729 function cleanAttr(n,v)
24732 if (v.match(/^\./) || v.match(/^\//)) {
24735 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24738 if (v.match(/^#/)) {
24741 if (v.match(/^\{/)) { // allow template editing.
24744 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24745 node.removeAttribute(n);
24749 var cwhite = this.cwhite;
24750 var cblack = this.cblack;
24752 function cleanStyle(n,v)
24754 if (v.match(/expression/)) { //XSS?? should we even bother..
24755 node.removeAttribute(n);
24759 var parts = v.split(/;/);
24762 Roo.each(parts, function(p) {
24763 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24767 var l = p.split(':').shift().replace(/\s+/g,'');
24768 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24770 if ( cwhite.length && cblack.indexOf(l) > -1) {
24771 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24772 //node.removeAttribute(n);
24776 // only allow 'c whitelisted system attributes'
24777 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24778 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24779 //node.removeAttribute(n);
24789 if (clean.length) {
24790 node.setAttribute(n, clean.join(';'));
24792 node.removeAttribute(n);
24798 for (var i = node.attributes.length-1; i > -1 ; i--) {
24799 var a = node.attributes[i];
24802 if (a.name.toLowerCase().substr(0,2)=='on') {
24803 node.removeAttribute(a.name);
24806 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24807 node.removeAttribute(a.name);
24810 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24811 cleanAttr(a.name,a.value); // fixme..
24814 if (a.name == 'style') {
24815 cleanStyle(a.name,a.value);
24818 /// clean up MS crap..
24819 // tecnically this should be a list of valid class'es..
24822 if (a.name == 'class') {
24823 if (a.value.match(/^Mso/)) {
24824 node.removeAttribute('class');
24827 if (a.value.match(/^body$/)) {
24828 node.removeAttribute('class');
24839 this.cleanUpChildren(node);
24845 * Clean up MS wordisms...
24847 cleanWord : function(node)
24850 this.cleanWord(this.doc.body);
24855 node.nodeName == 'SPAN' &&
24856 !node.hasAttributes() &&
24857 node.childNodes.length == 1 &&
24858 node.firstChild.nodeName == "#text"
24860 var textNode = node.firstChild;
24861 node.removeChild(textNode);
24862 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24863 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24865 node.parentNode.insertBefore(textNode, node);
24866 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24867 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24869 node.parentNode.removeChild(node);
24872 if (node.nodeName == "#text") {
24873 // clean up silly Windows -- stuff?
24876 if (node.nodeName == "#comment") {
24877 node.parentNode.removeChild(node);
24878 // clean up silly Windows -- stuff?
24882 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24883 node.parentNode.removeChild(node);
24886 //Roo.log(node.tagName);
24887 // remove - but keep children..
24888 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24889 //Roo.log('-- removed');
24890 while (node.childNodes.length) {
24891 var cn = node.childNodes[0];
24892 node.removeChild(cn);
24893 node.parentNode.insertBefore(cn, node);
24894 // move node to parent - and clean it..
24895 this.cleanWord(cn);
24897 node.parentNode.removeChild(node);
24898 /// no need to iterate chidlren = it's got none..
24899 //this.iterateChildren(node, this.cleanWord);
24903 if (node.className.length) {
24905 var cn = node.className.split(/\W+/);
24907 Roo.each(cn, function(cls) {
24908 if (cls.match(/Mso[a-zA-Z]+/)) {
24913 node.className = cna.length ? cna.join(' ') : '';
24915 node.removeAttribute("class");
24919 if (node.hasAttribute("lang")) {
24920 node.removeAttribute("lang");
24923 if (node.hasAttribute("style")) {
24925 var styles = node.getAttribute("style").split(";");
24927 Roo.each(styles, function(s) {
24928 if (!s.match(/:/)) {
24931 var kv = s.split(":");
24932 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24935 // what ever is left... we allow.
24938 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24939 if (!nstyle.length) {
24940 node.removeAttribute('style');
24943 this.iterateChildren(node, this.cleanWord);
24949 * iterateChildren of a Node, calling fn each time, using this as the scole..
24950 * @param {DomNode} node node to iterate children of.
24951 * @param {Function} fn method of this class to call on each item.
24953 iterateChildren : function(node, fn)
24955 if (!node.childNodes.length) {
24958 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24959 fn.call(this, node.childNodes[i])
24965 * cleanTableWidths.
24967 * Quite often pasting from word etc.. results in tables with column and widths.
24968 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24971 cleanTableWidths : function(node)
24976 this.cleanTableWidths(this.doc.body);
24981 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24984 Roo.log(node.tagName);
24985 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24986 this.iterateChildren(node, this.cleanTableWidths);
24989 if (node.hasAttribute('width')) {
24990 node.removeAttribute('width');
24994 if (node.hasAttribute("style")) {
24997 var styles = node.getAttribute("style").split(";");
24999 Roo.each(styles, function(s) {
25000 if (!s.match(/:/)) {
25003 var kv = s.split(":");
25004 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25007 // what ever is left... we allow.
25010 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25011 if (!nstyle.length) {
25012 node.removeAttribute('style');
25016 this.iterateChildren(node, this.cleanTableWidths);
25024 domToHTML : function(currentElement, depth, nopadtext) {
25026 depth = depth || 0;
25027 nopadtext = nopadtext || false;
25029 if (!currentElement) {
25030 return this.domToHTML(this.doc.body);
25033 //Roo.log(currentElement);
25035 var allText = false;
25036 var nodeName = currentElement.nodeName;
25037 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25039 if (nodeName == '#text') {
25041 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25046 if (nodeName != 'BODY') {
25049 // Prints the node tagName, such as <A>, <IMG>, etc
25052 for(i = 0; i < currentElement.attributes.length;i++) {
25054 var aname = currentElement.attributes.item(i).name;
25055 if (!currentElement.attributes.item(i).value.length) {
25058 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25061 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25070 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25073 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25078 // Traverse the tree
25080 var currentElementChild = currentElement.childNodes.item(i);
25081 var allText = true;
25082 var innerHTML = '';
25084 while (currentElementChild) {
25085 // Formatting code (indent the tree so it looks nice on the screen)
25086 var nopad = nopadtext;
25087 if (lastnode == 'SPAN') {
25091 if (currentElementChild.nodeName == '#text') {
25092 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25093 toadd = nopadtext ? toadd : toadd.trim();
25094 if (!nopad && toadd.length > 80) {
25095 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25097 innerHTML += toadd;
25100 currentElementChild = currentElement.childNodes.item(i);
25106 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25108 // Recursively traverse the tree structure of the child node
25109 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25110 lastnode = currentElementChild.nodeName;
25112 currentElementChild=currentElement.childNodes.item(i);
25118 // The remaining code is mostly for formatting the tree
25119 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25124 ret+= "</"+tagName+">";
25130 applyBlacklists : function()
25132 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25133 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25137 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25138 if (b.indexOf(tag) > -1) {
25141 this.white.push(tag);
25145 Roo.each(w, function(tag) {
25146 if (b.indexOf(tag) > -1) {
25149 if (this.white.indexOf(tag) > -1) {
25152 this.white.push(tag);
25157 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25158 if (w.indexOf(tag) > -1) {
25161 this.black.push(tag);
25165 Roo.each(b, function(tag) {
25166 if (w.indexOf(tag) > -1) {
25169 if (this.black.indexOf(tag) > -1) {
25172 this.black.push(tag);
25177 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25178 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25182 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25183 if (b.indexOf(tag) > -1) {
25186 this.cwhite.push(tag);
25190 Roo.each(w, function(tag) {
25191 if (b.indexOf(tag) > -1) {
25194 if (this.cwhite.indexOf(tag) > -1) {
25197 this.cwhite.push(tag);
25202 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25203 if (w.indexOf(tag) > -1) {
25206 this.cblack.push(tag);
25210 Roo.each(b, function(tag) {
25211 if (w.indexOf(tag) > -1) {
25214 if (this.cblack.indexOf(tag) > -1) {
25217 this.cblack.push(tag);
25222 setStylesheets : function(stylesheets)
25224 if(typeof(stylesheets) == 'string'){
25225 Roo.get(this.iframe.contentDocument.head).createChild({
25227 rel : 'stylesheet',
25236 Roo.each(stylesheets, function(s) {
25241 Roo.get(_this.iframe.contentDocument.head).createChild({
25243 rel : 'stylesheet',
25252 removeStylesheets : function()
25256 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25261 setStyle : function(style)
25263 Roo.get(this.iframe.contentDocument.head).createChild({
25272 // hide stuff that is not compatible
25286 * @event specialkey
25290 * @cfg {String} fieldClass @hide
25293 * @cfg {String} focusClass @hide
25296 * @cfg {String} autoCreate @hide
25299 * @cfg {String} inputType @hide
25302 * @cfg {String} invalidClass @hide
25305 * @cfg {String} invalidText @hide
25308 * @cfg {String} msgFx @hide
25311 * @cfg {String} validateOnBlur @hide
25315 Roo.HtmlEditorCore.white = [
25316 'area', 'br', 'img', 'input', 'hr', 'wbr',
25318 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25319 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25320 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25321 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25322 'table', 'ul', 'xmp',
25324 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25327 'dir', 'menu', 'ol', 'ul', 'dl',
25333 Roo.HtmlEditorCore.black = [
25334 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25336 'base', 'basefont', 'bgsound', 'blink', 'body',
25337 'frame', 'frameset', 'head', 'html', 'ilayer',
25338 'iframe', 'layer', 'link', 'meta', 'object',
25339 'script', 'style' ,'title', 'xml' // clean later..
25341 Roo.HtmlEditorCore.clean = [
25342 'script', 'style', 'title', 'xml'
25344 Roo.HtmlEditorCore.remove = [
25349 Roo.HtmlEditorCore.ablack = [
25353 Roo.HtmlEditorCore.aclean = [
25354 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25358 Roo.HtmlEditorCore.pwhite= [
25359 'http', 'https', 'mailto'
25362 // white listed style attributes.
25363 Roo.HtmlEditorCore.cwhite= [
25364 // 'text-align', /// default is to allow most things..
25370 // black listed style attributes.
25371 Roo.HtmlEditorCore.cblack= [
25372 // 'font-size' -- this can be set by the project
25376 Roo.HtmlEditorCore.swapCodes =[
25395 * @class Roo.bootstrap.HtmlEditor
25396 * @extends Roo.bootstrap.TextArea
25397 * Bootstrap HtmlEditor class
25400 * Create a new HtmlEditor
25401 * @param {Object} config The config object
25404 Roo.bootstrap.HtmlEditor = function(config){
25405 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25406 if (!this.toolbars) {
25407 this.toolbars = [];
25410 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25413 * @event initialize
25414 * Fires when the editor is fully initialized (including the iframe)
25415 * @param {HtmlEditor} this
25420 * Fires when the editor is first receives the focus. Any insertion must wait
25421 * until after this event.
25422 * @param {HtmlEditor} this
25426 * @event beforesync
25427 * Fires before the textarea is updated with content from the editor iframe. Return false
25428 * to cancel the sync.
25429 * @param {HtmlEditor} this
25430 * @param {String} html
25434 * @event beforepush
25435 * Fires before the iframe editor is updated with content from the textarea. Return false
25436 * to cancel the push.
25437 * @param {HtmlEditor} this
25438 * @param {String} html
25443 * Fires when the textarea is updated with content from the editor iframe.
25444 * @param {HtmlEditor} this
25445 * @param {String} html
25450 * Fires when the iframe editor is updated with content from the textarea.
25451 * @param {HtmlEditor} this
25452 * @param {String} html
25456 * @event editmodechange
25457 * Fires when the editor switches edit modes
25458 * @param {HtmlEditor} this
25459 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25461 editmodechange: true,
25463 * @event editorevent
25464 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25465 * @param {HtmlEditor} this
25469 * @event firstfocus
25470 * Fires when on first focus - needed by toolbars..
25471 * @param {HtmlEditor} this
25476 * Auto save the htmlEditor value as a file into Events
25477 * @param {HtmlEditor} this
25481 * @event savedpreview
25482 * preview the saved version of htmlEditor
25483 * @param {HtmlEditor} this
25490 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25494 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25499 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25504 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25509 * @cfg {Number} height (in pixels)
25513 * @cfg {Number} width (in pixels)
25518 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25521 stylesheets: false,
25526 // private properties
25527 validationEvent : false,
25529 initialized : false,
25532 onFocus : Roo.emptyFn,
25534 hideMode:'offsets',
25536 tbContainer : false,
25540 toolbarContainer :function() {
25541 return this.wrap.select('.x-html-editor-tb',true).first();
25545 * Protected method that will not generally be called directly. It
25546 * is called when the editor creates its toolbar. Override this method if you need to
25547 * add custom toolbar buttons.
25548 * @param {HtmlEditor} editor
25550 createToolbar : function(){
25551 Roo.log('renewing');
25552 Roo.log("create toolbars");
25554 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25555 this.toolbars[0].render(this.toolbarContainer());
25559 // if (!editor.toolbars || !editor.toolbars.length) {
25560 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25563 // for (var i =0 ; i < editor.toolbars.length;i++) {
25564 // editor.toolbars[i] = Roo.factory(
25565 // typeof(editor.toolbars[i]) == 'string' ?
25566 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25567 // Roo.bootstrap.HtmlEditor);
25568 // editor.toolbars[i].init(editor);
25574 onRender : function(ct, position)
25576 // Roo.log("Call onRender: " + this.xtype);
25578 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25580 this.wrap = this.inputEl().wrap({
25581 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25584 this.editorcore.onRender(ct, position);
25586 if (this.resizable) {
25587 this.resizeEl = new Roo.Resizable(this.wrap, {
25591 minHeight : this.height,
25592 height: this.height,
25593 handles : this.resizable,
25596 resize : function(r, w, h) {
25597 _t.onResize(w,h); // -something
25603 this.createToolbar(this);
25606 if(!this.width && this.resizable){
25607 this.setSize(this.wrap.getSize());
25609 if (this.resizeEl) {
25610 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25611 // should trigger onReize..
25617 onResize : function(w, h)
25619 Roo.log('resize: ' +w + ',' + h );
25620 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25624 if(this.inputEl() ){
25625 if(typeof w == 'number'){
25626 var aw = w - this.wrap.getFrameWidth('lr');
25627 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25630 if(typeof h == 'number'){
25631 var tbh = -11; // fixme it needs to tool bar size!
25632 for (var i =0; i < this.toolbars.length;i++) {
25633 // fixme - ask toolbars for heights?
25634 tbh += this.toolbars[i].el.getHeight();
25635 //if (this.toolbars[i].footer) {
25636 // tbh += this.toolbars[i].footer.el.getHeight();
25644 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25645 ah -= 5; // knock a few pixes off for look..
25646 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25650 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25651 this.editorcore.onResize(ew,eh);
25656 * Toggles the editor between standard and source edit mode.
25657 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25659 toggleSourceEdit : function(sourceEditMode)
25661 this.editorcore.toggleSourceEdit(sourceEditMode);
25663 if(this.editorcore.sourceEditMode){
25664 Roo.log('editor - showing textarea');
25667 // Roo.log(this.syncValue());
25669 this.inputEl().removeClass(['hide', 'x-hidden']);
25670 this.inputEl().dom.removeAttribute('tabIndex');
25671 this.inputEl().focus();
25673 Roo.log('editor - hiding textarea');
25675 // Roo.log(this.pushValue());
25678 this.inputEl().addClass(['hide', 'x-hidden']);
25679 this.inputEl().dom.setAttribute('tabIndex', -1);
25680 //this.deferFocus();
25683 if(this.resizable){
25684 this.setSize(this.wrap.getSize());
25687 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25690 // private (for BoxComponent)
25691 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25693 // private (for BoxComponent)
25694 getResizeEl : function(){
25698 // private (for BoxComponent)
25699 getPositionEl : function(){
25704 initEvents : function(){
25705 this.originalValue = this.getValue();
25709 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25712 // markInvalid : Roo.emptyFn,
25714 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25717 // clearInvalid : Roo.emptyFn,
25719 setValue : function(v){
25720 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25721 this.editorcore.pushValue();
25726 deferFocus : function(){
25727 this.focus.defer(10, this);
25731 focus : function(){
25732 this.editorcore.focus();
25738 onDestroy : function(){
25744 for (var i =0; i < this.toolbars.length;i++) {
25745 // fixme - ask toolbars for heights?
25746 this.toolbars[i].onDestroy();
25749 this.wrap.dom.innerHTML = '';
25750 this.wrap.remove();
25755 onFirstFocus : function(){
25756 //Roo.log("onFirstFocus");
25757 this.editorcore.onFirstFocus();
25758 for (var i =0; i < this.toolbars.length;i++) {
25759 this.toolbars[i].onFirstFocus();
25765 syncValue : function()
25767 this.editorcore.syncValue();
25770 pushValue : function()
25772 this.editorcore.pushValue();
25776 // hide stuff that is not compatible
25790 * @event specialkey
25794 * @cfg {String} fieldClass @hide
25797 * @cfg {String} focusClass @hide
25800 * @cfg {String} autoCreate @hide
25803 * @cfg {String} inputType @hide
25807 * @cfg {String} invalidText @hide
25810 * @cfg {String} msgFx @hide
25813 * @cfg {String} validateOnBlur @hide
25822 Roo.namespace('Roo.bootstrap.htmleditor');
25824 * @class Roo.bootstrap.HtmlEditorToolbar1
25830 new Roo.bootstrap.HtmlEditor({
25833 new Roo.bootstrap.HtmlEditorToolbar1({
25834 disable : { fonts: 1 , format: 1, ..., ... , ...],
25840 * @cfg {Object} disable List of elements to disable..
25841 * @cfg {Array} btns List of additional buttons.
25845 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25848 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25851 Roo.apply(this, config);
25853 // default disabled, based on 'good practice'..
25854 this.disable = this.disable || {};
25855 Roo.applyIf(this.disable, {
25858 specialElements : true
25860 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25862 this.editor = config.editor;
25863 this.editorcore = config.editor.editorcore;
25865 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25867 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25868 // dont call parent... till later.
25870 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25875 editorcore : false,
25880 "h1","h2","h3","h4","h5","h6",
25882 "abbr", "acronym", "address", "cite", "samp", "var",
25886 onRender : function(ct, position)
25888 // Roo.log("Call onRender: " + this.xtype);
25890 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25892 this.el.dom.style.marginBottom = '0';
25894 var editorcore = this.editorcore;
25895 var editor= this.editor;
25898 var btn = function(id,cmd , toggle, handler, html){
25900 var event = toggle ? 'toggle' : 'click';
25905 xns: Roo.bootstrap,
25909 enableToggle:toggle !== false,
25911 pressed : toggle ? false : null,
25914 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25915 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25921 // var cb_box = function...
25926 xns: Roo.bootstrap,
25931 xns: Roo.bootstrap,
25935 Roo.each(this.formats, function(f) {
25936 style.menu.items.push({
25938 xns: Roo.bootstrap,
25939 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25944 editorcore.insertTag(this.tagname);
25951 children.push(style);
25953 btn('bold',false,true);
25954 btn('italic',false,true);
25955 btn('align-left', 'justifyleft',true);
25956 btn('align-center', 'justifycenter',true);
25957 btn('align-right' , 'justifyright',true);
25958 btn('link', false, false, function(btn) {
25959 //Roo.log("create link?");
25960 var url = prompt(this.createLinkText, this.defaultLinkValue);
25961 if(url && url != 'http:/'+'/'){
25962 this.editorcore.relayCmd('createlink', url);
25965 btn('list','insertunorderedlist',true);
25966 btn('pencil', false,true, function(btn){
25968 this.toggleSourceEdit(btn.pressed);
25971 if (this.editor.btns.length > 0) {
25972 for (var i = 0; i<this.editor.btns.length; i++) {
25973 children.push(this.editor.btns[i]);
25981 xns: Roo.bootstrap,
25986 xns: Roo.bootstrap,
25991 cog.menu.items.push({
25993 xns: Roo.bootstrap,
25994 html : Clean styles,
25999 editorcore.insertTag(this.tagname);
26008 this.xtype = 'NavSimplebar';
26010 for(var i=0;i< children.length;i++) {
26012 this.buttons.add(this.addxtypeChild(children[i]));
26016 editor.on('editorevent', this.updateToolbar, this);
26018 onBtnClick : function(id)
26020 this.editorcore.relayCmd(id);
26021 this.editorcore.focus();
26025 * Protected method that will not generally be called directly. It triggers
26026 * a toolbar update by reading the markup state of the current selection in the editor.
26028 updateToolbar: function(){
26030 if(!this.editorcore.activated){
26031 this.editor.onFirstFocus(); // is this neeed?
26035 var btns = this.buttons;
26036 var doc = this.editorcore.doc;
26037 btns.get('bold').setActive(doc.queryCommandState('bold'));
26038 btns.get('italic').setActive(doc.queryCommandState('italic'));
26039 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26041 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26042 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26043 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26045 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26046 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26049 var ans = this.editorcore.getAllAncestors();
26050 if (this.formatCombo) {
26053 var store = this.formatCombo.store;
26054 this.formatCombo.setValue("");
26055 for (var i =0; i < ans.length;i++) {
26056 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26058 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26066 // hides menus... - so this cant be on a menu...
26067 Roo.bootstrap.MenuMgr.hideAll();
26069 Roo.bootstrap.MenuMgr.hideAll();
26070 //this.editorsyncValue();
26072 onFirstFocus: function() {
26073 this.buttons.each(function(item){
26077 toggleSourceEdit : function(sourceEditMode){
26080 if(sourceEditMode){
26081 Roo.log("disabling buttons");
26082 this.buttons.each( function(item){
26083 if(item.cmd != 'pencil'){
26089 Roo.log("enabling buttons");
26090 if(this.editorcore.initialized){
26091 this.buttons.each( function(item){
26097 Roo.log("calling toggole on editor");
26098 // tell the editor that it's been pressed..
26099 this.editor.toggleSourceEdit(sourceEditMode);
26113 * @class Roo.bootstrap.Markdown
26114 * @extends Roo.bootstrap.TextArea
26115 * Bootstrap Showdown editable area
26116 * @cfg {string} content
26119 * Create a new Showdown
26122 Roo.bootstrap.Markdown = function(config){
26123 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26127 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26131 initEvents : function()
26134 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26135 this.markdownEl = this.el.createChild({
26136 cls : 'roo-markdown-area'
26138 this.inputEl().addClass('d-none');
26139 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26140 this.markdownEl.on('click', this.toggleTextEdit, this);
26141 this.on('blur', this.toggleTextEdit, this);
26142 this.on('specialkey', this.resizeTextArea, this);
26145 toggleTextEdit : function()
26147 var sh = this.markdownEl.getHeight();
26148 this.inputEl().addClass('d-none');
26149 this.markdownEl.addClass('d-none');
26150 if (!this.editing) {
26152 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26153 this.inputEl().removeClass('d-none');
26154 this.inputEl().focus();
26155 this.editing = true;
26158 // show showdown...
26159 this.updateMarkdown();
26160 this.markdownEl.removeClass('d-none');
26161 this.editing = false;
26164 updateMarkdown : function()
26166 if (this.getValue() == '') {
26167 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26170 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26173 resizeTextArea: function () {
26176 Roo.log([sh, this.getValue().split("\n").length * 30]);
26177 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26179 setValue : function(val)
26181 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26182 if (!this.editing) {
26183 this.updateMarkdown();
26189 if (!this.editing) {
26190 this.toggleTextEdit();
26198 * @class Roo.bootstrap.Table.AbstractSelectionModel
26199 * @extends Roo.util.Observable
26200 * Abstract base class for grid SelectionModels. It provides the interface that should be
26201 * implemented by descendant classes. This class should not be directly instantiated.
26204 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26205 this.locked = false;
26206 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26210 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26211 /** @ignore Called by the grid automatically. Do not call directly. */
26212 init : function(grid){
26218 * Locks the selections.
26221 this.locked = true;
26225 * Unlocks the selections.
26227 unlock : function(){
26228 this.locked = false;
26232 * Returns true if the selections are locked.
26233 * @return {Boolean}
26235 isLocked : function(){
26236 return this.locked;
26240 initEvents : function ()
26246 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26247 * @class Roo.bootstrap.Table.RowSelectionModel
26248 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26249 * It supports multiple selections and keyboard selection/navigation.
26251 * @param {Object} config
26254 Roo.bootstrap.Table.RowSelectionModel = function(config){
26255 Roo.apply(this, config);
26256 this.selections = new Roo.util.MixedCollection(false, function(o){
26261 this.lastActive = false;
26265 * @event selectionchange
26266 * Fires when the selection changes
26267 * @param {SelectionModel} this
26269 "selectionchange" : true,
26271 * @event afterselectionchange
26272 * Fires after the selection changes (eg. by key press or clicking)
26273 * @param {SelectionModel} this
26275 "afterselectionchange" : true,
26277 * @event beforerowselect
26278 * Fires when a row is selected being selected, return false to cancel.
26279 * @param {SelectionModel} this
26280 * @param {Number} rowIndex The selected index
26281 * @param {Boolean} keepExisting False if other selections will be cleared
26283 "beforerowselect" : true,
26286 * Fires when a row is selected.
26287 * @param {SelectionModel} this
26288 * @param {Number} rowIndex The selected index
26289 * @param {Roo.data.Record} r The record
26291 "rowselect" : true,
26293 * @event rowdeselect
26294 * Fires when a row is deselected.
26295 * @param {SelectionModel} this
26296 * @param {Number} rowIndex The selected index
26298 "rowdeselect" : true
26300 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26301 this.locked = false;
26304 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26306 * @cfg {Boolean} singleSelect
26307 * True to allow selection of only one row at a time (defaults to false)
26309 singleSelect : false,
26312 initEvents : function()
26315 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26316 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26317 //}else{ // allow click to work like normal
26318 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26320 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26321 this.grid.on("rowclick", this.handleMouseDown, this);
26323 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26324 "up" : function(e){
26326 this.selectPrevious(e.shiftKey);
26327 }else if(this.last !== false && this.lastActive !== false){
26328 var last = this.last;
26329 this.selectRange(this.last, this.lastActive-1);
26330 this.grid.getView().focusRow(this.lastActive);
26331 if(last !== false){
26335 this.selectFirstRow();
26337 this.fireEvent("afterselectionchange", this);
26339 "down" : function(e){
26341 this.selectNext(e.shiftKey);
26342 }else if(this.last !== false && this.lastActive !== false){
26343 var last = this.last;
26344 this.selectRange(this.last, this.lastActive+1);
26345 this.grid.getView().focusRow(this.lastActive);
26346 if(last !== false){
26350 this.selectFirstRow();
26352 this.fireEvent("afterselectionchange", this);
26356 this.grid.store.on('load', function(){
26357 this.selections.clear();
26360 var view = this.grid.view;
26361 view.on("refresh", this.onRefresh, this);
26362 view.on("rowupdated", this.onRowUpdated, this);
26363 view.on("rowremoved", this.onRemove, this);
26368 onRefresh : function()
26370 var ds = this.grid.store, i, v = this.grid.view;
26371 var s = this.selections;
26372 s.each(function(r){
26373 if((i = ds.indexOfId(r.id)) != -1){
26382 onRemove : function(v, index, r){
26383 this.selections.remove(r);
26387 onRowUpdated : function(v, index, r){
26388 if(this.isSelected(r)){
26389 v.onRowSelect(index);
26395 * @param {Array} records The records to select
26396 * @param {Boolean} keepExisting (optional) True to keep existing selections
26398 selectRecords : function(records, keepExisting)
26401 this.clearSelections();
26403 var ds = this.grid.store;
26404 for(var i = 0, len = records.length; i < len; i++){
26405 this.selectRow(ds.indexOf(records[i]), true);
26410 * Gets the number of selected rows.
26413 getCount : function(){
26414 return this.selections.length;
26418 * Selects the first row in the grid.
26420 selectFirstRow : function(){
26425 * Select the last row.
26426 * @param {Boolean} keepExisting (optional) True to keep existing selections
26428 selectLastRow : function(keepExisting){
26429 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26430 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26434 * Selects the row immediately following the last selected row.
26435 * @param {Boolean} keepExisting (optional) True to keep existing selections
26437 selectNext : function(keepExisting)
26439 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26440 this.selectRow(this.last+1, keepExisting);
26441 this.grid.getView().focusRow(this.last);
26446 * Selects the row that precedes the last selected row.
26447 * @param {Boolean} keepExisting (optional) True to keep existing selections
26449 selectPrevious : function(keepExisting){
26451 this.selectRow(this.last-1, keepExisting);
26452 this.grid.getView().focusRow(this.last);
26457 * Returns the selected records
26458 * @return {Array} Array of selected records
26460 getSelections : function(){
26461 return [].concat(this.selections.items);
26465 * Returns the first selected record.
26468 getSelected : function(){
26469 return this.selections.itemAt(0);
26474 * Clears all selections.
26476 clearSelections : function(fast)
26482 var ds = this.grid.store;
26483 var s = this.selections;
26484 s.each(function(r){
26485 this.deselectRow(ds.indexOfId(r.id));
26489 this.selections.clear();
26496 * Selects all rows.
26498 selectAll : function(){
26502 this.selections.clear();
26503 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26504 this.selectRow(i, true);
26509 * Returns True if there is a selection.
26510 * @return {Boolean}
26512 hasSelection : function(){
26513 return this.selections.length > 0;
26517 * Returns True if the specified row is selected.
26518 * @param {Number/Record} record The record or index of the record to check
26519 * @return {Boolean}
26521 isSelected : function(index){
26522 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26523 return (r && this.selections.key(r.id) ? true : false);
26527 * Returns True if the specified record id is selected.
26528 * @param {String} id The id of record to check
26529 * @return {Boolean}
26531 isIdSelected : function(id){
26532 return (this.selections.key(id) ? true : false);
26537 handleMouseDBClick : function(e, t){
26541 handleMouseDown : function(e, t)
26543 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26544 if(this.isLocked() || rowIndex < 0 ){
26547 if(e.shiftKey && this.last !== false){
26548 var last = this.last;
26549 this.selectRange(last, rowIndex, e.ctrlKey);
26550 this.last = last; // reset the last
26554 var isSelected = this.isSelected(rowIndex);
26555 //Roo.log("select row:" + rowIndex);
26557 this.deselectRow(rowIndex);
26559 this.selectRow(rowIndex, true);
26563 if(e.button !== 0 && isSelected){
26564 alert('rowIndex 2: ' + rowIndex);
26565 view.focusRow(rowIndex);
26566 }else if(e.ctrlKey && isSelected){
26567 this.deselectRow(rowIndex);
26568 }else if(!isSelected){
26569 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26570 view.focusRow(rowIndex);
26574 this.fireEvent("afterselectionchange", this);
26577 handleDragableRowClick : function(grid, rowIndex, e)
26579 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26580 this.selectRow(rowIndex, false);
26581 grid.view.focusRow(rowIndex);
26582 this.fireEvent("afterselectionchange", this);
26587 * Selects multiple rows.
26588 * @param {Array} rows Array of the indexes of the row to select
26589 * @param {Boolean} keepExisting (optional) True to keep existing selections
26591 selectRows : function(rows, keepExisting){
26593 this.clearSelections();
26595 for(var i = 0, len = rows.length; i < len; i++){
26596 this.selectRow(rows[i], true);
26601 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26602 * @param {Number} startRow The index of the first row in the range
26603 * @param {Number} endRow The index of the last row in the range
26604 * @param {Boolean} keepExisting (optional) True to retain existing selections
26606 selectRange : function(startRow, endRow, keepExisting){
26611 this.clearSelections();
26613 if(startRow <= endRow){
26614 for(var i = startRow; i <= endRow; i++){
26615 this.selectRow(i, true);
26618 for(var i = startRow; i >= endRow; i--){
26619 this.selectRow(i, true);
26625 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26626 * @param {Number} startRow The index of the first row in the range
26627 * @param {Number} endRow The index of the last row in the range
26629 deselectRange : function(startRow, endRow, preventViewNotify){
26633 for(var i = startRow; i <= endRow; i++){
26634 this.deselectRow(i, preventViewNotify);
26640 * @param {Number} row The index of the row to select
26641 * @param {Boolean} keepExisting (optional) True to keep existing selections
26643 selectRow : function(index, keepExisting, preventViewNotify)
26645 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26648 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26649 if(!keepExisting || this.singleSelect){
26650 this.clearSelections();
26653 var r = this.grid.store.getAt(index);
26654 //console.log('selectRow - record id :' + r.id);
26656 this.selections.add(r);
26657 this.last = this.lastActive = index;
26658 if(!preventViewNotify){
26659 var proxy = new Roo.Element(
26660 this.grid.getRowDom(index)
26662 proxy.addClass('bg-info info');
26664 this.fireEvent("rowselect", this, index, r);
26665 this.fireEvent("selectionchange", this);
26671 * @param {Number} row The index of the row to deselect
26673 deselectRow : function(index, preventViewNotify)
26678 if(this.last == index){
26681 if(this.lastActive == index){
26682 this.lastActive = false;
26685 var r = this.grid.store.getAt(index);
26690 this.selections.remove(r);
26691 //.console.log('deselectRow - record id :' + r.id);
26692 if(!preventViewNotify){
26694 var proxy = new Roo.Element(
26695 this.grid.getRowDom(index)
26697 proxy.removeClass('bg-info info');
26699 this.fireEvent("rowdeselect", this, index);
26700 this.fireEvent("selectionchange", this);
26704 restoreLast : function(){
26706 this.last = this._last;
26711 acceptsNav : function(row, col, cm){
26712 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26716 onEditorKey : function(field, e){
26717 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26722 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26724 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26726 }else if(k == e.ENTER && !e.ctrlKey){
26730 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26732 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26734 }else if(k == e.ESC){
26738 g.startEditing(newCell[0], newCell[1]);
26744 * Ext JS Library 1.1.1
26745 * Copyright(c) 2006-2007, Ext JS, LLC.
26747 * Originally Released Under LGPL - original licence link has changed is not relivant.
26750 * <script type="text/javascript">
26754 * @class Roo.bootstrap.PagingToolbar
26755 * @extends Roo.bootstrap.NavSimplebar
26756 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26758 * Create a new PagingToolbar
26759 * @param {Object} config The config object
26760 * @param {Roo.data.Store} store
26762 Roo.bootstrap.PagingToolbar = function(config)
26764 // old args format still supported... - xtype is prefered..
26765 // created from xtype...
26767 this.ds = config.dataSource;
26769 if (config.store && !this.ds) {
26770 this.store= Roo.factory(config.store, Roo.data);
26771 this.ds = this.store;
26772 this.ds.xmodule = this.xmodule || false;
26775 this.toolbarItems = [];
26776 if (config.items) {
26777 this.toolbarItems = config.items;
26780 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26785 this.bind(this.ds);
26788 if (Roo.bootstrap.version == 4) {
26789 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26791 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26796 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26798 * @cfg {Roo.data.Store} dataSource
26799 * The underlying data store providing the paged data
26802 * @cfg {String/HTMLElement/Element} container
26803 * container The id or element that will contain the toolbar
26806 * @cfg {Boolean} displayInfo
26807 * True to display the displayMsg (defaults to false)
26810 * @cfg {Number} pageSize
26811 * The number of records to display per page (defaults to 20)
26815 * @cfg {String} displayMsg
26816 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26818 displayMsg : 'Displaying {0} - {1} of {2}',
26820 * @cfg {String} emptyMsg
26821 * The message to display when no records are found (defaults to "No data to display")
26823 emptyMsg : 'No data to display',
26825 * Customizable piece of the default paging text (defaults to "Page")
26828 beforePageText : "Page",
26830 * Customizable piece of the default paging text (defaults to "of %0")
26833 afterPageText : "of {0}",
26835 * Customizable piece of the default paging text (defaults to "First Page")
26838 firstText : "First Page",
26840 * Customizable piece of the default paging text (defaults to "Previous Page")
26843 prevText : "Previous Page",
26845 * Customizable piece of the default paging text (defaults to "Next Page")
26848 nextText : "Next Page",
26850 * Customizable piece of the default paging text (defaults to "Last Page")
26853 lastText : "Last Page",
26855 * Customizable piece of the default paging text (defaults to "Refresh")
26858 refreshText : "Refresh",
26862 onRender : function(ct, position)
26864 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26865 this.navgroup.parentId = this.id;
26866 this.navgroup.onRender(this.el, null);
26867 // add the buttons to the navgroup
26869 if(this.displayInfo){
26870 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26871 this.displayEl = this.el.select('.x-paging-info', true).first();
26872 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26873 // this.displayEl = navel.el.select('span',true).first();
26879 Roo.each(_this.buttons, function(e){ // this might need to use render????
26880 Roo.factory(e).render(_this.el);
26884 Roo.each(_this.toolbarItems, function(e) {
26885 _this.navgroup.addItem(e);
26889 this.first = this.navgroup.addItem({
26890 tooltip: this.firstText,
26891 cls: "prev btn-outline-secondary",
26892 html : ' <i class="fa fa-step-backward"></i>',
26894 preventDefault: true,
26895 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26898 this.prev = this.navgroup.addItem({
26899 tooltip: this.prevText,
26900 cls: "prev btn-outline-secondary",
26901 html : ' <i class="fa fa-backward"></i>',
26903 preventDefault: true,
26904 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26906 //this.addSeparator();
26909 var field = this.navgroup.addItem( {
26911 cls : 'x-paging-position btn-outline-secondary',
26913 html : this.beforePageText +
26914 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26915 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26918 this.field = field.el.select('input', true).first();
26919 this.field.on("keydown", this.onPagingKeydown, this);
26920 this.field.on("focus", function(){this.dom.select();});
26923 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26924 //this.field.setHeight(18);
26925 //this.addSeparator();
26926 this.next = this.navgroup.addItem({
26927 tooltip: this.nextText,
26928 cls: "next btn-outline-secondary",
26929 html : ' <i class="fa fa-forward"></i>',
26931 preventDefault: true,
26932 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26934 this.last = this.navgroup.addItem({
26935 tooltip: this.lastText,
26936 html : ' <i class="fa fa-step-forward"></i>',
26937 cls: "next btn-outline-secondary",
26939 preventDefault: true,
26940 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26942 //this.addSeparator();
26943 this.loading = this.navgroup.addItem({
26944 tooltip: this.refreshText,
26945 cls: "btn-outline-secondary",
26946 html : ' <i class="fa fa-refresh"></i>',
26947 preventDefault: true,
26948 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26954 updateInfo : function(){
26955 if(this.displayEl){
26956 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26957 var msg = count == 0 ?
26961 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26963 this.displayEl.update(msg);
26968 onLoad : function(ds, r, o)
26970 this.cursor = o.params.start ? o.params.start : 0;
26972 var d = this.getPageData(),
26977 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26978 this.field.dom.value = ap;
26979 this.first.setDisabled(ap == 1);
26980 this.prev.setDisabled(ap == 1);
26981 this.next.setDisabled(ap == ps);
26982 this.last.setDisabled(ap == ps);
26983 this.loading.enable();
26988 getPageData : function(){
26989 var total = this.ds.getTotalCount();
26992 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26993 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26998 onLoadError : function(){
26999 this.loading.enable();
27003 onPagingKeydown : function(e){
27004 var k = e.getKey();
27005 var d = this.getPageData();
27007 var v = this.field.dom.value, pageNum;
27008 if(!v || isNaN(pageNum = parseInt(v, 10))){
27009 this.field.dom.value = d.activePage;
27012 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27013 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27016 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))
27018 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27019 this.field.dom.value = pageNum;
27020 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27023 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27025 var v = this.field.dom.value, pageNum;
27026 var increment = (e.shiftKey) ? 10 : 1;
27027 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27030 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27031 this.field.dom.value = d.activePage;
27034 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27036 this.field.dom.value = parseInt(v, 10) + increment;
27037 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27038 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27045 beforeLoad : function(){
27047 this.loading.disable();
27052 onClick : function(which){
27061 ds.load({params:{start: 0, limit: this.pageSize}});
27064 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27067 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27070 var total = ds.getTotalCount();
27071 var extra = total % this.pageSize;
27072 var lastStart = extra ? (total - extra) : total-this.pageSize;
27073 ds.load({params:{start: lastStart, limit: this.pageSize}});
27076 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27082 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27083 * @param {Roo.data.Store} store The data store to unbind
27085 unbind : function(ds){
27086 ds.un("beforeload", this.beforeLoad, this);
27087 ds.un("load", this.onLoad, this);
27088 ds.un("loadexception", this.onLoadError, this);
27089 ds.un("remove", this.updateInfo, this);
27090 ds.un("add", this.updateInfo, this);
27091 this.ds = undefined;
27095 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27096 * @param {Roo.data.Store} store The data store to bind
27098 bind : function(ds){
27099 ds.on("beforeload", this.beforeLoad, this);
27100 ds.on("load", this.onLoad, this);
27101 ds.on("loadexception", this.onLoadError, this);
27102 ds.on("remove", this.updateInfo, this);
27103 ds.on("add", this.updateInfo, this);
27114 * @class Roo.bootstrap.MessageBar
27115 * @extends Roo.bootstrap.Component
27116 * Bootstrap MessageBar class
27117 * @cfg {String} html contents of the MessageBar
27118 * @cfg {String} weight (info | success | warning | danger) default info
27119 * @cfg {String} beforeClass insert the bar before the given class
27120 * @cfg {Boolean} closable (true | false) default false
27121 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27124 * Create a new Element
27125 * @param {Object} config The config object
27128 Roo.bootstrap.MessageBar = function(config){
27129 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27132 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27138 beforeClass: 'bootstrap-sticky-wrap',
27140 getAutoCreate : function(){
27144 cls: 'alert alert-dismissable alert-' + this.weight,
27149 html: this.html || ''
27155 cfg.cls += ' alert-messages-fixed';
27169 onRender : function(ct, position)
27171 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27174 var cfg = Roo.apply({}, this.getAutoCreate());
27178 cfg.cls += ' ' + this.cls;
27181 cfg.style = this.style;
27183 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27185 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27188 this.el.select('>button.close').on('click', this.hide, this);
27194 if (!this.rendered) {
27200 this.fireEvent('show', this);
27206 if (!this.rendered) {
27212 this.fireEvent('hide', this);
27215 update : function()
27217 // var e = this.el.dom.firstChild;
27219 // if(this.closable){
27220 // e = e.nextSibling;
27223 // e.data = this.html || '';
27225 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27241 * @class Roo.bootstrap.Graph
27242 * @extends Roo.bootstrap.Component
27243 * Bootstrap Graph class
27247 @cfg {String} graphtype bar | vbar | pie
27248 @cfg {number} g_x coodinator | centre x (pie)
27249 @cfg {number} g_y coodinator | centre y (pie)
27250 @cfg {number} g_r radius (pie)
27251 @cfg {number} g_height height of the chart (respected by all elements in the set)
27252 @cfg {number} g_width width of the chart (respected by all elements in the set)
27253 @cfg {Object} title The title of the chart
27256 -opts (object) options for the chart
27258 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27259 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27261 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.
27262 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27264 o stretch (boolean)
27266 -opts (object) options for the pie
27269 o startAngle (number)
27270 o endAngle (number)
27274 * Create a new Input
27275 * @param {Object} config The config object
27278 Roo.bootstrap.Graph = function(config){
27279 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27285 * The img click event for the img.
27286 * @param {Roo.EventObject} e
27292 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27303 //g_colors: this.colors,
27310 getAutoCreate : function(){
27321 onRender : function(ct,position){
27324 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27326 if (typeof(Raphael) == 'undefined') {
27327 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27331 this.raphael = Raphael(this.el.dom);
27333 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27334 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27335 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27336 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27338 r.text(160, 10, "Single Series Chart").attr(txtattr);
27339 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27340 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27341 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27343 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27344 r.barchart(330, 10, 300, 220, data1);
27345 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27346 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27349 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27350 // r.barchart(30, 30, 560, 250, xdata, {
27351 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27352 // axis : "0 0 1 1",
27353 // axisxlabels : xdata
27354 // //yvalues : cols,
27357 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27359 // this.load(null,xdata,{
27360 // axis : "0 0 1 1",
27361 // axisxlabels : xdata
27366 load : function(graphtype,xdata,opts)
27368 this.raphael.clear();
27370 graphtype = this.graphtype;
27375 var r = this.raphael,
27376 fin = function () {
27377 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27379 fout = function () {
27380 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27382 pfin = function() {
27383 this.sector.stop();
27384 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27387 this.label[0].stop();
27388 this.label[0].attr({ r: 7.5 });
27389 this.label[1].attr({ "font-weight": 800 });
27392 pfout = function() {
27393 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27396 this.label[0].animate({ r: 5 }, 500, "bounce");
27397 this.label[1].attr({ "font-weight": 400 });
27403 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27406 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27409 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27410 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27412 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27419 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27424 setTitle: function(o)
27429 initEvents: function() {
27432 this.el.on('click', this.onClick, this);
27436 onClick : function(e)
27438 Roo.log('img onclick');
27439 this.fireEvent('click', this, e);
27451 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27454 * @class Roo.bootstrap.dash.NumberBox
27455 * @extends Roo.bootstrap.Component
27456 * Bootstrap NumberBox class
27457 * @cfg {String} headline Box headline
27458 * @cfg {String} content Box content
27459 * @cfg {String} icon Box icon
27460 * @cfg {String} footer Footer text
27461 * @cfg {String} fhref Footer href
27464 * Create a new NumberBox
27465 * @param {Object} config The config object
27469 Roo.bootstrap.dash.NumberBox = function(config){
27470 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27474 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27483 getAutoCreate : function(){
27487 cls : 'small-box ',
27495 cls : 'roo-headline',
27496 html : this.headline
27500 cls : 'roo-content',
27501 html : this.content
27515 cls : 'ion ' + this.icon
27524 cls : 'small-box-footer',
27525 href : this.fhref || '#',
27529 cfg.cn.push(footer);
27536 onRender : function(ct,position){
27537 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27544 setHeadline: function (value)
27546 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27549 setFooter: function (value, href)
27551 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27554 this.el.select('a.small-box-footer',true).first().attr('href', href);
27559 setContent: function (value)
27561 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27564 initEvents: function()
27578 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27581 * @class Roo.bootstrap.dash.TabBox
27582 * @extends Roo.bootstrap.Component
27583 * Bootstrap TabBox class
27584 * @cfg {String} title Title of the TabBox
27585 * @cfg {String} icon Icon of the TabBox
27586 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27587 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27590 * Create a new TabBox
27591 * @param {Object} config The config object
27595 Roo.bootstrap.dash.TabBox = function(config){
27596 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27601 * When a pane is added
27602 * @param {Roo.bootstrap.dash.TabPane} pane
27606 * @event activatepane
27607 * When a pane is activated
27608 * @param {Roo.bootstrap.dash.TabPane} pane
27610 "activatepane" : true
27618 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27623 tabScrollable : false,
27625 getChildContainer : function()
27627 return this.el.select('.tab-content', true).first();
27630 getAutoCreate : function(){
27634 cls: 'pull-left header',
27642 cls: 'fa ' + this.icon
27648 cls: 'nav nav-tabs pull-right',
27654 if(this.tabScrollable){
27661 cls: 'nav nav-tabs pull-right',
27672 cls: 'nav-tabs-custom',
27677 cls: 'tab-content no-padding',
27685 initEvents : function()
27687 //Roo.log('add add pane handler');
27688 this.on('addpane', this.onAddPane, this);
27691 * Updates the box title
27692 * @param {String} html to set the title to.
27694 setTitle : function(value)
27696 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27698 onAddPane : function(pane)
27700 this.panes.push(pane);
27701 //Roo.log('addpane');
27703 // tabs are rendere left to right..
27704 if(!this.showtabs){
27708 var ctr = this.el.select('.nav-tabs', true).first();
27711 var existing = ctr.select('.nav-tab',true);
27712 var qty = existing.getCount();;
27715 var tab = ctr.createChild({
27717 cls : 'nav-tab' + (qty ? '' : ' active'),
27725 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27728 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27730 pane.el.addClass('active');
27735 onTabClick : function(ev,un,ob,pane)
27737 //Roo.log('tab - prev default');
27738 ev.preventDefault();
27741 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27742 pane.tab.addClass('active');
27743 //Roo.log(pane.title);
27744 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27745 // technically we should have a deactivate event.. but maybe add later.
27746 // and it should not de-activate the selected tab...
27747 this.fireEvent('activatepane', pane);
27748 pane.el.addClass('active');
27749 pane.fireEvent('activate');
27754 getActivePane : function()
27757 Roo.each(this.panes, function(p) {
27758 if(p.el.hasClass('active')){
27779 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27781 * @class Roo.bootstrap.TabPane
27782 * @extends Roo.bootstrap.Component
27783 * Bootstrap TabPane class
27784 * @cfg {Boolean} active (false | true) Default false
27785 * @cfg {String} title title of panel
27789 * Create a new TabPane
27790 * @param {Object} config The config object
27793 Roo.bootstrap.dash.TabPane = function(config){
27794 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27800 * When a pane is activated
27801 * @param {Roo.bootstrap.dash.TabPane} pane
27808 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27813 // the tabBox that this is attached to.
27816 getAutoCreate : function()
27824 cfg.cls += ' active';
27829 initEvents : function()
27831 //Roo.log('trigger add pane handler');
27832 this.parent().fireEvent('addpane', this)
27836 * Updates the tab title
27837 * @param {String} html to set the title to.
27839 setTitle: function(str)
27845 this.tab.select('a', true).first().dom.innerHTML = str;
27862 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27865 * @class Roo.bootstrap.menu.Menu
27866 * @extends Roo.bootstrap.Component
27867 * Bootstrap Menu class - container for Menu
27868 * @cfg {String} html Text of the menu
27869 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27870 * @cfg {String} icon Font awesome icon
27871 * @cfg {String} pos Menu align to (top | bottom) default bottom
27875 * Create a new Menu
27876 * @param {Object} config The config object
27880 Roo.bootstrap.menu.Menu = function(config){
27881 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27885 * @event beforeshow
27886 * Fires before this menu is displayed
27887 * @param {Roo.bootstrap.menu.Menu} this
27891 * @event beforehide
27892 * Fires before this menu is hidden
27893 * @param {Roo.bootstrap.menu.Menu} this
27898 * Fires after this menu is displayed
27899 * @param {Roo.bootstrap.menu.Menu} this
27904 * Fires after this menu is hidden
27905 * @param {Roo.bootstrap.menu.Menu} this
27910 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27911 * @param {Roo.bootstrap.menu.Menu} this
27912 * @param {Roo.EventObject} e
27919 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27923 weight : 'default',
27928 getChildContainer : function() {
27929 if(this.isSubMenu){
27933 return this.el.select('ul.dropdown-menu', true).first();
27936 getAutoCreate : function()
27941 cls : 'roo-menu-text',
27949 cls : 'fa ' + this.icon
27960 cls : 'dropdown-button btn btn-' + this.weight,
27965 cls : 'dropdown-toggle btn btn-' + this.weight,
27975 cls : 'dropdown-menu'
27981 if(this.pos == 'top'){
27982 cfg.cls += ' dropup';
27985 if(this.isSubMenu){
27988 cls : 'dropdown-menu'
27995 onRender : function(ct, position)
27997 this.isSubMenu = ct.hasClass('dropdown-submenu');
27999 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28002 initEvents : function()
28004 if(this.isSubMenu){
28008 this.hidden = true;
28010 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28011 this.triggerEl.on('click', this.onTriggerPress, this);
28013 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28014 this.buttonEl.on('click', this.onClick, this);
28020 if(this.isSubMenu){
28024 return this.el.select('ul.dropdown-menu', true).first();
28027 onClick : function(e)
28029 this.fireEvent("click", this, e);
28032 onTriggerPress : function(e)
28034 if (this.isVisible()) {
28041 isVisible : function(){
28042 return !this.hidden;
28047 this.fireEvent("beforeshow", this);
28049 this.hidden = false;
28050 this.el.addClass('open');
28052 Roo.get(document).on("mouseup", this.onMouseUp, this);
28054 this.fireEvent("show", this);
28061 this.fireEvent("beforehide", this);
28063 this.hidden = true;
28064 this.el.removeClass('open');
28066 Roo.get(document).un("mouseup", this.onMouseUp);
28068 this.fireEvent("hide", this);
28071 onMouseUp : function()
28085 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28088 * @class Roo.bootstrap.menu.Item
28089 * @extends Roo.bootstrap.Component
28090 * Bootstrap MenuItem class
28091 * @cfg {Boolean} submenu (true | false) default false
28092 * @cfg {String} html text of the item
28093 * @cfg {String} href the link
28094 * @cfg {Boolean} disable (true | false) default false
28095 * @cfg {Boolean} preventDefault (true | false) default true
28096 * @cfg {String} icon Font awesome icon
28097 * @cfg {String} pos Submenu align to (left | right) default right
28101 * Create a new Item
28102 * @param {Object} config The config object
28106 Roo.bootstrap.menu.Item = function(config){
28107 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28111 * Fires when the mouse is hovering over this menu
28112 * @param {Roo.bootstrap.menu.Item} this
28113 * @param {Roo.EventObject} e
28118 * Fires when the mouse exits this menu
28119 * @param {Roo.bootstrap.menu.Item} this
28120 * @param {Roo.EventObject} e
28126 * The raw click event for the entire grid.
28127 * @param {Roo.EventObject} e
28133 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28138 preventDefault: true,
28143 getAutoCreate : function()
28148 cls : 'roo-menu-item-text',
28156 cls : 'fa ' + this.icon
28165 href : this.href || '#',
28172 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28176 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28178 if(this.pos == 'left'){
28179 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28186 initEvents : function()
28188 this.el.on('mouseover', this.onMouseOver, this);
28189 this.el.on('mouseout', this.onMouseOut, this);
28191 this.el.select('a', true).first().on('click', this.onClick, this);
28195 onClick : function(e)
28197 if(this.preventDefault){
28198 e.preventDefault();
28201 this.fireEvent("click", this, e);
28204 onMouseOver : function(e)
28206 if(this.submenu && this.pos == 'left'){
28207 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28210 this.fireEvent("mouseover", this, e);
28213 onMouseOut : function(e)
28215 this.fireEvent("mouseout", this, e);
28227 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28230 * @class Roo.bootstrap.menu.Separator
28231 * @extends Roo.bootstrap.Component
28232 * Bootstrap Separator class
28235 * Create a new Separator
28236 * @param {Object} config The config object
28240 Roo.bootstrap.menu.Separator = function(config){
28241 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28244 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28246 getAutoCreate : function(){
28267 * @class Roo.bootstrap.Tooltip
28268 * Bootstrap Tooltip class
28269 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28270 * to determine which dom element triggers the tooltip.
28272 * It needs to add support for additional attributes like tooltip-position
28275 * Create a new Toolti
28276 * @param {Object} config The config object
28279 Roo.bootstrap.Tooltip = function(config){
28280 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28282 this.alignment = Roo.bootstrap.Tooltip.alignment;
28284 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28285 this.alignment = config.alignment;
28290 Roo.apply(Roo.bootstrap.Tooltip, {
28292 * @function init initialize tooltip monitoring.
28296 currentTip : false,
28297 currentRegion : false,
28303 Roo.get(document).on('mouseover', this.enter ,this);
28304 Roo.get(document).on('mouseout', this.leave, this);
28307 this.currentTip = new Roo.bootstrap.Tooltip();
28310 enter : function(ev)
28312 var dom = ev.getTarget();
28314 //Roo.log(['enter',dom]);
28315 var el = Roo.fly(dom);
28316 if (this.currentEl) {
28318 //Roo.log(this.currentEl);
28319 //Roo.log(this.currentEl.contains(dom));
28320 if (this.currentEl == el) {
28323 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28329 if (this.currentTip.el) {
28330 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28334 if(!el || el.dom == document){
28340 // you can not look for children, as if el is the body.. then everythign is the child..
28341 if (!el.attr('tooltip')) { //
28342 if (!el.select("[tooltip]").elements.length) {
28345 // is the mouse over this child...?
28346 bindEl = el.select("[tooltip]").first();
28347 var xy = ev.getXY();
28348 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28349 //Roo.log("not in region.");
28352 //Roo.log("child element over..");
28355 this.currentEl = bindEl;
28356 this.currentTip.bind(bindEl);
28357 this.currentRegion = Roo.lib.Region.getRegion(dom);
28358 this.currentTip.enter();
28361 leave : function(ev)
28363 var dom = ev.getTarget();
28364 //Roo.log(['leave',dom]);
28365 if (!this.currentEl) {
28370 if (dom != this.currentEl.dom) {
28373 var xy = ev.getXY();
28374 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28377 // only activate leave if mouse cursor is outside... bounding box..
28382 if (this.currentTip) {
28383 this.currentTip.leave();
28385 //Roo.log('clear currentEl');
28386 this.currentEl = false;
28391 'left' : ['r-l', [-2,0], 'right'],
28392 'right' : ['l-r', [2,0], 'left'],
28393 'bottom' : ['t-b', [0,2], 'top'],
28394 'top' : [ 'b-t', [0,-2], 'bottom']
28400 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28405 delay : null, // can be { show : 300 , hide: 500}
28409 hoverState : null, //???
28411 placement : 'bottom',
28415 getAutoCreate : function(){
28422 cls : 'tooltip-arrow arrow'
28425 cls : 'tooltip-inner'
28432 bind : function(el)
28437 initEvents : function()
28439 this.arrowEl = this.el.select('.arrow', true).first();
28440 this.innerEl = this.el.select('.tooltip-inner', true).first();
28443 enter : function () {
28445 if (this.timeout != null) {
28446 clearTimeout(this.timeout);
28449 this.hoverState = 'in';
28450 //Roo.log("enter - show");
28451 if (!this.delay || !this.delay.show) {
28456 this.timeout = setTimeout(function () {
28457 if (_t.hoverState == 'in') {
28460 }, this.delay.show);
28464 clearTimeout(this.timeout);
28466 this.hoverState = 'out';
28467 if (!this.delay || !this.delay.hide) {
28473 this.timeout = setTimeout(function () {
28474 //Roo.log("leave - timeout");
28476 if (_t.hoverState == 'out') {
28478 Roo.bootstrap.Tooltip.currentEl = false;
28483 show : function (msg)
28486 this.render(document.body);
28489 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28491 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28493 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28495 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28496 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28498 var placement = typeof this.placement == 'function' ?
28499 this.placement.call(this, this.el, on_el) :
28502 var autoToken = /\s?auto?\s?/i;
28503 var autoPlace = autoToken.test(placement);
28505 placement = placement.replace(autoToken, '') || 'top';
28509 //this.el.setXY([0,0]);
28511 //this.el.dom.style.display='block';
28513 //this.el.appendTo(on_el);
28515 var p = this.getPosition();
28516 var box = this.el.getBox();
28522 var align = this.alignment[placement];
28524 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28526 if(placement == 'top' || placement == 'bottom'){
28528 placement = 'right';
28531 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28532 placement = 'left';
28535 var scroll = Roo.select('body', true).first().getScroll();
28537 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28541 align = this.alignment[placement];
28543 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28547 this.el.alignTo(this.bindEl, align[0],align[1]);
28548 //var arrow = this.el.select('.arrow',true).first();
28549 //arrow.set(align[2],
28551 this.el.addClass(placement);
28552 this.el.addClass("bs-tooltip-"+ placement);
28554 this.el.addClass('in fade show');
28556 this.hoverState = null;
28558 if (this.el.hasClass('fade')) {
28573 //this.el.setXY([0,0]);
28574 this.el.removeClass(['show', 'in']);
28590 * @class Roo.bootstrap.LocationPicker
28591 * @extends Roo.bootstrap.Component
28592 * Bootstrap LocationPicker class
28593 * @cfg {Number} latitude Position when init default 0
28594 * @cfg {Number} longitude Position when init default 0
28595 * @cfg {Number} zoom default 15
28596 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28597 * @cfg {Boolean} mapTypeControl default false
28598 * @cfg {Boolean} disableDoubleClickZoom default false
28599 * @cfg {Boolean} scrollwheel default true
28600 * @cfg {Boolean} streetViewControl default false
28601 * @cfg {Number} radius default 0
28602 * @cfg {String} locationName
28603 * @cfg {Boolean} draggable default true
28604 * @cfg {Boolean} enableAutocomplete default false
28605 * @cfg {Boolean} enableReverseGeocode default true
28606 * @cfg {String} markerTitle
28609 * Create a new LocationPicker
28610 * @param {Object} config The config object
28614 Roo.bootstrap.LocationPicker = function(config){
28616 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28621 * Fires when the picker initialized.
28622 * @param {Roo.bootstrap.LocationPicker} this
28623 * @param {Google Location} location
28627 * @event positionchanged
28628 * Fires when the picker position changed.
28629 * @param {Roo.bootstrap.LocationPicker} this
28630 * @param {Google Location} location
28632 positionchanged : true,
28635 * Fires when the map resize.
28636 * @param {Roo.bootstrap.LocationPicker} this
28641 * Fires when the map show.
28642 * @param {Roo.bootstrap.LocationPicker} this
28647 * Fires when the map hide.
28648 * @param {Roo.bootstrap.LocationPicker} this
28653 * Fires when click the map.
28654 * @param {Roo.bootstrap.LocationPicker} this
28655 * @param {Map event} e
28659 * @event mapRightClick
28660 * Fires when right click the map.
28661 * @param {Roo.bootstrap.LocationPicker} this
28662 * @param {Map event} e
28664 mapRightClick : true,
28666 * @event markerClick
28667 * Fires when click the marker.
28668 * @param {Roo.bootstrap.LocationPicker} this
28669 * @param {Map event} e
28671 markerClick : true,
28673 * @event markerRightClick
28674 * Fires when right click the marker.
28675 * @param {Roo.bootstrap.LocationPicker} this
28676 * @param {Map event} e
28678 markerRightClick : true,
28680 * @event OverlayViewDraw
28681 * Fires when OverlayView Draw
28682 * @param {Roo.bootstrap.LocationPicker} this
28684 OverlayViewDraw : true,
28686 * @event OverlayViewOnAdd
28687 * Fires when OverlayView Draw
28688 * @param {Roo.bootstrap.LocationPicker} this
28690 OverlayViewOnAdd : true,
28692 * @event OverlayViewOnRemove
28693 * Fires when OverlayView Draw
28694 * @param {Roo.bootstrap.LocationPicker} this
28696 OverlayViewOnRemove : true,
28698 * @event OverlayViewShow
28699 * Fires when OverlayView Draw
28700 * @param {Roo.bootstrap.LocationPicker} this
28701 * @param {Pixel} cpx
28703 OverlayViewShow : true,
28705 * @event OverlayViewHide
28706 * Fires when OverlayView Draw
28707 * @param {Roo.bootstrap.LocationPicker} this
28709 OverlayViewHide : true,
28711 * @event loadexception
28712 * Fires when load google lib failed.
28713 * @param {Roo.bootstrap.LocationPicker} this
28715 loadexception : true
28720 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28722 gMapContext: false,
28728 mapTypeControl: false,
28729 disableDoubleClickZoom: false,
28731 streetViewControl: false,
28735 enableAutocomplete: false,
28736 enableReverseGeocode: true,
28739 getAutoCreate: function()
28744 cls: 'roo-location-picker'
28750 initEvents: function(ct, position)
28752 if(!this.el.getWidth() || this.isApplied()){
28756 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28761 initial: function()
28763 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28764 this.fireEvent('loadexception', this);
28768 if(!this.mapTypeId){
28769 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28772 this.gMapContext = this.GMapContext();
28774 this.initOverlayView();
28776 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28780 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28781 _this.setPosition(_this.gMapContext.marker.position);
28784 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28785 _this.fireEvent('mapClick', this, event);
28789 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28790 _this.fireEvent('mapRightClick', this, event);
28794 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28795 _this.fireEvent('markerClick', this, event);
28799 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28800 _this.fireEvent('markerRightClick', this, event);
28804 this.setPosition(this.gMapContext.location);
28806 this.fireEvent('initial', this, this.gMapContext.location);
28809 initOverlayView: function()
28813 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28817 _this.fireEvent('OverlayViewDraw', _this);
28822 _this.fireEvent('OverlayViewOnAdd', _this);
28825 onRemove: function()
28827 _this.fireEvent('OverlayViewOnRemove', _this);
28830 show: function(cpx)
28832 _this.fireEvent('OverlayViewShow', _this, cpx);
28837 _this.fireEvent('OverlayViewHide', _this);
28843 fromLatLngToContainerPixel: function(event)
28845 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28848 isApplied: function()
28850 return this.getGmapContext() == false ? false : true;
28853 getGmapContext: function()
28855 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28858 GMapContext: function()
28860 var position = new google.maps.LatLng(this.latitude, this.longitude);
28862 var _map = new google.maps.Map(this.el.dom, {
28865 mapTypeId: this.mapTypeId,
28866 mapTypeControl: this.mapTypeControl,
28867 disableDoubleClickZoom: this.disableDoubleClickZoom,
28868 scrollwheel: this.scrollwheel,
28869 streetViewControl: this.streetViewControl,
28870 locationName: this.locationName,
28871 draggable: this.draggable,
28872 enableAutocomplete: this.enableAutocomplete,
28873 enableReverseGeocode: this.enableReverseGeocode
28876 var _marker = new google.maps.Marker({
28877 position: position,
28879 title: this.markerTitle,
28880 draggable: this.draggable
28887 location: position,
28888 radius: this.radius,
28889 locationName: this.locationName,
28890 addressComponents: {
28891 formatted_address: null,
28892 addressLine1: null,
28893 addressLine2: null,
28895 streetNumber: null,
28899 stateOrProvince: null
28902 domContainer: this.el.dom,
28903 geodecoder: new google.maps.Geocoder()
28907 drawCircle: function(center, radius, options)
28909 if (this.gMapContext.circle != null) {
28910 this.gMapContext.circle.setMap(null);
28914 options = Roo.apply({}, options, {
28915 strokeColor: "#0000FF",
28916 strokeOpacity: .35,
28918 fillColor: "#0000FF",
28922 options.map = this.gMapContext.map;
28923 options.radius = radius;
28924 options.center = center;
28925 this.gMapContext.circle = new google.maps.Circle(options);
28926 return this.gMapContext.circle;
28932 setPosition: function(location)
28934 this.gMapContext.location = location;
28935 this.gMapContext.marker.setPosition(location);
28936 this.gMapContext.map.panTo(location);
28937 this.drawCircle(location, this.gMapContext.radius, {});
28941 if (this.gMapContext.settings.enableReverseGeocode) {
28942 this.gMapContext.geodecoder.geocode({
28943 latLng: this.gMapContext.location
28944 }, function(results, status) {
28946 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28947 _this.gMapContext.locationName = results[0].formatted_address;
28948 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28950 _this.fireEvent('positionchanged', this, location);
28957 this.fireEvent('positionchanged', this, location);
28962 google.maps.event.trigger(this.gMapContext.map, "resize");
28964 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28966 this.fireEvent('resize', this);
28969 setPositionByLatLng: function(latitude, longitude)
28971 this.setPosition(new google.maps.LatLng(latitude, longitude));
28974 getCurrentPosition: function()
28977 latitude: this.gMapContext.location.lat(),
28978 longitude: this.gMapContext.location.lng()
28982 getAddressName: function()
28984 return this.gMapContext.locationName;
28987 getAddressComponents: function()
28989 return this.gMapContext.addressComponents;
28992 address_component_from_google_geocode: function(address_components)
28996 for (var i = 0; i < address_components.length; i++) {
28997 var component = address_components[i];
28998 if (component.types.indexOf("postal_code") >= 0) {
28999 result.postalCode = component.short_name;
29000 } else if (component.types.indexOf("street_number") >= 0) {
29001 result.streetNumber = component.short_name;
29002 } else if (component.types.indexOf("route") >= 0) {
29003 result.streetName = component.short_name;
29004 } else if (component.types.indexOf("neighborhood") >= 0) {
29005 result.city = component.short_name;
29006 } else if (component.types.indexOf("locality") >= 0) {
29007 result.city = component.short_name;
29008 } else if (component.types.indexOf("sublocality") >= 0) {
29009 result.district = component.short_name;
29010 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29011 result.stateOrProvince = component.short_name;
29012 } else if (component.types.indexOf("country") >= 0) {
29013 result.country = component.short_name;
29017 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29018 result.addressLine2 = "";
29022 setZoomLevel: function(zoom)
29024 this.gMapContext.map.setZoom(zoom);
29037 this.fireEvent('show', this);
29048 this.fireEvent('hide', this);
29053 Roo.apply(Roo.bootstrap.LocationPicker, {
29055 OverlayView : function(map, options)
29057 options = options || {};
29064 * @class Roo.bootstrap.Alert
29065 * @extends Roo.bootstrap.Component
29066 * Bootstrap Alert class - shows an alert area box
29068 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29069 Enter a valid email address
29072 * @cfg {String} title The title of alert
29073 * @cfg {String} html The content of alert
29074 * @cfg {String} weight ( success | info | warning | danger )
29075 * @cfg {String} faicon font-awesomeicon
29078 * Create a new alert
29079 * @param {Object} config The config object
29083 Roo.bootstrap.Alert = function(config){
29084 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29088 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29095 getAutoCreate : function()
29104 cls : 'roo-alert-icon'
29109 cls : 'roo-alert-title',
29114 cls : 'roo-alert-text',
29121 cfg.cn[0].cls += ' fa ' + this.faicon;
29125 cfg.cls += ' alert-' + this.weight;
29131 initEvents: function()
29133 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29136 setTitle : function(str)
29138 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29141 setText : function(str)
29143 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29146 setWeight : function(weight)
29149 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29152 this.weight = weight;
29154 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29157 setIcon : function(icon)
29160 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29163 this.faicon = icon;
29165 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29186 * @class Roo.bootstrap.UploadCropbox
29187 * @extends Roo.bootstrap.Component
29188 * Bootstrap UploadCropbox class
29189 * @cfg {String} emptyText show when image has been loaded
29190 * @cfg {String} rotateNotify show when image too small to rotate
29191 * @cfg {Number} errorTimeout default 3000
29192 * @cfg {Number} minWidth default 300
29193 * @cfg {Number} minHeight default 300
29194 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29195 * @cfg {Boolean} isDocument (true|false) default false
29196 * @cfg {String} url action url
29197 * @cfg {String} paramName default 'imageUpload'
29198 * @cfg {String} method default POST
29199 * @cfg {Boolean} loadMask (true|false) default true
29200 * @cfg {Boolean} loadingText default 'Loading...'
29203 * Create a new UploadCropbox
29204 * @param {Object} config The config object
29207 Roo.bootstrap.UploadCropbox = function(config){
29208 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29212 * @event beforeselectfile
29213 * Fire before select file
29214 * @param {Roo.bootstrap.UploadCropbox} this
29216 "beforeselectfile" : true,
29219 * Fire after initEvent
29220 * @param {Roo.bootstrap.UploadCropbox} this
29225 * Fire after initEvent
29226 * @param {Roo.bootstrap.UploadCropbox} this
29227 * @param {String} data
29232 * Fire when preparing the file data
29233 * @param {Roo.bootstrap.UploadCropbox} this
29234 * @param {Object} file
29239 * Fire when get exception
29240 * @param {Roo.bootstrap.UploadCropbox} this
29241 * @param {XMLHttpRequest} xhr
29243 "exception" : true,
29245 * @event beforeloadcanvas
29246 * Fire before load the canvas
29247 * @param {Roo.bootstrap.UploadCropbox} this
29248 * @param {String} src
29250 "beforeloadcanvas" : true,
29253 * Fire when trash image
29254 * @param {Roo.bootstrap.UploadCropbox} this
29259 * Fire when download the image
29260 * @param {Roo.bootstrap.UploadCropbox} this
29264 * @event footerbuttonclick
29265 * Fire when footerbuttonclick
29266 * @param {Roo.bootstrap.UploadCropbox} this
29267 * @param {String} type
29269 "footerbuttonclick" : true,
29273 * @param {Roo.bootstrap.UploadCropbox} this
29278 * Fire when rotate the image
29279 * @param {Roo.bootstrap.UploadCropbox} this
29280 * @param {String} pos
29285 * Fire when inspect the file
29286 * @param {Roo.bootstrap.UploadCropbox} this
29287 * @param {Object} file
29292 * Fire when xhr upload the file
29293 * @param {Roo.bootstrap.UploadCropbox} this
29294 * @param {Object} data
29299 * Fire when arrange the file data
29300 * @param {Roo.bootstrap.UploadCropbox} this
29301 * @param {Object} formData
29306 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29309 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29311 emptyText : 'Click to upload image',
29312 rotateNotify : 'Image is too small to rotate',
29313 errorTimeout : 3000,
29327 cropType : 'image/jpeg',
29329 canvasLoaded : false,
29330 isDocument : false,
29332 paramName : 'imageUpload',
29334 loadingText : 'Loading...',
29337 getAutoCreate : function()
29341 cls : 'roo-upload-cropbox',
29345 cls : 'roo-upload-cropbox-selector',
29350 cls : 'roo-upload-cropbox-body',
29351 style : 'cursor:pointer',
29355 cls : 'roo-upload-cropbox-preview'
29359 cls : 'roo-upload-cropbox-thumb'
29363 cls : 'roo-upload-cropbox-empty-notify',
29364 html : this.emptyText
29368 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29369 html : this.rotateNotify
29375 cls : 'roo-upload-cropbox-footer',
29378 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29388 onRender : function(ct, position)
29390 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29392 if (this.buttons.length) {
29394 Roo.each(this.buttons, function(bb) {
29396 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29398 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29404 this.maskEl = this.el;
29408 initEvents : function()
29410 this.urlAPI = (window.createObjectURL && window) ||
29411 (window.URL && URL.revokeObjectURL && URL) ||
29412 (window.webkitURL && webkitURL);
29414 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29415 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29417 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29418 this.selectorEl.hide();
29420 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29421 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29423 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29424 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29425 this.thumbEl.hide();
29427 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29428 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29430 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29431 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29432 this.errorEl.hide();
29434 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29435 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29436 this.footerEl.hide();
29438 this.setThumbBoxSize();
29444 this.fireEvent('initial', this);
29451 window.addEventListener("resize", function() { _this.resize(); } );
29453 this.bodyEl.on('click', this.beforeSelectFile, this);
29456 this.bodyEl.on('touchstart', this.onTouchStart, this);
29457 this.bodyEl.on('touchmove', this.onTouchMove, this);
29458 this.bodyEl.on('touchend', this.onTouchEnd, this);
29462 this.bodyEl.on('mousedown', this.onMouseDown, this);
29463 this.bodyEl.on('mousemove', this.onMouseMove, this);
29464 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29465 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29466 Roo.get(document).on('mouseup', this.onMouseUp, this);
29469 this.selectorEl.on('change', this.onFileSelected, this);
29475 this.baseScale = 1;
29477 this.baseRotate = 1;
29478 this.dragable = false;
29479 this.pinching = false;
29482 this.cropData = false;
29483 this.notifyEl.dom.innerHTML = this.emptyText;
29485 this.selectorEl.dom.value = '';
29489 resize : function()
29491 if(this.fireEvent('resize', this) != false){
29492 this.setThumbBoxPosition();
29493 this.setCanvasPosition();
29497 onFooterButtonClick : function(e, el, o, type)
29500 case 'rotate-left' :
29501 this.onRotateLeft(e);
29503 case 'rotate-right' :
29504 this.onRotateRight(e);
29507 this.beforeSelectFile(e);
29522 this.fireEvent('footerbuttonclick', this, type);
29525 beforeSelectFile : function(e)
29527 e.preventDefault();
29529 if(this.fireEvent('beforeselectfile', this) != false){
29530 this.selectorEl.dom.click();
29534 onFileSelected : function(e)
29536 e.preventDefault();
29538 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29542 var file = this.selectorEl.dom.files[0];
29544 if(this.fireEvent('inspect', this, file) != false){
29545 this.prepare(file);
29550 trash : function(e)
29552 this.fireEvent('trash', this);
29555 download : function(e)
29557 this.fireEvent('download', this);
29560 loadCanvas : function(src)
29562 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29566 this.imageEl = document.createElement('img');
29570 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29572 this.imageEl.src = src;
29576 onLoadCanvas : function()
29578 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29579 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29581 this.bodyEl.un('click', this.beforeSelectFile, this);
29583 this.notifyEl.hide();
29584 this.thumbEl.show();
29585 this.footerEl.show();
29587 this.baseRotateLevel();
29589 if(this.isDocument){
29590 this.setThumbBoxSize();
29593 this.setThumbBoxPosition();
29595 this.baseScaleLevel();
29601 this.canvasLoaded = true;
29604 this.maskEl.unmask();
29609 setCanvasPosition : function()
29611 if(!this.canvasEl){
29615 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29616 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29618 this.previewEl.setLeft(pw);
29619 this.previewEl.setTop(ph);
29623 onMouseDown : function(e)
29627 this.dragable = true;
29628 this.pinching = false;
29630 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29631 this.dragable = false;
29635 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29636 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29640 onMouseMove : function(e)
29644 if(!this.canvasLoaded){
29648 if (!this.dragable){
29652 var minX = Math.ceil(this.thumbEl.getLeft(true));
29653 var minY = Math.ceil(this.thumbEl.getTop(true));
29655 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29656 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29658 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29659 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29661 x = x - this.mouseX;
29662 y = y - this.mouseY;
29664 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29665 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29667 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29668 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29670 this.previewEl.setLeft(bgX);
29671 this.previewEl.setTop(bgY);
29673 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29674 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29677 onMouseUp : function(e)
29681 this.dragable = false;
29684 onMouseWheel : function(e)
29688 this.startScale = this.scale;
29690 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29692 if(!this.zoomable()){
29693 this.scale = this.startScale;
29702 zoomable : function()
29704 var minScale = this.thumbEl.getWidth() / this.minWidth;
29706 if(this.minWidth < this.minHeight){
29707 minScale = this.thumbEl.getHeight() / this.minHeight;
29710 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29711 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29715 (this.rotate == 0 || this.rotate == 180) &&
29717 width > this.imageEl.OriginWidth ||
29718 height > this.imageEl.OriginHeight ||
29719 (width < this.minWidth && height < this.minHeight)
29727 (this.rotate == 90 || this.rotate == 270) &&
29729 width > this.imageEl.OriginWidth ||
29730 height > this.imageEl.OriginHeight ||
29731 (width < this.minHeight && height < this.minWidth)
29738 !this.isDocument &&
29739 (this.rotate == 0 || this.rotate == 180) &&
29741 width < this.minWidth ||
29742 width > this.imageEl.OriginWidth ||
29743 height < this.minHeight ||
29744 height > this.imageEl.OriginHeight
29751 !this.isDocument &&
29752 (this.rotate == 90 || this.rotate == 270) &&
29754 width < this.minHeight ||
29755 width > this.imageEl.OriginWidth ||
29756 height < this.minWidth ||
29757 height > this.imageEl.OriginHeight
29767 onRotateLeft : function(e)
29769 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29771 var minScale = this.thumbEl.getWidth() / this.minWidth;
29773 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29774 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29776 this.startScale = this.scale;
29778 while (this.getScaleLevel() < minScale){
29780 this.scale = this.scale + 1;
29782 if(!this.zoomable()){
29787 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29788 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29793 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29800 this.scale = this.startScale;
29802 this.onRotateFail();
29807 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29809 if(this.isDocument){
29810 this.setThumbBoxSize();
29811 this.setThumbBoxPosition();
29812 this.setCanvasPosition();
29817 this.fireEvent('rotate', this, 'left');
29821 onRotateRight : function(e)
29823 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29825 var minScale = this.thumbEl.getWidth() / this.minWidth;
29827 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29828 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29830 this.startScale = this.scale;
29832 while (this.getScaleLevel() < minScale){
29834 this.scale = this.scale + 1;
29836 if(!this.zoomable()){
29841 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29842 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29847 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29854 this.scale = this.startScale;
29856 this.onRotateFail();
29861 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29863 if(this.isDocument){
29864 this.setThumbBoxSize();
29865 this.setThumbBoxPosition();
29866 this.setCanvasPosition();
29871 this.fireEvent('rotate', this, 'right');
29874 onRotateFail : function()
29876 this.errorEl.show(true);
29880 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29885 this.previewEl.dom.innerHTML = '';
29887 var canvasEl = document.createElement("canvas");
29889 var contextEl = canvasEl.getContext("2d");
29891 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29892 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29893 var center = this.imageEl.OriginWidth / 2;
29895 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29896 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29897 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29898 center = this.imageEl.OriginHeight / 2;
29901 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29903 contextEl.translate(center, center);
29904 contextEl.rotate(this.rotate * Math.PI / 180);
29906 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29908 this.canvasEl = document.createElement("canvas");
29910 this.contextEl = this.canvasEl.getContext("2d");
29912 switch (this.rotate) {
29915 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29916 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29918 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29923 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29924 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29926 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29927 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);
29931 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29936 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29937 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29939 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29940 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);
29944 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);
29949 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29950 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29952 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29953 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29957 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);
29964 this.previewEl.appendChild(this.canvasEl);
29966 this.setCanvasPosition();
29971 if(!this.canvasLoaded){
29975 var imageCanvas = document.createElement("canvas");
29977 var imageContext = imageCanvas.getContext("2d");
29979 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29980 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29982 var center = imageCanvas.width / 2;
29984 imageContext.translate(center, center);
29986 imageContext.rotate(this.rotate * Math.PI / 180);
29988 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29990 var canvas = document.createElement("canvas");
29992 var context = canvas.getContext("2d");
29994 canvas.width = this.minWidth;
29995 canvas.height = this.minHeight;
29997 switch (this.rotate) {
30000 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30001 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30003 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30004 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30006 var targetWidth = this.minWidth - 2 * x;
30007 var targetHeight = this.minHeight - 2 * y;
30011 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30012 scale = targetWidth / width;
30015 if(x > 0 && y == 0){
30016 scale = targetHeight / height;
30019 if(x > 0 && y > 0){
30020 scale = targetWidth / width;
30022 if(width < height){
30023 scale = targetHeight / height;
30027 context.scale(scale, scale);
30029 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30030 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30032 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30033 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30035 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30040 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30041 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30043 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30044 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30046 var targetWidth = this.minWidth - 2 * x;
30047 var targetHeight = this.minHeight - 2 * y;
30051 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30052 scale = targetWidth / width;
30055 if(x > 0 && y == 0){
30056 scale = targetHeight / height;
30059 if(x > 0 && y > 0){
30060 scale = targetWidth / width;
30062 if(width < height){
30063 scale = targetHeight / height;
30067 context.scale(scale, scale);
30069 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30070 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30072 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30073 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30075 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30077 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30082 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30083 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30085 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30086 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30088 var targetWidth = this.minWidth - 2 * x;
30089 var targetHeight = this.minHeight - 2 * y;
30093 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30094 scale = targetWidth / width;
30097 if(x > 0 && y == 0){
30098 scale = targetHeight / height;
30101 if(x > 0 && y > 0){
30102 scale = targetWidth / width;
30104 if(width < height){
30105 scale = targetHeight / height;
30109 context.scale(scale, scale);
30111 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30112 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30114 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30115 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30117 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30118 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30120 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30125 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30126 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30128 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30129 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30131 var targetWidth = this.minWidth - 2 * x;
30132 var targetHeight = this.minHeight - 2 * y;
30136 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30137 scale = targetWidth / width;
30140 if(x > 0 && y == 0){
30141 scale = targetHeight / height;
30144 if(x > 0 && y > 0){
30145 scale = targetWidth / width;
30147 if(width < height){
30148 scale = targetHeight / height;
30152 context.scale(scale, scale);
30154 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30155 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30157 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30158 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30160 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30162 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30169 this.cropData = canvas.toDataURL(this.cropType);
30171 if(this.fireEvent('crop', this, this.cropData) !== false){
30172 this.process(this.file, this.cropData);
30179 setThumbBoxSize : function()
30183 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30184 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30185 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30187 this.minWidth = width;
30188 this.minHeight = height;
30190 if(this.rotate == 90 || this.rotate == 270){
30191 this.minWidth = height;
30192 this.minHeight = width;
30197 width = Math.ceil(this.minWidth * height / this.minHeight);
30199 if(this.minWidth > this.minHeight){
30201 height = Math.ceil(this.minHeight * width / this.minWidth);
30204 this.thumbEl.setStyle({
30205 width : width + 'px',
30206 height : height + 'px'
30213 setThumbBoxPosition : function()
30215 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30216 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30218 this.thumbEl.setLeft(x);
30219 this.thumbEl.setTop(y);
30223 baseRotateLevel : function()
30225 this.baseRotate = 1;
30228 typeof(this.exif) != 'undefined' &&
30229 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30230 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30232 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30235 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30239 baseScaleLevel : function()
30243 if(this.isDocument){
30245 if(this.baseRotate == 6 || this.baseRotate == 8){
30247 height = this.thumbEl.getHeight();
30248 this.baseScale = height / this.imageEl.OriginWidth;
30250 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30251 width = this.thumbEl.getWidth();
30252 this.baseScale = width / this.imageEl.OriginHeight;
30258 height = this.thumbEl.getHeight();
30259 this.baseScale = height / this.imageEl.OriginHeight;
30261 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30262 width = this.thumbEl.getWidth();
30263 this.baseScale = width / this.imageEl.OriginWidth;
30269 if(this.baseRotate == 6 || this.baseRotate == 8){
30271 width = this.thumbEl.getHeight();
30272 this.baseScale = width / this.imageEl.OriginHeight;
30274 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30275 height = this.thumbEl.getWidth();
30276 this.baseScale = height / this.imageEl.OriginHeight;
30279 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30280 height = this.thumbEl.getWidth();
30281 this.baseScale = height / this.imageEl.OriginHeight;
30283 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30284 width = this.thumbEl.getHeight();
30285 this.baseScale = width / this.imageEl.OriginWidth;
30292 width = this.thumbEl.getWidth();
30293 this.baseScale = width / this.imageEl.OriginWidth;
30295 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30296 height = this.thumbEl.getHeight();
30297 this.baseScale = height / this.imageEl.OriginHeight;
30300 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30302 height = this.thumbEl.getHeight();
30303 this.baseScale = height / this.imageEl.OriginHeight;
30305 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30306 width = this.thumbEl.getWidth();
30307 this.baseScale = width / this.imageEl.OriginWidth;
30315 getScaleLevel : function()
30317 return this.baseScale * Math.pow(1.1, this.scale);
30320 onTouchStart : function(e)
30322 if(!this.canvasLoaded){
30323 this.beforeSelectFile(e);
30327 var touches = e.browserEvent.touches;
30333 if(touches.length == 1){
30334 this.onMouseDown(e);
30338 if(touches.length != 2){
30344 for(var i = 0, finger; finger = touches[i]; i++){
30345 coords.push(finger.pageX, finger.pageY);
30348 var x = Math.pow(coords[0] - coords[2], 2);
30349 var y = Math.pow(coords[1] - coords[3], 2);
30351 this.startDistance = Math.sqrt(x + y);
30353 this.startScale = this.scale;
30355 this.pinching = true;
30356 this.dragable = false;
30360 onTouchMove : function(e)
30362 if(!this.pinching && !this.dragable){
30366 var touches = e.browserEvent.touches;
30373 this.onMouseMove(e);
30379 for(var i = 0, finger; finger = touches[i]; i++){
30380 coords.push(finger.pageX, finger.pageY);
30383 var x = Math.pow(coords[0] - coords[2], 2);
30384 var y = Math.pow(coords[1] - coords[3], 2);
30386 this.endDistance = Math.sqrt(x + y);
30388 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30390 if(!this.zoomable()){
30391 this.scale = this.startScale;
30399 onTouchEnd : function(e)
30401 this.pinching = false;
30402 this.dragable = false;
30406 process : function(file, crop)
30409 this.maskEl.mask(this.loadingText);
30412 this.xhr = new XMLHttpRequest();
30414 file.xhr = this.xhr;
30416 this.xhr.open(this.method, this.url, true);
30419 "Accept": "application/json",
30420 "Cache-Control": "no-cache",
30421 "X-Requested-With": "XMLHttpRequest"
30424 for (var headerName in headers) {
30425 var headerValue = headers[headerName];
30427 this.xhr.setRequestHeader(headerName, headerValue);
30433 this.xhr.onload = function()
30435 _this.xhrOnLoad(_this.xhr);
30438 this.xhr.onerror = function()
30440 _this.xhrOnError(_this.xhr);
30443 var formData = new FormData();
30445 formData.append('returnHTML', 'NO');
30448 formData.append('crop', crop);
30451 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30452 formData.append(this.paramName, file, file.name);
30455 if(typeof(file.filename) != 'undefined'){
30456 formData.append('filename', file.filename);
30459 if(typeof(file.mimetype) != 'undefined'){
30460 formData.append('mimetype', file.mimetype);
30463 if(this.fireEvent('arrange', this, formData) != false){
30464 this.xhr.send(formData);
30468 xhrOnLoad : function(xhr)
30471 this.maskEl.unmask();
30474 if (xhr.readyState !== 4) {
30475 this.fireEvent('exception', this, xhr);
30479 var response = Roo.decode(xhr.responseText);
30481 if(!response.success){
30482 this.fireEvent('exception', this, xhr);
30486 var response = Roo.decode(xhr.responseText);
30488 this.fireEvent('upload', this, response);
30492 xhrOnError : function()
30495 this.maskEl.unmask();
30498 Roo.log('xhr on error');
30500 var response = Roo.decode(xhr.responseText);
30506 prepare : function(file)
30509 this.maskEl.mask(this.loadingText);
30515 if(typeof(file) === 'string'){
30516 this.loadCanvas(file);
30520 if(!file || !this.urlAPI){
30525 this.cropType = file.type;
30529 if(this.fireEvent('prepare', this, this.file) != false){
30531 var reader = new FileReader();
30533 reader.onload = function (e) {
30534 if (e.target.error) {
30535 Roo.log(e.target.error);
30539 var buffer = e.target.result,
30540 dataView = new DataView(buffer),
30542 maxOffset = dataView.byteLength - 4,
30546 if (dataView.getUint16(0) === 0xffd8) {
30547 while (offset < maxOffset) {
30548 markerBytes = dataView.getUint16(offset);
30550 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30551 markerLength = dataView.getUint16(offset + 2) + 2;
30552 if (offset + markerLength > dataView.byteLength) {
30553 Roo.log('Invalid meta data: Invalid segment size.');
30557 if(markerBytes == 0xffe1){
30558 _this.parseExifData(
30565 offset += markerLength;
30575 var url = _this.urlAPI.createObjectURL(_this.file);
30577 _this.loadCanvas(url);
30582 reader.readAsArrayBuffer(this.file);
30588 parseExifData : function(dataView, offset, length)
30590 var tiffOffset = offset + 10,
30594 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30595 // No Exif data, might be XMP data instead
30599 // Check for the ASCII code for "Exif" (0x45786966):
30600 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30601 // No Exif data, might be XMP data instead
30604 if (tiffOffset + 8 > dataView.byteLength) {
30605 Roo.log('Invalid Exif data: Invalid segment size.');
30608 // Check for the two null bytes:
30609 if (dataView.getUint16(offset + 8) !== 0x0000) {
30610 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30613 // Check the byte alignment:
30614 switch (dataView.getUint16(tiffOffset)) {
30616 littleEndian = true;
30619 littleEndian = false;
30622 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30625 // Check for the TIFF tag marker (0x002A):
30626 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30627 Roo.log('Invalid Exif data: Missing TIFF marker.');
30630 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30631 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30633 this.parseExifTags(
30636 tiffOffset + dirOffset,
30641 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30646 if (dirOffset + 6 > dataView.byteLength) {
30647 Roo.log('Invalid Exif data: Invalid directory offset.');
30650 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30651 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30652 if (dirEndOffset + 4 > dataView.byteLength) {
30653 Roo.log('Invalid Exif data: Invalid directory size.');
30656 for (i = 0; i < tagsNumber; i += 1) {
30660 dirOffset + 2 + 12 * i, // tag offset
30664 // Return the offset to the next directory:
30665 return dataView.getUint32(dirEndOffset, littleEndian);
30668 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30670 var tag = dataView.getUint16(offset, littleEndian);
30672 this.exif[tag] = this.getExifValue(
30676 dataView.getUint16(offset + 2, littleEndian), // tag type
30677 dataView.getUint32(offset + 4, littleEndian), // tag length
30682 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30684 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30693 Roo.log('Invalid Exif data: Invalid tag type.');
30697 tagSize = tagType.size * length;
30698 // Determine if the value is contained in the dataOffset bytes,
30699 // or if the value at the dataOffset is a pointer to the actual data:
30700 dataOffset = tagSize > 4 ?
30701 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30702 if (dataOffset + tagSize > dataView.byteLength) {
30703 Roo.log('Invalid Exif data: Invalid data offset.');
30706 if (length === 1) {
30707 return tagType.getValue(dataView, dataOffset, littleEndian);
30710 for (i = 0; i < length; i += 1) {
30711 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30714 if (tagType.ascii) {
30716 // Concatenate the chars:
30717 for (i = 0; i < values.length; i += 1) {
30719 // Ignore the terminating NULL byte(s):
30720 if (c === '\u0000') {
30732 Roo.apply(Roo.bootstrap.UploadCropbox, {
30734 'Orientation': 0x0112
30738 1: 0, //'top-left',
30740 3: 180, //'bottom-right',
30741 // 4: 'bottom-left',
30743 6: 90, //'right-top',
30744 // 7: 'right-bottom',
30745 8: 270 //'left-bottom'
30749 // byte, 8-bit unsigned int:
30751 getValue: function (dataView, dataOffset) {
30752 return dataView.getUint8(dataOffset);
30756 // ascii, 8-bit byte:
30758 getValue: function (dataView, dataOffset) {
30759 return String.fromCharCode(dataView.getUint8(dataOffset));
30764 // short, 16 bit int:
30766 getValue: function (dataView, dataOffset, littleEndian) {
30767 return dataView.getUint16(dataOffset, littleEndian);
30771 // long, 32 bit int:
30773 getValue: function (dataView, dataOffset, littleEndian) {
30774 return dataView.getUint32(dataOffset, littleEndian);
30778 // rational = two long values, first is numerator, second is denominator:
30780 getValue: function (dataView, dataOffset, littleEndian) {
30781 return dataView.getUint32(dataOffset, littleEndian) /
30782 dataView.getUint32(dataOffset + 4, littleEndian);
30786 // slong, 32 bit signed int:
30788 getValue: function (dataView, dataOffset, littleEndian) {
30789 return dataView.getInt32(dataOffset, littleEndian);
30793 // srational, two slongs, first is numerator, second is denominator:
30795 getValue: function (dataView, dataOffset, littleEndian) {
30796 return dataView.getInt32(dataOffset, littleEndian) /
30797 dataView.getInt32(dataOffset + 4, littleEndian);
30807 cls : 'btn-group roo-upload-cropbox-rotate-left',
30808 action : 'rotate-left',
30812 cls : 'btn btn-default',
30813 html : '<i class="fa fa-undo"></i>'
30819 cls : 'btn-group roo-upload-cropbox-picture',
30820 action : 'picture',
30824 cls : 'btn btn-default',
30825 html : '<i class="fa fa-picture-o"></i>'
30831 cls : 'btn-group roo-upload-cropbox-rotate-right',
30832 action : 'rotate-right',
30836 cls : 'btn btn-default',
30837 html : '<i class="fa fa-repeat"></i>'
30845 cls : 'btn-group roo-upload-cropbox-rotate-left',
30846 action : 'rotate-left',
30850 cls : 'btn btn-default',
30851 html : '<i class="fa fa-undo"></i>'
30857 cls : 'btn-group roo-upload-cropbox-download',
30858 action : 'download',
30862 cls : 'btn btn-default',
30863 html : '<i class="fa fa-download"></i>'
30869 cls : 'btn-group roo-upload-cropbox-crop',
30874 cls : 'btn btn-default',
30875 html : '<i class="fa fa-crop"></i>'
30881 cls : 'btn-group roo-upload-cropbox-trash',
30886 cls : 'btn btn-default',
30887 html : '<i class="fa fa-trash"></i>'
30893 cls : 'btn-group roo-upload-cropbox-rotate-right',
30894 action : 'rotate-right',
30898 cls : 'btn btn-default',
30899 html : '<i class="fa fa-repeat"></i>'
30907 cls : 'btn-group roo-upload-cropbox-rotate-left',
30908 action : 'rotate-left',
30912 cls : 'btn btn-default',
30913 html : '<i class="fa fa-undo"></i>'
30919 cls : 'btn-group roo-upload-cropbox-rotate-right',
30920 action : 'rotate-right',
30924 cls : 'btn btn-default',
30925 html : '<i class="fa fa-repeat"></i>'
30938 * @class Roo.bootstrap.DocumentManager
30939 * @extends Roo.bootstrap.Component
30940 * Bootstrap DocumentManager class
30941 * @cfg {String} paramName default 'imageUpload'
30942 * @cfg {String} toolTipName default 'filename'
30943 * @cfg {String} method default POST
30944 * @cfg {String} url action url
30945 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30946 * @cfg {Boolean} multiple multiple upload default true
30947 * @cfg {Number} thumbSize default 300
30948 * @cfg {String} fieldLabel
30949 * @cfg {Number} labelWidth default 4
30950 * @cfg {String} labelAlign (left|top) default left
30951 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30952 * @cfg {Number} labellg set the width of label (1-12)
30953 * @cfg {Number} labelmd set the width of label (1-12)
30954 * @cfg {Number} labelsm set the width of label (1-12)
30955 * @cfg {Number} labelxs set the width of label (1-12)
30958 * Create a new DocumentManager
30959 * @param {Object} config The config object
30962 Roo.bootstrap.DocumentManager = function(config){
30963 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30966 this.delegates = [];
30971 * Fire when initial the DocumentManager
30972 * @param {Roo.bootstrap.DocumentManager} this
30977 * inspect selected file
30978 * @param {Roo.bootstrap.DocumentManager} this
30979 * @param {File} file
30984 * Fire when xhr load exception
30985 * @param {Roo.bootstrap.DocumentManager} this
30986 * @param {XMLHttpRequest} xhr
30988 "exception" : true,
30990 * @event afterupload
30991 * Fire when xhr load exception
30992 * @param {Roo.bootstrap.DocumentManager} this
30993 * @param {XMLHttpRequest} xhr
30995 "afterupload" : true,
30998 * prepare the form data
30999 * @param {Roo.bootstrap.DocumentManager} this
31000 * @param {Object} formData
31005 * Fire when remove the file
31006 * @param {Roo.bootstrap.DocumentManager} this
31007 * @param {Object} file
31012 * Fire after refresh the file
31013 * @param {Roo.bootstrap.DocumentManager} this
31018 * Fire after click the image
31019 * @param {Roo.bootstrap.DocumentManager} this
31020 * @param {Object} file
31025 * Fire when upload a image and editable set to true
31026 * @param {Roo.bootstrap.DocumentManager} this
31027 * @param {Object} file
31031 * @event beforeselectfile
31032 * Fire before select file
31033 * @param {Roo.bootstrap.DocumentManager} this
31035 "beforeselectfile" : true,
31038 * Fire before process file
31039 * @param {Roo.bootstrap.DocumentManager} this
31040 * @param {Object} file
31044 * @event previewrendered
31045 * Fire when preview rendered
31046 * @param {Roo.bootstrap.DocumentManager} this
31047 * @param {Object} file
31049 "previewrendered" : true,
31052 "previewResize" : true
31057 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31066 paramName : 'imageUpload',
31067 toolTipName : 'filename',
31070 labelAlign : 'left',
31080 getAutoCreate : function()
31082 var managerWidget = {
31084 cls : 'roo-document-manager',
31088 cls : 'roo-document-manager-selector',
31093 cls : 'roo-document-manager-uploader',
31097 cls : 'roo-document-manager-upload-btn',
31098 html : '<i class="fa fa-plus"></i>'
31109 cls : 'column col-md-12',
31114 if(this.fieldLabel.length){
31119 cls : 'column col-md-12',
31120 html : this.fieldLabel
31124 cls : 'column col-md-12',
31129 if(this.labelAlign == 'left'){
31134 html : this.fieldLabel
31143 if(this.labelWidth > 12){
31144 content[0].style = "width: " + this.labelWidth + 'px';
31147 if(this.labelWidth < 13 && this.labelmd == 0){
31148 this.labelmd = this.labelWidth;
31151 if(this.labellg > 0){
31152 content[0].cls += ' col-lg-' + this.labellg;
31153 content[1].cls += ' col-lg-' + (12 - this.labellg);
31156 if(this.labelmd > 0){
31157 content[0].cls += ' col-md-' + this.labelmd;
31158 content[1].cls += ' col-md-' + (12 - this.labelmd);
31161 if(this.labelsm > 0){
31162 content[0].cls += ' col-sm-' + this.labelsm;
31163 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31166 if(this.labelxs > 0){
31167 content[0].cls += ' col-xs-' + this.labelxs;
31168 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31176 cls : 'row clearfix',
31184 initEvents : function()
31186 this.managerEl = this.el.select('.roo-document-manager', true).first();
31187 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31189 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31190 this.selectorEl.hide();
31193 this.selectorEl.attr('multiple', 'multiple');
31196 this.selectorEl.on('change', this.onFileSelected, this);
31198 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31199 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31201 this.uploader.on('click', this.onUploaderClick, this);
31203 this.renderProgressDialog();
31207 window.addEventListener("resize", function() { _this.refresh(); } );
31209 this.fireEvent('initial', this);
31212 renderProgressDialog : function()
31216 this.progressDialog = new Roo.bootstrap.Modal({
31217 cls : 'roo-document-manager-progress-dialog',
31218 allow_close : false,
31229 btnclick : function() {
31230 _this.uploadCancel();
31236 this.progressDialog.render(Roo.get(document.body));
31238 this.progress = new Roo.bootstrap.Progress({
31239 cls : 'roo-document-manager-progress',
31244 this.progress.render(this.progressDialog.getChildContainer());
31246 this.progressBar = new Roo.bootstrap.ProgressBar({
31247 cls : 'roo-document-manager-progress-bar',
31250 aria_valuemax : 12,
31254 this.progressBar.render(this.progress.getChildContainer());
31257 onUploaderClick : function(e)
31259 e.preventDefault();
31261 if(this.fireEvent('beforeselectfile', this) != false){
31262 this.selectorEl.dom.click();
31267 onFileSelected : function(e)
31269 e.preventDefault();
31271 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31275 Roo.each(this.selectorEl.dom.files, function(file){
31276 if(this.fireEvent('inspect', this, file) != false){
31277 this.files.push(file);
31287 this.selectorEl.dom.value = '';
31289 if(!this.files || !this.files.length){
31293 if(this.boxes > 0 && this.files.length > this.boxes){
31294 this.files = this.files.slice(0, this.boxes);
31297 this.uploader.show();
31299 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31300 this.uploader.hide();
31309 Roo.each(this.files, function(file){
31311 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31312 var f = this.renderPreview(file);
31317 if(file.type.indexOf('image') != -1){
31318 this.delegates.push(
31320 _this.process(file);
31321 }).createDelegate(this)
31329 _this.process(file);
31330 }).createDelegate(this)
31335 this.files = files;
31337 this.delegates = this.delegates.concat(docs);
31339 if(!this.delegates.length){
31344 this.progressBar.aria_valuemax = this.delegates.length;
31351 arrange : function()
31353 if(!this.delegates.length){
31354 this.progressDialog.hide();
31359 var delegate = this.delegates.shift();
31361 this.progressDialog.show();
31363 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31365 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31370 refresh : function()
31372 this.uploader.show();
31374 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31375 this.uploader.hide();
31378 Roo.isTouch ? this.closable(false) : this.closable(true);
31380 this.fireEvent('refresh', this);
31383 onRemove : function(e, el, o)
31385 e.preventDefault();
31387 this.fireEvent('remove', this, o);
31391 remove : function(o)
31395 Roo.each(this.files, function(file){
31396 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31405 this.files = files;
31412 Roo.each(this.files, function(file){
31417 file.target.remove();
31426 onClick : function(e, el, o)
31428 e.preventDefault();
31430 this.fireEvent('click', this, o);
31434 closable : function(closable)
31436 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31438 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31450 xhrOnLoad : function(xhr)
31452 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31456 if (xhr.readyState !== 4) {
31458 this.fireEvent('exception', this, xhr);
31462 var response = Roo.decode(xhr.responseText);
31464 if(!response.success){
31466 this.fireEvent('exception', this, xhr);
31470 var file = this.renderPreview(response.data);
31472 this.files.push(file);
31476 this.fireEvent('afterupload', this, xhr);
31480 xhrOnError : function(xhr)
31482 Roo.log('xhr on error');
31484 var response = Roo.decode(xhr.responseText);
31491 process : function(file)
31493 if(this.fireEvent('process', this, file) !== false){
31494 if(this.editable && file.type.indexOf('image') != -1){
31495 this.fireEvent('edit', this, file);
31499 this.uploadStart(file, false);
31506 uploadStart : function(file, crop)
31508 this.xhr = new XMLHttpRequest();
31510 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31515 file.xhr = this.xhr;
31517 this.managerEl.createChild({
31519 cls : 'roo-document-manager-loading',
31523 tooltip : file.name,
31524 cls : 'roo-document-manager-thumb',
31525 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31531 this.xhr.open(this.method, this.url, true);
31534 "Accept": "application/json",
31535 "Cache-Control": "no-cache",
31536 "X-Requested-With": "XMLHttpRequest"
31539 for (var headerName in headers) {
31540 var headerValue = headers[headerName];
31542 this.xhr.setRequestHeader(headerName, headerValue);
31548 this.xhr.onload = function()
31550 _this.xhrOnLoad(_this.xhr);
31553 this.xhr.onerror = function()
31555 _this.xhrOnError(_this.xhr);
31558 var formData = new FormData();
31560 formData.append('returnHTML', 'NO');
31563 formData.append('crop', crop);
31566 formData.append(this.paramName, file, file.name);
31573 if(this.fireEvent('prepare', this, formData, options) != false){
31575 if(options.manually){
31579 this.xhr.send(formData);
31583 this.uploadCancel();
31586 uploadCancel : function()
31592 this.delegates = [];
31594 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31601 renderPreview : function(file)
31603 if(typeof(file.target) != 'undefined' && file.target){
31607 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31609 var previewEl = this.managerEl.createChild({
31611 cls : 'roo-document-manager-preview',
31615 tooltip : file[this.toolTipName],
31616 cls : 'roo-document-manager-thumb',
31617 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31622 html : '<i class="fa fa-times-circle"></i>'
31627 var close = previewEl.select('button.close', true).first();
31629 close.on('click', this.onRemove, this, file);
31631 file.target = previewEl;
31633 var image = previewEl.select('img', true).first();
31637 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31639 image.on('click', this.onClick, this, file);
31641 this.fireEvent('previewrendered', this, file);
31647 onPreviewLoad : function(file, image)
31649 if(typeof(file.target) == 'undefined' || !file.target){
31653 var width = image.dom.naturalWidth || image.dom.width;
31654 var height = image.dom.naturalHeight || image.dom.height;
31656 if(!this.previewResize) {
31660 if(width > height){
31661 file.target.addClass('wide');
31665 file.target.addClass('tall');
31670 uploadFromSource : function(file, crop)
31672 this.xhr = new XMLHttpRequest();
31674 this.managerEl.createChild({
31676 cls : 'roo-document-manager-loading',
31680 tooltip : file.name,
31681 cls : 'roo-document-manager-thumb',
31682 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31688 this.xhr.open(this.method, this.url, true);
31691 "Accept": "application/json",
31692 "Cache-Control": "no-cache",
31693 "X-Requested-With": "XMLHttpRequest"
31696 for (var headerName in headers) {
31697 var headerValue = headers[headerName];
31699 this.xhr.setRequestHeader(headerName, headerValue);
31705 this.xhr.onload = function()
31707 _this.xhrOnLoad(_this.xhr);
31710 this.xhr.onerror = function()
31712 _this.xhrOnError(_this.xhr);
31715 var formData = new FormData();
31717 formData.append('returnHTML', 'NO');
31719 formData.append('crop', crop);
31721 if(typeof(file.filename) != 'undefined'){
31722 formData.append('filename', file.filename);
31725 if(typeof(file.mimetype) != 'undefined'){
31726 formData.append('mimetype', file.mimetype);
31731 if(this.fireEvent('prepare', this, formData) != false){
31732 this.xhr.send(formData);
31742 * @class Roo.bootstrap.DocumentViewer
31743 * @extends Roo.bootstrap.Component
31744 * Bootstrap DocumentViewer class
31745 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31746 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31749 * Create a new DocumentViewer
31750 * @param {Object} config The config object
31753 Roo.bootstrap.DocumentViewer = function(config){
31754 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31759 * Fire after initEvent
31760 * @param {Roo.bootstrap.DocumentViewer} this
31766 * @param {Roo.bootstrap.DocumentViewer} this
31771 * Fire after download button
31772 * @param {Roo.bootstrap.DocumentViewer} this
31777 * Fire after trash button
31778 * @param {Roo.bootstrap.DocumentViewer} this
31785 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31787 showDownload : true,
31791 getAutoCreate : function()
31795 cls : 'roo-document-viewer',
31799 cls : 'roo-document-viewer-body',
31803 cls : 'roo-document-viewer-thumb',
31807 cls : 'roo-document-viewer-image'
31815 cls : 'roo-document-viewer-footer',
31818 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31822 cls : 'btn-group roo-document-viewer-download',
31826 cls : 'btn btn-default',
31827 html : '<i class="fa fa-download"></i>'
31833 cls : 'btn-group roo-document-viewer-trash',
31837 cls : 'btn btn-default',
31838 html : '<i class="fa fa-trash"></i>'
31851 initEvents : function()
31853 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31854 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31856 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31857 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31859 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31860 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31862 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31863 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31865 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31866 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31868 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31869 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31871 this.bodyEl.on('click', this.onClick, this);
31872 this.downloadBtn.on('click', this.onDownload, this);
31873 this.trashBtn.on('click', this.onTrash, this);
31875 this.downloadBtn.hide();
31876 this.trashBtn.hide();
31878 if(this.showDownload){
31879 this.downloadBtn.show();
31882 if(this.showTrash){
31883 this.trashBtn.show();
31886 if(!this.showDownload && !this.showTrash) {
31887 this.footerEl.hide();
31892 initial : function()
31894 this.fireEvent('initial', this);
31898 onClick : function(e)
31900 e.preventDefault();
31902 this.fireEvent('click', this);
31905 onDownload : function(e)
31907 e.preventDefault();
31909 this.fireEvent('download', this);
31912 onTrash : function(e)
31914 e.preventDefault();
31916 this.fireEvent('trash', this);
31928 * @class Roo.bootstrap.NavProgressBar
31929 * @extends Roo.bootstrap.Component
31930 * Bootstrap NavProgressBar class
31933 * Create a new nav progress bar
31934 * @param {Object} config The config object
31937 Roo.bootstrap.NavProgressBar = function(config){
31938 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31940 this.bullets = this.bullets || [];
31942 // Roo.bootstrap.NavProgressBar.register(this);
31946 * Fires when the active item changes
31947 * @param {Roo.bootstrap.NavProgressBar} this
31948 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31949 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31956 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31961 getAutoCreate : function()
31963 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31967 cls : 'roo-navigation-bar-group',
31971 cls : 'roo-navigation-top-bar'
31975 cls : 'roo-navigation-bullets-bar',
31979 cls : 'roo-navigation-bar'
31986 cls : 'roo-navigation-bottom-bar'
31996 initEvents: function()
32001 onRender : function(ct, position)
32003 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32005 if(this.bullets.length){
32006 Roo.each(this.bullets, function(b){
32015 addItem : function(cfg)
32017 var item = new Roo.bootstrap.NavProgressItem(cfg);
32019 item.parentId = this.id;
32020 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32023 var top = new Roo.bootstrap.Element({
32025 cls : 'roo-navigation-bar-text'
32028 var bottom = new Roo.bootstrap.Element({
32030 cls : 'roo-navigation-bar-text'
32033 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32034 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32036 var topText = new Roo.bootstrap.Element({
32038 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32041 var bottomText = new Roo.bootstrap.Element({
32043 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32046 topText.onRender(top.el, null);
32047 bottomText.onRender(bottom.el, null);
32050 item.bottomEl = bottom;
32053 this.barItems.push(item);
32058 getActive : function()
32060 var active = false;
32062 Roo.each(this.barItems, function(v){
32064 if (!v.isActive()) {
32076 setActiveItem : function(item)
32080 Roo.each(this.barItems, function(v){
32081 if (v.rid == item.rid) {
32085 if (v.isActive()) {
32086 v.setActive(false);
32091 item.setActive(true);
32093 this.fireEvent('changed', this, item, prev);
32096 getBarItem: function(rid)
32100 Roo.each(this.barItems, function(e) {
32101 if (e.rid != rid) {
32112 indexOfItem : function(item)
32116 Roo.each(this.barItems, function(v, i){
32118 if (v.rid != item.rid) {
32129 setActiveNext : function()
32131 var i = this.indexOfItem(this.getActive());
32133 if (i > this.barItems.length) {
32137 this.setActiveItem(this.barItems[i+1]);
32140 setActivePrev : function()
32142 var i = this.indexOfItem(this.getActive());
32148 this.setActiveItem(this.barItems[i-1]);
32151 format : function()
32153 if(!this.barItems.length){
32157 var width = 100 / this.barItems.length;
32159 Roo.each(this.barItems, function(i){
32160 i.el.setStyle('width', width + '%');
32161 i.topEl.el.setStyle('width', width + '%');
32162 i.bottomEl.el.setStyle('width', width + '%');
32171 * Nav Progress Item
32176 * @class Roo.bootstrap.NavProgressItem
32177 * @extends Roo.bootstrap.Component
32178 * Bootstrap NavProgressItem class
32179 * @cfg {String} rid the reference id
32180 * @cfg {Boolean} active (true|false) Is item active default false
32181 * @cfg {Boolean} disabled (true|false) Is item active default false
32182 * @cfg {String} html
32183 * @cfg {String} position (top|bottom) text position default bottom
32184 * @cfg {String} icon show icon instead of number
32187 * Create a new NavProgressItem
32188 * @param {Object} config The config object
32190 Roo.bootstrap.NavProgressItem = function(config){
32191 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32196 * The raw click event for the entire grid.
32197 * @param {Roo.bootstrap.NavProgressItem} this
32198 * @param {Roo.EventObject} e
32205 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32211 position : 'bottom',
32214 getAutoCreate : function()
32216 var iconCls = 'roo-navigation-bar-item-icon';
32218 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32222 cls: 'roo-navigation-bar-item',
32232 cfg.cls += ' active';
32235 cfg.cls += ' disabled';
32241 disable : function()
32243 this.setDisabled(true);
32246 enable : function()
32248 this.setDisabled(false);
32251 initEvents: function()
32253 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32255 this.iconEl.on('click', this.onClick, this);
32258 onClick : function(e)
32260 e.preventDefault();
32266 if(this.fireEvent('click', this, e) === false){
32270 this.parent().setActiveItem(this);
32273 isActive: function ()
32275 return this.active;
32278 setActive : function(state)
32280 if(this.active == state){
32284 this.active = state;
32287 this.el.addClass('active');
32291 this.el.removeClass('active');
32296 setDisabled : function(state)
32298 if(this.disabled == state){
32302 this.disabled = state;
32305 this.el.addClass('disabled');
32309 this.el.removeClass('disabled');
32312 tooltipEl : function()
32314 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32327 * @class Roo.bootstrap.FieldLabel
32328 * @extends Roo.bootstrap.Component
32329 * Bootstrap FieldLabel class
32330 * @cfg {String} html contents of the element
32331 * @cfg {String} tag tag of the element default label
32332 * @cfg {String} cls class of the element
32333 * @cfg {String} target label target
32334 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32335 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32336 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32337 * @cfg {String} iconTooltip default "This field is required"
32338 * @cfg {String} indicatorpos (left|right) default left
32341 * Create a new FieldLabel
32342 * @param {Object} config The config object
32345 Roo.bootstrap.FieldLabel = function(config){
32346 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32351 * Fires after the field has been marked as invalid.
32352 * @param {Roo.form.FieldLabel} this
32353 * @param {String} msg The validation message
32358 * Fires after the field has been validated with no errors.
32359 * @param {Roo.form.FieldLabel} this
32365 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32372 invalidClass : 'has-warning',
32373 validClass : 'has-success',
32374 iconTooltip : 'This field is required',
32375 indicatorpos : 'left',
32377 getAutoCreate : function(){
32380 if (!this.allowBlank) {
32386 cls : 'roo-bootstrap-field-label ' + this.cls,
32391 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32392 tooltip : this.iconTooltip
32401 if(this.indicatorpos == 'right'){
32404 cls : 'roo-bootstrap-field-label ' + this.cls,
32413 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32414 tooltip : this.iconTooltip
32423 initEvents: function()
32425 Roo.bootstrap.Element.superclass.initEvents.call(this);
32427 this.indicator = this.indicatorEl();
32429 if(this.indicator){
32430 this.indicator.removeClass('visible');
32431 this.indicator.addClass('invisible');
32434 Roo.bootstrap.FieldLabel.register(this);
32437 indicatorEl : function()
32439 var indicator = this.el.select('i.roo-required-indicator',true).first();
32450 * Mark this field as valid
32452 markValid : function()
32454 if(this.indicator){
32455 this.indicator.removeClass('visible');
32456 this.indicator.addClass('invisible');
32458 if (Roo.bootstrap.version == 3) {
32459 this.el.removeClass(this.invalidClass);
32460 this.el.addClass(this.validClass);
32462 this.el.removeClass('is-invalid');
32463 this.el.addClass('is-valid');
32467 this.fireEvent('valid', this);
32471 * Mark this field as invalid
32472 * @param {String} msg The validation message
32474 markInvalid : function(msg)
32476 if(this.indicator){
32477 this.indicator.removeClass('invisible');
32478 this.indicator.addClass('visible');
32480 if (Roo.bootstrap.version == 3) {
32481 this.el.removeClass(this.validClass);
32482 this.el.addClass(this.invalidClass);
32484 this.el.removeClass('is-valid');
32485 this.el.addClass('is-invalid');
32489 this.fireEvent('invalid', this, msg);
32495 Roo.apply(Roo.bootstrap.FieldLabel, {
32500 * register a FieldLabel Group
32501 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32503 register : function(label)
32505 if(this.groups.hasOwnProperty(label.target)){
32509 this.groups[label.target] = label;
32513 * fetch a FieldLabel Group based on the target
32514 * @param {string} target
32515 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32517 get: function(target) {
32518 if (typeof(this.groups[target]) == 'undefined') {
32522 return this.groups[target] ;
32531 * page DateSplitField.
32537 * @class Roo.bootstrap.DateSplitField
32538 * @extends Roo.bootstrap.Component
32539 * Bootstrap DateSplitField class
32540 * @cfg {string} fieldLabel - the label associated
32541 * @cfg {Number} labelWidth set the width of label (0-12)
32542 * @cfg {String} labelAlign (top|left)
32543 * @cfg {Boolean} dayAllowBlank (true|false) default false
32544 * @cfg {Boolean} monthAllowBlank (true|false) default false
32545 * @cfg {Boolean} yearAllowBlank (true|false) default false
32546 * @cfg {string} dayPlaceholder
32547 * @cfg {string} monthPlaceholder
32548 * @cfg {string} yearPlaceholder
32549 * @cfg {string} dayFormat default 'd'
32550 * @cfg {string} monthFormat default 'm'
32551 * @cfg {string} yearFormat default 'Y'
32552 * @cfg {Number} labellg set the width of label (1-12)
32553 * @cfg {Number} labelmd set the width of label (1-12)
32554 * @cfg {Number} labelsm set the width of label (1-12)
32555 * @cfg {Number} labelxs set the width of label (1-12)
32559 * Create a new DateSplitField
32560 * @param {Object} config The config object
32563 Roo.bootstrap.DateSplitField = function(config){
32564 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32570 * getting the data of years
32571 * @param {Roo.bootstrap.DateSplitField} this
32572 * @param {Object} years
32577 * getting the data of days
32578 * @param {Roo.bootstrap.DateSplitField} this
32579 * @param {Object} days
32584 * Fires after the field has been marked as invalid.
32585 * @param {Roo.form.Field} this
32586 * @param {String} msg The validation message
32591 * Fires after the field has been validated with no errors.
32592 * @param {Roo.form.Field} this
32598 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32601 labelAlign : 'top',
32603 dayAllowBlank : false,
32604 monthAllowBlank : false,
32605 yearAllowBlank : false,
32606 dayPlaceholder : '',
32607 monthPlaceholder : '',
32608 yearPlaceholder : '',
32612 isFormField : true,
32618 getAutoCreate : function()
32622 cls : 'row roo-date-split-field-group',
32627 cls : 'form-hidden-field roo-date-split-field-group-value',
32633 var labelCls = 'col-md-12';
32634 var contentCls = 'col-md-4';
32636 if(this.fieldLabel){
32640 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32644 html : this.fieldLabel
32649 if(this.labelAlign == 'left'){
32651 if(this.labelWidth > 12){
32652 label.style = "width: " + this.labelWidth + 'px';
32655 if(this.labelWidth < 13 && this.labelmd == 0){
32656 this.labelmd = this.labelWidth;
32659 if(this.labellg > 0){
32660 labelCls = ' col-lg-' + this.labellg;
32661 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32664 if(this.labelmd > 0){
32665 labelCls = ' col-md-' + this.labelmd;
32666 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32669 if(this.labelsm > 0){
32670 labelCls = ' col-sm-' + this.labelsm;
32671 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32674 if(this.labelxs > 0){
32675 labelCls = ' col-xs-' + this.labelxs;
32676 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32680 label.cls += ' ' + labelCls;
32682 cfg.cn.push(label);
32685 Roo.each(['day', 'month', 'year'], function(t){
32688 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32695 inputEl: function ()
32697 return this.el.select('.roo-date-split-field-group-value', true).first();
32700 onRender : function(ct, position)
32704 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32706 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32708 this.dayField = new Roo.bootstrap.ComboBox({
32709 allowBlank : this.dayAllowBlank,
32710 alwaysQuery : true,
32711 displayField : 'value',
32714 forceSelection : true,
32716 placeholder : this.dayPlaceholder,
32717 selectOnFocus : true,
32718 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32719 triggerAction : 'all',
32721 valueField : 'value',
32722 store : new Roo.data.SimpleStore({
32723 data : (function() {
32725 _this.fireEvent('days', _this, days);
32728 fields : [ 'value' ]
32731 select : function (_self, record, index)
32733 _this.setValue(_this.getValue());
32738 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32740 this.monthField = new Roo.bootstrap.MonthField({
32741 after : '<i class=\"fa fa-calendar\"></i>',
32742 allowBlank : this.monthAllowBlank,
32743 placeholder : this.monthPlaceholder,
32746 render : function (_self)
32748 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32749 e.preventDefault();
32753 select : function (_self, oldvalue, newvalue)
32755 _this.setValue(_this.getValue());
32760 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32762 this.yearField = new Roo.bootstrap.ComboBox({
32763 allowBlank : this.yearAllowBlank,
32764 alwaysQuery : true,
32765 displayField : 'value',
32768 forceSelection : true,
32770 placeholder : this.yearPlaceholder,
32771 selectOnFocus : true,
32772 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32773 triggerAction : 'all',
32775 valueField : 'value',
32776 store : new Roo.data.SimpleStore({
32777 data : (function() {
32779 _this.fireEvent('years', _this, years);
32782 fields : [ 'value' ]
32785 select : function (_self, record, index)
32787 _this.setValue(_this.getValue());
32792 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32795 setValue : function(v, format)
32797 this.inputEl.dom.value = v;
32799 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32801 var d = Date.parseDate(v, f);
32808 this.setDay(d.format(this.dayFormat));
32809 this.setMonth(d.format(this.monthFormat));
32810 this.setYear(d.format(this.yearFormat));
32817 setDay : function(v)
32819 this.dayField.setValue(v);
32820 this.inputEl.dom.value = this.getValue();
32825 setMonth : function(v)
32827 this.monthField.setValue(v, true);
32828 this.inputEl.dom.value = this.getValue();
32833 setYear : function(v)
32835 this.yearField.setValue(v);
32836 this.inputEl.dom.value = this.getValue();
32841 getDay : function()
32843 return this.dayField.getValue();
32846 getMonth : function()
32848 return this.monthField.getValue();
32851 getYear : function()
32853 return this.yearField.getValue();
32856 getValue : function()
32858 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32860 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32870 this.inputEl.dom.value = '';
32875 validate : function()
32877 var d = this.dayField.validate();
32878 var m = this.monthField.validate();
32879 var y = this.yearField.validate();
32884 (!this.dayAllowBlank && !d) ||
32885 (!this.monthAllowBlank && !m) ||
32886 (!this.yearAllowBlank && !y)
32891 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32900 this.markInvalid();
32905 markValid : function()
32908 var label = this.el.select('label', true).first();
32909 var icon = this.el.select('i.fa-star', true).first();
32915 this.fireEvent('valid', this);
32919 * Mark this field as invalid
32920 * @param {String} msg The validation message
32922 markInvalid : function(msg)
32925 var label = this.el.select('label', true).first();
32926 var icon = this.el.select('i.fa-star', true).first();
32928 if(label && !icon){
32929 this.el.select('.roo-date-split-field-label', true).createChild({
32931 cls : 'text-danger fa fa-lg fa-star',
32932 tooltip : 'This field is required',
32933 style : 'margin-right:5px;'
32937 this.fireEvent('invalid', this, msg);
32940 clearInvalid : function()
32942 var label = this.el.select('label', true).first();
32943 var icon = this.el.select('i.fa-star', true).first();
32949 this.fireEvent('valid', this);
32952 getName: function()
32962 * http://masonry.desandro.com
32964 * The idea is to render all the bricks based on vertical width...
32966 * The original code extends 'outlayer' - we might need to use that....
32972 * @class Roo.bootstrap.LayoutMasonry
32973 * @extends Roo.bootstrap.Component
32974 * Bootstrap Layout Masonry class
32977 * Create a new Element
32978 * @param {Object} config The config object
32981 Roo.bootstrap.LayoutMasonry = function(config){
32983 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32987 Roo.bootstrap.LayoutMasonry.register(this);
32993 * Fire after layout the items
32994 * @param {Roo.bootstrap.LayoutMasonry} this
32995 * @param {Roo.EventObject} e
33002 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33005 * @cfg {Boolean} isLayoutInstant = no animation?
33007 isLayoutInstant : false, // needed?
33010 * @cfg {Number} boxWidth width of the columns
33015 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33020 * @cfg {Number} padWidth padding below box..
33025 * @cfg {Number} gutter gutter width..
33030 * @cfg {Number} maxCols maximum number of columns
33036 * @cfg {Boolean} isAutoInitial defalut true
33038 isAutoInitial : true,
33043 * @cfg {Boolean} isHorizontal defalut false
33045 isHorizontal : false,
33047 currentSize : null,
33053 bricks: null, //CompositeElement
33057 _isLayoutInited : false,
33059 // isAlternative : false, // only use for vertical layout...
33062 * @cfg {Number} alternativePadWidth padding below box..
33064 alternativePadWidth : 50,
33066 selectedBrick : [],
33068 getAutoCreate : function(){
33070 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33074 cls: 'blog-masonary-wrapper ' + this.cls,
33076 cls : 'mas-boxes masonary'
33083 getChildContainer: function( )
33085 if (this.boxesEl) {
33086 return this.boxesEl;
33089 this.boxesEl = this.el.select('.mas-boxes').first();
33091 return this.boxesEl;
33095 initEvents : function()
33099 if(this.isAutoInitial){
33100 Roo.log('hook children rendered');
33101 this.on('childrenrendered', function() {
33102 Roo.log('children rendered');
33108 initial : function()
33110 this.selectedBrick = [];
33112 this.currentSize = this.el.getBox(true);
33114 Roo.EventManager.onWindowResize(this.resize, this);
33116 if(!this.isAutoInitial){
33124 //this.layout.defer(500,this);
33128 resize : function()
33130 var cs = this.el.getBox(true);
33133 this.currentSize.width == cs.width &&
33134 this.currentSize.x == cs.x &&
33135 this.currentSize.height == cs.height &&
33136 this.currentSize.y == cs.y
33138 Roo.log("no change in with or X or Y");
33142 this.currentSize = cs;
33148 layout : function()
33150 this._resetLayout();
33152 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33154 this.layoutItems( isInstant );
33156 this._isLayoutInited = true;
33158 this.fireEvent('layout', this);
33162 _resetLayout : function()
33164 if(this.isHorizontal){
33165 this.horizontalMeasureColumns();
33169 this.verticalMeasureColumns();
33173 verticalMeasureColumns : function()
33175 this.getContainerWidth();
33177 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33178 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33182 var boxWidth = this.boxWidth + this.padWidth;
33184 if(this.containerWidth < this.boxWidth){
33185 boxWidth = this.containerWidth
33188 var containerWidth = this.containerWidth;
33190 var cols = Math.floor(containerWidth / boxWidth);
33192 this.cols = Math.max( cols, 1 );
33194 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33196 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33198 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33200 this.colWidth = boxWidth + avail - this.padWidth;
33202 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33203 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33206 horizontalMeasureColumns : function()
33208 this.getContainerWidth();
33210 var boxWidth = this.boxWidth;
33212 if(this.containerWidth < boxWidth){
33213 boxWidth = this.containerWidth;
33216 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33218 this.el.setHeight(boxWidth);
33222 getContainerWidth : function()
33224 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33227 layoutItems : function( isInstant )
33229 Roo.log(this.bricks);
33231 var items = Roo.apply([], this.bricks);
33233 if(this.isHorizontal){
33234 this._horizontalLayoutItems( items , isInstant );
33238 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33239 // this._verticalAlternativeLayoutItems( items , isInstant );
33243 this._verticalLayoutItems( items , isInstant );
33247 _verticalLayoutItems : function ( items , isInstant)
33249 if ( !items || !items.length ) {
33254 ['xs', 'xs', 'xs', 'tall'],
33255 ['xs', 'xs', 'tall'],
33256 ['xs', 'xs', 'sm'],
33257 ['xs', 'xs', 'xs'],
33263 ['sm', 'xs', 'xs'],
33267 ['tall', 'xs', 'xs', 'xs'],
33268 ['tall', 'xs', 'xs'],
33280 Roo.each(items, function(item, k){
33282 switch (item.size) {
33283 // these layouts take up a full box,
33294 boxes.push([item]);
33317 var filterPattern = function(box, length)
33325 var pattern = box.slice(0, length);
33329 Roo.each(pattern, function(i){
33330 format.push(i.size);
33333 Roo.each(standard, function(s){
33335 if(String(s) != String(format)){
33344 if(!match && length == 1){
33349 filterPattern(box, length - 1);
33353 queue.push(pattern);
33355 box = box.slice(length, box.length);
33357 filterPattern(box, 4);
33363 Roo.each(boxes, function(box, k){
33369 if(box.length == 1){
33374 filterPattern(box, 4);
33378 this._processVerticalLayoutQueue( queue, isInstant );
33382 // _verticalAlternativeLayoutItems : function( items , isInstant )
33384 // if ( !items || !items.length ) {
33388 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33392 _horizontalLayoutItems : function ( items , isInstant)
33394 if ( !items || !items.length || items.length < 3) {
33400 var eItems = items.slice(0, 3);
33402 items = items.slice(3, items.length);
33405 ['xs', 'xs', 'xs', 'wide'],
33406 ['xs', 'xs', 'wide'],
33407 ['xs', 'xs', 'sm'],
33408 ['xs', 'xs', 'xs'],
33414 ['sm', 'xs', 'xs'],
33418 ['wide', 'xs', 'xs', 'xs'],
33419 ['wide', 'xs', 'xs'],
33432 Roo.each(items, function(item, k){
33434 switch (item.size) {
33445 boxes.push([item]);
33469 var filterPattern = function(box, length)
33477 var pattern = box.slice(0, length);
33481 Roo.each(pattern, function(i){
33482 format.push(i.size);
33485 Roo.each(standard, function(s){
33487 if(String(s) != String(format)){
33496 if(!match && length == 1){
33501 filterPattern(box, length - 1);
33505 queue.push(pattern);
33507 box = box.slice(length, box.length);
33509 filterPattern(box, 4);
33515 Roo.each(boxes, function(box, k){
33521 if(box.length == 1){
33526 filterPattern(box, 4);
33533 var pos = this.el.getBox(true);
33537 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33539 var hit_end = false;
33541 Roo.each(queue, function(box){
33545 Roo.each(box, function(b){
33547 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33557 Roo.each(box, function(b){
33559 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33562 mx = Math.max(mx, b.x);
33566 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33570 Roo.each(box, function(b){
33572 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33586 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33589 /** Sets position of item in DOM
33590 * @param {Element} item
33591 * @param {Number} x - horizontal position
33592 * @param {Number} y - vertical position
33593 * @param {Boolean} isInstant - disables transitions
33595 _processVerticalLayoutQueue : function( queue, isInstant )
33597 var pos = this.el.getBox(true);
33602 for (var i = 0; i < this.cols; i++){
33606 Roo.each(queue, function(box, k){
33608 var col = k % this.cols;
33610 Roo.each(box, function(b,kk){
33612 b.el.position('absolute');
33614 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33615 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33617 if(b.size == 'md-left' || b.size == 'md-right'){
33618 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33619 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33622 b.el.setWidth(width);
33623 b.el.setHeight(height);
33625 b.el.select('iframe',true).setSize(width,height);
33629 for (var i = 0; i < this.cols; i++){
33631 if(maxY[i] < maxY[col]){
33636 col = Math.min(col, i);
33640 x = pos.x + col * (this.colWidth + this.padWidth);
33644 var positions = [];
33646 switch (box.length){
33648 positions = this.getVerticalOneBoxColPositions(x, y, box);
33651 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33654 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33657 positions = this.getVerticalFourBoxColPositions(x, y, box);
33663 Roo.each(box, function(b,kk){
33665 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33667 var sz = b.el.getSize();
33669 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33677 for (var i = 0; i < this.cols; i++){
33678 mY = Math.max(mY, maxY[i]);
33681 this.el.setHeight(mY - pos.y);
33685 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33687 // var pos = this.el.getBox(true);
33690 // var maxX = pos.right;
33692 // var maxHeight = 0;
33694 // Roo.each(items, function(item, k){
33698 // item.el.position('absolute');
33700 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33702 // item.el.setWidth(width);
33704 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33706 // item.el.setHeight(height);
33709 // item.el.setXY([x, y], isInstant ? false : true);
33711 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33714 // y = y + height + this.alternativePadWidth;
33716 // maxHeight = maxHeight + height + this.alternativePadWidth;
33720 // this.el.setHeight(maxHeight);
33724 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33726 var pos = this.el.getBox(true);
33731 var maxX = pos.right;
33733 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33735 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33737 Roo.each(queue, function(box, k){
33739 Roo.each(box, function(b, kk){
33741 b.el.position('absolute');
33743 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33744 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33746 if(b.size == 'md-left' || b.size == 'md-right'){
33747 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33748 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33751 b.el.setWidth(width);
33752 b.el.setHeight(height);
33760 var positions = [];
33762 switch (box.length){
33764 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33767 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33770 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33773 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33779 Roo.each(box, function(b,kk){
33781 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33783 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33791 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33793 Roo.each(eItems, function(b,k){
33795 b.size = (k == 0) ? 'sm' : 'xs';
33796 b.x = (k == 0) ? 2 : 1;
33797 b.y = (k == 0) ? 2 : 1;
33799 b.el.position('absolute');
33801 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33803 b.el.setWidth(width);
33805 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33807 b.el.setHeight(height);
33811 var positions = [];
33814 x : maxX - this.unitWidth * 2 - this.gutter,
33819 x : maxX - this.unitWidth,
33820 y : minY + (this.unitWidth + this.gutter) * 2
33824 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33828 Roo.each(eItems, function(b,k){
33830 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33836 getVerticalOneBoxColPositions : function(x, y, box)
33840 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33842 if(box[0].size == 'md-left'){
33846 if(box[0].size == 'md-right'){
33851 x : x + (this.unitWidth + this.gutter) * rand,
33858 getVerticalTwoBoxColPositions : function(x, y, box)
33862 if(box[0].size == 'xs'){
33866 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33870 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33884 x : x + (this.unitWidth + this.gutter) * 2,
33885 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33892 getVerticalThreeBoxColPositions : function(x, y, box)
33896 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33904 x : x + (this.unitWidth + this.gutter) * 1,
33909 x : x + (this.unitWidth + this.gutter) * 2,
33917 if(box[0].size == 'xs' && box[1].size == 'xs'){
33926 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33930 x : x + (this.unitWidth + this.gutter) * 1,
33944 x : x + (this.unitWidth + this.gutter) * 2,
33949 x : x + (this.unitWidth + this.gutter) * 2,
33950 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33957 getVerticalFourBoxColPositions : function(x, y, box)
33961 if(box[0].size == 'xs'){
33970 y : y + (this.unitHeight + this.gutter) * 1
33975 y : y + (this.unitHeight + this.gutter) * 2
33979 x : x + (this.unitWidth + this.gutter) * 1,
33993 x : x + (this.unitWidth + this.gutter) * 2,
33998 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33999 y : y + (this.unitHeight + this.gutter) * 1
34003 x : x + (this.unitWidth + this.gutter) * 2,
34004 y : y + (this.unitWidth + this.gutter) * 2
34011 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34015 if(box[0].size == 'md-left'){
34017 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34024 if(box[0].size == 'md-right'){
34026 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34027 y : minY + (this.unitWidth + this.gutter) * 1
34033 var rand = Math.floor(Math.random() * (4 - box[0].y));
34036 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34037 y : minY + (this.unitWidth + this.gutter) * rand
34044 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34048 if(box[0].size == 'xs'){
34051 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34056 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34057 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34065 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34070 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34071 y : minY + (this.unitWidth + this.gutter) * 2
34078 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34082 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34085 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34090 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34091 y : minY + (this.unitWidth + this.gutter) * 1
34095 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34096 y : minY + (this.unitWidth + this.gutter) * 2
34103 if(box[0].size == 'xs' && box[1].size == 'xs'){
34106 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34111 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34116 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34117 y : minY + (this.unitWidth + this.gutter) * 1
34125 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34130 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34131 y : minY + (this.unitWidth + this.gutter) * 2
34135 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34136 y : minY + (this.unitWidth + this.gutter) * 2
34143 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34147 if(box[0].size == 'xs'){
34150 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34155 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34160 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),
34165 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34166 y : minY + (this.unitWidth + this.gutter) * 1
34174 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34179 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34180 y : minY + (this.unitWidth + this.gutter) * 2
34184 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34185 y : minY + (this.unitWidth + this.gutter) * 2
34189 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),
34190 y : minY + (this.unitWidth + this.gutter) * 2
34198 * remove a Masonry Brick
34199 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34201 removeBrick : function(brick_id)
34207 for (var i = 0; i<this.bricks.length; i++) {
34208 if (this.bricks[i].id == brick_id) {
34209 this.bricks.splice(i,1);
34210 this.el.dom.removeChild(Roo.get(brick_id).dom);
34217 * adds a Masonry Brick
34218 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34220 addBrick : function(cfg)
34222 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34223 //this.register(cn);
34224 cn.parentId = this.id;
34225 cn.render(this.el);
34230 * register a Masonry Brick
34231 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34234 register : function(brick)
34236 this.bricks.push(brick);
34237 brick.masonryId = this.id;
34241 * clear all the Masonry Brick
34243 clearAll : function()
34246 //this.getChildContainer().dom.innerHTML = "";
34247 this.el.dom.innerHTML = '';
34250 getSelected : function()
34252 if (!this.selectedBrick) {
34256 return this.selectedBrick;
34260 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34264 * register a Masonry Layout
34265 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34268 register : function(layout)
34270 this.groups[layout.id] = layout;
34273 * fetch a Masonry Layout based on the masonry layout ID
34274 * @param {string} the masonry layout to add
34275 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34278 get: function(layout_id) {
34279 if (typeof(this.groups[layout_id]) == 'undefined') {
34282 return this.groups[layout_id] ;
34294 * http://masonry.desandro.com
34296 * The idea is to render all the bricks based on vertical width...
34298 * The original code extends 'outlayer' - we might need to use that....
34304 * @class Roo.bootstrap.LayoutMasonryAuto
34305 * @extends Roo.bootstrap.Component
34306 * Bootstrap Layout Masonry class
34309 * Create a new Element
34310 * @param {Object} config The config object
34313 Roo.bootstrap.LayoutMasonryAuto = function(config){
34314 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34317 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34320 * @cfg {Boolean} isFitWidth - resize the width..
34322 isFitWidth : false, // options..
34324 * @cfg {Boolean} isOriginLeft = left align?
34326 isOriginLeft : true,
34328 * @cfg {Boolean} isOriginTop = top align?
34330 isOriginTop : false,
34332 * @cfg {Boolean} isLayoutInstant = no animation?
34334 isLayoutInstant : false, // needed?
34336 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34338 isResizingContainer : true,
34340 * @cfg {Number} columnWidth width of the columns
34346 * @cfg {Number} maxCols maximum number of columns
34351 * @cfg {Number} padHeight padding below box..
34357 * @cfg {Boolean} isAutoInitial defalut true
34360 isAutoInitial : true,
34366 initialColumnWidth : 0,
34367 currentSize : null,
34369 colYs : null, // array.
34376 bricks: null, //CompositeElement
34377 cols : 0, // array?
34378 // element : null, // wrapped now this.el
34379 _isLayoutInited : null,
34382 getAutoCreate : function(){
34386 cls: 'blog-masonary-wrapper ' + this.cls,
34388 cls : 'mas-boxes masonary'
34395 getChildContainer: function( )
34397 if (this.boxesEl) {
34398 return this.boxesEl;
34401 this.boxesEl = this.el.select('.mas-boxes').first();
34403 return this.boxesEl;
34407 initEvents : function()
34411 if(this.isAutoInitial){
34412 Roo.log('hook children rendered');
34413 this.on('childrenrendered', function() {
34414 Roo.log('children rendered');
34421 initial : function()
34423 this.reloadItems();
34425 this.currentSize = this.el.getBox(true);
34427 /// was window resize... - let's see if this works..
34428 Roo.EventManager.onWindowResize(this.resize, this);
34430 if(!this.isAutoInitial){
34435 this.layout.defer(500,this);
34438 reloadItems: function()
34440 this.bricks = this.el.select('.masonry-brick', true);
34442 this.bricks.each(function(b) {
34443 //Roo.log(b.getSize());
34444 if (!b.attr('originalwidth')) {
34445 b.attr('originalwidth', b.getSize().width);
34450 Roo.log(this.bricks.elements.length);
34453 resize : function()
34456 var cs = this.el.getBox(true);
34458 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34459 Roo.log("no change in with or X");
34462 this.currentSize = cs;
34466 layout : function()
34469 this._resetLayout();
34470 //this._manageStamps();
34472 // don't animate first layout
34473 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34474 this.layoutItems( isInstant );
34476 // flag for initalized
34477 this._isLayoutInited = true;
34480 layoutItems : function( isInstant )
34482 //var items = this._getItemsForLayout( this.items );
34483 // original code supports filtering layout items.. we just ignore it..
34485 this._layoutItems( this.bricks , isInstant );
34487 this._postLayout();
34489 _layoutItems : function ( items , isInstant)
34491 //this.fireEvent( 'layout', this, items );
34494 if ( !items || !items.elements.length ) {
34495 // no items, emit event with empty array
34500 items.each(function(item) {
34501 Roo.log("layout item");
34503 // get x/y object from method
34504 var position = this._getItemLayoutPosition( item );
34506 position.item = item;
34507 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34508 queue.push( position );
34511 this._processLayoutQueue( queue );
34513 /** Sets position of item in DOM
34514 * @param {Element} item
34515 * @param {Number} x - horizontal position
34516 * @param {Number} y - vertical position
34517 * @param {Boolean} isInstant - disables transitions
34519 _processLayoutQueue : function( queue )
34521 for ( var i=0, len = queue.length; i < len; i++ ) {
34522 var obj = queue[i];
34523 obj.item.position('absolute');
34524 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34530 * Any logic you want to do after each layout,
34531 * i.e. size the container
34533 _postLayout : function()
34535 this.resizeContainer();
34538 resizeContainer : function()
34540 if ( !this.isResizingContainer ) {
34543 var size = this._getContainerSize();
34545 this.el.setSize(size.width,size.height);
34546 this.boxesEl.setSize(size.width,size.height);
34552 _resetLayout : function()
34554 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34555 this.colWidth = this.el.getWidth();
34556 //this.gutter = this.el.getWidth();
34558 this.measureColumns();
34564 this.colYs.push( 0 );
34570 measureColumns : function()
34572 this.getContainerWidth();
34573 // if columnWidth is 0, default to outerWidth of first item
34574 if ( !this.columnWidth ) {
34575 var firstItem = this.bricks.first();
34576 Roo.log(firstItem);
34577 this.columnWidth = this.containerWidth;
34578 if (firstItem && firstItem.attr('originalwidth') ) {
34579 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34581 // columnWidth fall back to item of first element
34582 Roo.log("set column width?");
34583 this.initialColumnWidth = this.columnWidth ;
34585 // if first elem has no width, default to size of container
34590 if (this.initialColumnWidth) {
34591 this.columnWidth = this.initialColumnWidth;
34596 // column width is fixed at the top - however if container width get's smaller we should
34599 // this bit calcs how man columns..
34601 var columnWidth = this.columnWidth += this.gutter;
34603 // calculate columns
34604 var containerWidth = this.containerWidth + this.gutter;
34606 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34607 // fix rounding errors, typically with gutters
34608 var excess = columnWidth - containerWidth % columnWidth;
34611 // if overshoot is less than a pixel, round up, otherwise floor it
34612 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34613 cols = Math[ mathMethod ]( cols );
34614 this.cols = Math.max( cols, 1 );
34615 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34617 // padding positioning..
34618 var totalColWidth = this.cols * this.columnWidth;
34619 var padavail = this.containerWidth - totalColWidth;
34620 // so for 2 columns - we need 3 'pads'
34622 var padNeeded = (1+this.cols) * this.padWidth;
34624 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34626 this.columnWidth += padExtra
34627 //this.padWidth = Math.floor(padavail / ( this.cols));
34629 // adjust colum width so that padding is fixed??
34631 // we have 3 columns ... total = width * 3
34632 // we have X left over... that should be used by
34634 //if (this.expandC) {
34642 getContainerWidth : function()
34644 /* // container is parent if fit width
34645 var container = this.isFitWidth ? this.element.parentNode : this.element;
34646 // check that this.size and size are there
34647 // IE8 triggers resize on body size change, so they might not be
34649 var size = getSize( container ); //FIXME
34650 this.containerWidth = size && size.innerWidth; //FIXME
34653 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34657 _getItemLayoutPosition : function( item ) // what is item?
34659 // we resize the item to our columnWidth..
34661 item.setWidth(this.columnWidth);
34662 item.autoBoxAdjust = false;
34664 var sz = item.getSize();
34666 // how many columns does this brick span
34667 var remainder = this.containerWidth % this.columnWidth;
34669 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34670 // round if off by 1 pixel, otherwise use ceil
34671 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34672 colSpan = Math.min( colSpan, this.cols );
34674 // normally this should be '1' as we dont' currently allow multi width columns..
34676 var colGroup = this._getColGroup( colSpan );
34677 // get the minimum Y value from the columns
34678 var minimumY = Math.min.apply( Math, colGroup );
34679 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34681 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34683 // position the brick
34685 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34686 y: this.currentSize.y + minimumY + this.padHeight
34690 // apply setHeight to necessary columns
34691 var setHeight = minimumY + sz.height + this.padHeight;
34692 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34694 var setSpan = this.cols + 1 - colGroup.length;
34695 for ( var i = 0; i < setSpan; i++ ) {
34696 this.colYs[ shortColIndex + i ] = setHeight ;
34703 * @param {Number} colSpan - number of columns the element spans
34704 * @returns {Array} colGroup
34706 _getColGroup : function( colSpan )
34708 if ( colSpan < 2 ) {
34709 // if brick spans only one column, use all the column Ys
34714 // how many different places could this brick fit horizontally
34715 var groupCount = this.cols + 1 - colSpan;
34716 // for each group potential horizontal position
34717 for ( var i = 0; i < groupCount; i++ ) {
34718 // make an array of colY values for that one group
34719 var groupColYs = this.colYs.slice( i, i + colSpan );
34720 // and get the max value of the array
34721 colGroup[i] = Math.max.apply( Math, groupColYs );
34726 _manageStamp : function( stamp )
34728 var stampSize = stamp.getSize();
34729 var offset = stamp.getBox();
34730 // get the columns that this stamp affects
34731 var firstX = this.isOriginLeft ? offset.x : offset.right;
34732 var lastX = firstX + stampSize.width;
34733 var firstCol = Math.floor( firstX / this.columnWidth );
34734 firstCol = Math.max( 0, firstCol );
34736 var lastCol = Math.floor( lastX / this.columnWidth );
34737 // lastCol should not go over if multiple of columnWidth #425
34738 lastCol -= lastX % this.columnWidth ? 0 : 1;
34739 lastCol = Math.min( this.cols - 1, lastCol );
34741 // set colYs to bottom of the stamp
34742 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34745 for ( var i = firstCol; i <= lastCol; i++ ) {
34746 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34751 _getContainerSize : function()
34753 this.maxY = Math.max.apply( Math, this.colYs );
34758 if ( this.isFitWidth ) {
34759 size.width = this._getContainerFitWidth();
34765 _getContainerFitWidth : function()
34767 var unusedCols = 0;
34768 // count unused columns
34771 if ( this.colYs[i] !== 0 ) {
34776 // fit container to columns that have been used
34777 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34780 needsResizeLayout : function()
34782 var previousWidth = this.containerWidth;
34783 this.getContainerWidth();
34784 return previousWidth !== this.containerWidth;
34799 * @class Roo.bootstrap.MasonryBrick
34800 * @extends Roo.bootstrap.Component
34801 * Bootstrap MasonryBrick class
34804 * Create a new MasonryBrick
34805 * @param {Object} config The config object
34808 Roo.bootstrap.MasonryBrick = function(config){
34810 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34812 Roo.bootstrap.MasonryBrick.register(this);
34818 * When a MasonryBrick is clcik
34819 * @param {Roo.bootstrap.MasonryBrick} this
34820 * @param {Roo.EventObject} e
34826 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34829 * @cfg {String} title
34833 * @cfg {String} html
34837 * @cfg {String} bgimage
34841 * @cfg {String} videourl
34845 * @cfg {String} cls
34849 * @cfg {String} href
34853 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34858 * @cfg {String} placetitle (center|bottom)
34863 * @cfg {Boolean} isFitContainer defalut true
34865 isFitContainer : true,
34868 * @cfg {Boolean} preventDefault defalut false
34870 preventDefault : false,
34873 * @cfg {Boolean} inverse defalut false
34875 maskInverse : false,
34877 getAutoCreate : function()
34879 if(!this.isFitContainer){
34880 return this.getSplitAutoCreate();
34883 var cls = 'masonry-brick masonry-brick-full';
34885 if(this.href.length){
34886 cls += ' masonry-brick-link';
34889 if(this.bgimage.length){
34890 cls += ' masonry-brick-image';
34893 if(this.maskInverse){
34894 cls += ' mask-inverse';
34897 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34898 cls += ' enable-mask';
34902 cls += ' masonry-' + this.size + '-brick';
34905 if(this.placetitle.length){
34907 switch (this.placetitle) {
34909 cls += ' masonry-center-title';
34912 cls += ' masonry-bottom-title';
34919 if(!this.html.length && !this.bgimage.length){
34920 cls += ' masonry-center-title';
34923 if(!this.html.length && this.bgimage.length){
34924 cls += ' masonry-bottom-title';
34929 cls += ' ' + this.cls;
34933 tag: (this.href.length) ? 'a' : 'div',
34938 cls: 'masonry-brick-mask'
34942 cls: 'masonry-brick-paragraph',
34948 if(this.href.length){
34949 cfg.href = this.href;
34952 var cn = cfg.cn[1].cn;
34954 if(this.title.length){
34957 cls: 'masonry-brick-title',
34962 if(this.html.length){
34965 cls: 'masonry-brick-text',
34970 if (!this.title.length && !this.html.length) {
34971 cfg.cn[1].cls += ' hide';
34974 if(this.bgimage.length){
34977 cls: 'masonry-brick-image-view',
34982 if(this.videourl.length){
34983 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34984 // youtube support only?
34987 cls: 'masonry-brick-image-view',
34990 allowfullscreen : true
34998 getSplitAutoCreate : function()
35000 var cls = 'masonry-brick masonry-brick-split';
35002 if(this.href.length){
35003 cls += ' masonry-brick-link';
35006 if(this.bgimage.length){
35007 cls += ' masonry-brick-image';
35011 cls += ' masonry-' + this.size + '-brick';
35014 switch (this.placetitle) {
35016 cls += ' masonry-center-title';
35019 cls += ' masonry-bottom-title';
35022 if(!this.bgimage.length){
35023 cls += ' masonry-center-title';
35026 if(this.bgimage.length){
35027 cls += ' masonry-bottom-title';
35033 cls += ' ' + this.cls;
35037 tag: (this.href.length) ? 'a' : 'div',
35042 cls: 'masonry-brick-split-head',
35046 cls: 'masonry-brick-paragraph',
35053 cls: 'masonry-brick-split-body',
35059 if(this.href.length){
35060 cfg.href = this.href;
35063 if(this.title.length){
35064 cfg.cn[0].cn[0].cn.push({
35066 cls: 'masonry-brick-title',
35071 if(this.html.length){
35072 cfg.cn[1].cn.push({
35074 cls: 'masonry-brick-text',
35079 if(this.bgimage.length){
35080 cfg.cn[0].cn.push({
35082 cls: 'masonry-brick-image-view',
35087 if(this.videourl.length){
35088 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35089 // youtube support only?
35090 cfg.cn[0].cn.cn.push({
35092 cls: 'masonry-brick-image-view',
35095 allowfullscreen : true
35102 initEvents: function()
35104 switch (this.size) {
35137 this.el.on('touchstart', this.onTouchStart, this);
35138 this.el.on('touchmove', this.onTouchMove, this);
35139 this.el.on('touchend', this.onTouchEnd, this);
35140 this.el.on('contextmenu', this.onContextMenu, this);
35142 this.el.on('mouseenter' ,this.enter, this);
35143 this.el.on('mouseleave', this.leave, this);
35144 this.el.on('click', this.onClick, this);
35147 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35148 this.parent().bricks.push(this);
35153 onClick: function(e, el)
35155 var time = this.endTimer - this.startTimer;
35156 // Roo.log(e.preventDefault());
35159 e.preventDefault();
35164 if(!this.preventDefault){
35168 e.preventDefault();
35170 if (this.activeClass != '') {
35171 this.selectBrick();
35174 this.fireEvent('click', this, e);
35177 enter: function(e, el)
35179 e.preventDefault();
35181 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35185 if(this.bgimage.length && this.html.length){
35186 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35190 leave: function(e, el)
35192 e.preventDefault();
35194 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35198 if(this.bgimage.length && this.html.length){
35199 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35203 onTouchStart: function(e, el)
35205 // e.preventDefault();
35207 this.touchmoved = false;
35209 if(!this.isFitContainer){
35213 if(!this.bgimage.length || !this.html.length){
35217 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35219 this.timer = new Date().getTime();
35223 onTouchMove: function(e, el)
35225 this.touchmoved = true;
35228 onContextMenu : function(e,el)
35230 e.preventDefault();
35231 e.stopPropagation();
35235 onTouchEnd: function(e, el)
35237 // e.preventDefault();
35239 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35246 if(!this.bgimage.length || !this.html.length){
35248 if(this.href.length){
35249 window.location.href = this.href;
35255 if(!this.isFitContainer){
35259 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35261 window.location.href = this.href;
35264 //selection on single brick only
35265 selectBrick : function() {
35267 if (!this.parentId) {
35271 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35272 var index = m.selectedBrick.indexOf(this.id);
35275 m.selectedBrick.splice(index,1);
35276 this.el.removeClass(this.activeClass);
35280 for(var i = 0; i < m.selectedBrick.length; i++) {
35281 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35282 b.el.removeClass(b.activeClass);
35285 m.selectedBrick = [];
35287 m.selectedBrick.push(this.id);
35288 this.el.addClass(this.activeClass);
35292 isSelected : function(){
35293 return this.el.hasClass(this.activeClass);
35298 Roo.apply(Roo.bootstrap.MasonryBrick, {
35301 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35303 * register a Masonry Brick
35304 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35307 register : function(brick)
35309 //this.groups[brick.id] = brick;
35310 this.groups.add(brick.id, brick);
35313 * fetch a masonry brick based on the masonry brick ID
35314 * @param {string} the masonry brick to add
35315 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35318 get: function(brick_id)
35320 // if (typeof(this.groups[brick_id]) == 'undefined') {
35323 // return this.groups[brick_id] ;
35325 if(this.groups.key(brick_id)) {
35326 return this.groups.key(brick_id);
35344 * @class Roo.bootstrap.Brick
35345 * @extends Roo.bootstrap.Component
35346 * Bootstrap Brick class
35349 * Create a new Brick
35350 * @param {Object} config The config object
35353 Roo.bootstrap.Brick = function(config){
35354 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35360 * When a Brick is click
35361 * @param {Roo.bootstrap.Brick} this
35362 * @param {Roo.EventObject} e
35368 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35371 * @cfg {String} title
35375 * @cfg {String} html
35379 * @cfg {String} bgimage
35383 * @cfg {String} cls
35387 * @cfg {String} href
35391 * @cfg {String} video
35395 * @cfg {Boolean} square
35399 getAutoCreate : function()
35401 var cls = 'roo-brick';
35403 if(this.href.length){
35404 cls += ' roo-brick-link';
35407 if(this.bgimage.length){
35408 cls += ' roo-brick-image';
35411 if(!this.html.length && !this.bgimage.length){
35412 cls += ' roo-brick-center-title';
35415 if(!this.html.length && this.bgimage.length){
35416 cls += ' roo-brick-bottom-title';
35420 cls += ' ' + this.cls;
35424 tag: (this.href.length) ? 'a' : 'div',
35429 cls: 'roo-brick-paragraph',
35435 if(this.href.length){
35436 cfg.href = this.href;
35439 var cn = cfg.cn[0].cn;
35441 if(this.title.length){
35444 cls: 'roo-brick-title',
35449 if(this.html.length){
35452 cls: 'roo-brick-text',
35459 if(this.bgimage.length){
35462 cls: 'roo-brick-image-view',
35470 initEvents: function()
35472 if(this.title.length || this.html.length){
35473 this.el.on('mouseenter' ,this.enter, this);
35474 this.el.on('mouseleave', this.leave, this);
35477 Roo.EventManager.onWindowResize(this.resize, this);
35479 if(this.bgimage.length){
35480 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35481 this.imageEl.on('load', this.onImageLoad, this);
35488 onImageLoad : function()
35493 resize : function()
35495 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35497 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35499 if(this.bgimage.length){
35500 var image = this.el.select('.roo-brick-image-view', true).first();
35502 image.setWidth(paragraph.getWidth());
35505 image.setHeight(paragraph.getWidth());
35508 this.el.setHeight(image.getHeight());
35509 paragraph.setHeight(image.getHeight());
35515 enter: function(e, el)
35517 e.preventDefault();
35519 if(this.bgimage.length){
35520 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35521 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35525 leave: function(e, el)
35527 e.preventDefault();
35529 if(this.bgimage.length){
35530 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35531 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35546 * @class Roo.bootstrap.NumberField
35547 * @extends Roo.bootstrap.Input
35548 * Bootstrap NumberField class
35554 * Create a new NumberField
35555 * @param {Object} config The config object
35558 Roo.bootstrap.NumberField = function(config){
35559 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35562 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35565 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35567 allowDecimals : true,
35569 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35571 decimalSeparator : ".",
35573 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35575 decimalPrecision : 2,
35577 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35579 allowNegative : true,
35582 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35586 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35588 minValue : Number.NEGATIVE_INFINITY,
35590 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35592 maxValue : Number.MAX_VALUE,
35594 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35596 minText : "The minimum value for this field is {0}",
35598 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35600 maxText : "The maximum value for this field is {0}",
35602 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35603 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35605 nanText : "{0} is not a valid number",
35607 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35609 thousandsDelimiter : false,
35611 * @cfg {String} valueAlign alignment of value
35613 valueAlign : "left",
35615 getAutoCreate : function()
35617 var hiddenInput = {
35621 cls: 'hidden-number-input'
35625 hiddenInput.name = this.name;
35630 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35632 this.name = hiddenInput.name;
35634 if(cfg.cn.length > 0) {
35635 cfg.cn.push(hiddenInput);
35642 initEvents : function()
35644 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35646 var allowed = "0123456789";
35648 if(this.allowDecimals){
35649 allowed += this.decimalSeparator;
35652 if(this.allowNegative){
35656 if(this.thousandsDelimiter) {
35660 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35662 var keyPress = function(e){
35664 var k = e.getKey();
35666 var c = e.getCharCode();
35669 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35670 allowed.indexOf(String.fromCharCode(c)) === -1
35676 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35680 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35685 this.el.on("keypress", keyPress, this);
35688 validateValue : function(value)
35691 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35695 var num = this.parseValue(value);
35698 this.markInvalid(String.format(this.nanText, value));
35702 if(num < this.minValue){
35703 this.markInvalid(String.format(this.minText, this.minValue));
35707 if(num > this.maxValue){
35708 this.markInvalid(String.format(this.maxText, this.maxValue));
35715 getValue : function()
35717 var v = this.hiddenEl().getValue();
35719 return this.fixPrecision(this.parseValue(v));
35722 parseValue : function(value)
35724 if(this.thousandsDelimiter) {
35726 r = new RegExp(",", "g");
35727 value = value.replace(r, "");
35730 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35731 return isNaN(value) ? '' : value;
35734 fixPrecision : function(value)
35736 if(this.thousandsDelimiter) {
35738 r = new RegExp(",", "g");
35739 value = value.replace(r, "");
35742 var nan = isNaN(value);
35744 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35745 return nan ? '' : value;
35747 return parseFloat(value).toFixed(this.decimalPrecision);
35750 setValue : function(v)
35752 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35758 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35760 this.inputEl().dom.value = (v == '') ? '' :
35761 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35763 if(!this.allowZero && v === '0') {
35764 this.hiddenEl().dom.value = '';
35765 this.inputEl().dom.value = '';
35772 decimalPrecisionFcn : function(v)
35774 return Math.floor(v);
35777 beforeBlur : function()
35779 var v = this.parseValue(this.getRawValue());
35781 if(v || v === 0 || v === ''){
35786 hiddenEl : function()
35788 return this.el.select('input.hidden-number-input',true).first();
35800 * @class Roo.bootstrap.DocumentSlider
35801 * @extends Roo.bootstrap.Component
35802 * Bootstrap DocumentSlider class
35805 * Create a new DocumentViewer
35806 * @param {Object} config The config object
35809 Roo.bootstrap.DocumentSlider = function(config){
35810 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35817 * Fire after initEvent
35818 * @param {Roo.bootstrap.DocumentSlider} this
35823 * Fire after update
35824 * @param {Roo.bootstrap.DocumentSlider} this
35830 * @param {Roo.bootstrap.DocumentSlider} this
35836 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35842 getAutoCreate : function()
35846 cls : 'roo-document-slider',
35850 cls : 'roo-document-slider-header',
35854 cls : 'roo-document-slider-header-title'
35860 cls : 'roo-document-slider-body',
35864 cls : 'roo-document-slider-prev',
35868 cls : 'fa fa-chevron-left'
35874 cls : 'roo-document-slider-thumb',
35878 cls : 'roo-document-slider-image'
35884 cls : 'roo-document-slider-next',
35888 cls : 'fa fa-chevron-right'
35900 initEvents : function()
35902 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35903 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35905 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35906 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35908 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35909 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35911 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35912 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35914 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35915 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35917 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35918 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35920 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35921 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35923 this.thumbEl.on('click', this.onClick, this);
35925 this.prevIndicator.on('click', this.prev, this);
35927 this.nextIndicator.on('click', this.next, this);
35931 initial : function()
35933 if(this.files.length){
35934 this.indicator = 1;
35938 this.fireEvent('initial', this);
35941 update : function()
35943 this.imageEl.attr('src', this.files[this.indicator - 1]);
35945 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35947 this.prevIndicator.show();
35949 if(this.indicator == 1){
35950 this.prevIndicator.hide();
35953 this.nextIndicator.show();
35955 if(this.indicator == this.files.length){
35956 this.nextIndicator.hide();
35959 this.thumbEl.scrollTo('top');
35961 this.fireEvent('update', this);
35964 onClick : function(e)
35966 e.preventDefault();
35968 this.fireEvent('click', this);
35973 e.preventDefault();
35975 this.indicator = Math.max(1, this.indicator - 1);
35982 e.preventDefault();
35984 this.indicator = Math.min(this.files.length, this.indicator + 1);
35998 * @class Roo.bootstrap.RadioSet
35999 * @extends Roo.bootstrap.Input
36000 * Bootstrap RadioSet class
36001 * @cfg {String} indicatorpos (left|right) default left
36002 * @cfg {Boolean} inline (true|false) inline the element (default true)
36003 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36005 * Create a new RadioSet
36006 * @param {Object} config The config object
36009 Roo.bootstrap.RadioSet = function(config){
36011 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36015 Roo.bootstrap.RadioSet.register(this);
36020 * Fires when the element is checked or unchecked.
36021 * @param {Roo.bootstrap.RadioSet} this This radio
36022 * @param {Roo.bootstrap.Radio} item The checked item
36027 * Fires when the element is click.
36028 * @param {Roo.bootstrap.RadioSet} this This radio set
36029 * @param {Roo.bootstrap.Radio} item The checked item
36030 * @param {Roo.EventObject} e The event object
36037 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36045 indicatorpos : 'left',
36047 getAutoCreate : function()
36051 cls : 'roo-radio-set-label',
36055 html : this.fieldLabel
36059 if (Roo.bootstrap.version == 3) {
36062 if(this.indicatorpos == 'left'){
36065 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36066 tooltip : 'This field is required'
36071 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36072 tooltip : 'This field is required'
36078 cls : 'roo-radio-set-items'
36081 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36083 if (align === 'left' && this.fieldLabel.length) {
36086 cls : "roo-radio-set-right",
36092 if(this.labelWidth > 12){
36093 label.style = "width: " + this.labelWidth + 'px';
36096 if(this.labelWidth < 13 && this.labelmd == 0){
36097 this.labelmd = this.labelWidth;
36100 if(this.labellg > 0){
36101 label.cls += ' col-lg-' + this.labellg;
36102 items.cls += ' col-lg-' + (12 - this.labellg);
36105 if(this.labelmd > 0){
36106 label.cls += ' col-md-' + this.labelmd;
36107 items.cls += ' col-md-' + (12 - this.labelmd);
36110 if(this.labelsm > 0){
36111 label.cls += ' col-sm-' + this.labelsm;
36112 items.cls += ' col-sm-' + (12 - this.labelsm);
36115 if(this.labelxs > 0){
36116 label.cls += ' col-xs-' + this.labelxs;
36117 items.cls += ' col-xs-' + (12 - this.labelxs);
36123 cls : 'roo-radio-set',
36127 cls : 'roo-radio-set-input',
36130 value : this.value ? this.value : ''
36137 if(this.weight.length){
36138 cfg.cls += ' roo-radio-' + this.weight;
36142 cfg.cls += ' roo-radio-set-inline';
36146 ['xs','sm','md','lg'].map(function(size){
36147 if (settings[size]) {
36148 cfg.cls += ' col-' + size + '-' + settings[size];
36156 initEvents : function()
36158 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36159 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36161 if(!this.fieldLabel.length){
36162 this.labelEl.hide();
36165 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36166 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36168 this.indicator = this.indicatorEl();
36170 if(this.indicator){
36171 this.indicator.addClass('invisible');
36174 this.originalValue = this.getValue();
36178 inputEl: function ()
36180 return this.el.select('.roo-radio-set-input', true).first();
36183 getChildContainer : function()
36185 return this.itemsEl;
36188 register : function(item)
36190 this.radioes.push(item);
36194 validate : function()
36196 if(this.getVisibilityEl().hasClass('hidden')){
36202 Roo.each(this.radioes, function(i){
36211 if(this.allowBlank) {
36215 if(this.disabled || valid){
36220 this.markInvalid();
36225 markValid : function()
36227 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36228 this.indicatorEl().removeClass('visible');
36229 this.indicatorEl().addClass('invisible');
36233 if (Roo.bootstrap.version == 3) {
36234 this.el.removeClass([this.invalidClass, this.validClass]);
36235 this.el.addClass(this.validClass);
36237 this.el.removeClass(['is-invalid','is-valid']);
36238 this.el.addClass(['is-valid']);
36240 this.fireEvent('valid', this);
36243 markInvalid : function(msg)
36245 if(this.allowBlank || this.disabled){
36249 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36250 this.indicatorEl().removeClass('invisible');
36251 this.indicatorEl().addClass('visible');
36253 if (Roo.bootstrap.version == 3) {
36254 this.el.removeClass([this.invalidClass, this.validClass]);
36255 this.el.addClass(this.invalidClass);
36257 this.el.removeClass(['is-invalid','is-valid']);
36258 this.el.addClass(['is-invalid']);
36261 this.fireEvent('invalid', this, msg);
36265 setValue : function(v, suppressEvent)
36267 if(this.value === v){
36274 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36277 Roo.each(this.radioes, function(i){
36279 i.el.removeClass('checked');
36282 Roo.each(this.radioes, function(i){
36284 if(i.value === v || i.value.toString() === v.toString()){
36286 i.el.addClass('checked');
36288 if(suppressEvent !== true){
36289 this.fireEvent('check', this, i);
36300 clearInvalid : function(){
36302 if(!this.el || this.preventMark){
36306 this.el.removeClass([this.invalidClass]);
36308 this.fireEvent('valid', this);
36313 Roo.apply(Roo.bootstrap.RadioSet, {
36317 register : function(set)
36319 this.groups[set.name] = set;
36322 get: function(name)
36324 if (typeof(this.groups[name]) == 'undefined') {
36328 return this.groups[name] ;
36334 * Ext JS Library 1.1.1
36335 * Copyright(c) 2006-2007, Ext JS, LLC.
36337 * Originally Released Under LGPL - original licence link has changed is not relivant.
36340 * <script type="text/javascript">
36345 * @class Roo.bootstrap.SplitBar
36346 * @extends Roo.util.Observable
36347 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36351 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36352 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36353 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36354 split.minSize = 100;
36355 split.maxSize = 600;
36356 split.animate = true;
36357 split.on('moved', splitterMoved);
36360 * Create a new SplitBar
36361 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36362 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36363 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36364 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36365 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36366 position of the SplitBar).
36368 Roo.bootstrap.SplitBar = function(cfg){
36373 // dragElement : elm
36374 // resizingElement: el,
36376 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36377 // placement : Roo.bootstrap.SplitBar.LEFT ,
36378 // existingProxy ???
36381 this.el = Roo.get(cfg.dragElement, true);
36382 this.el.dom.unselectable = "on";
36384 this.resizingEl = Roo.get(cfg.resizingElement, true);
36388 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36389 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36392 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36395 * The minimum size of the resizing element. (Defaults to 0)
36401 * The maximum size of the resizing element. (Defaults to 2000)
36404 this.maxSize = 2000;
36407 * Whether to animate the transition to the new size
36410 this.animate = false;
36413 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36416 this.useShim = false;
36421 if(!cfg.existingProxy){
36423 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36425 this.proxy = Roo.get(cfg.existingProxy).dom;
36428 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36431 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36434 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36437 this.dragSpecs = {};
36440 * @private The adapter to use to positon and resize elements
36442 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36443 this.adapter.init(this);
36445 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36447 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36448 this.el.addClass("roo-splitbar-h");
36451 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36452 this.el.addClass("roo-splitbar-v");
36458 * Fires when the splitter is moved (alias for {@link #event-moved})
36459 * @param {Roo.bootstrap.SplitBar} this
36460 * @param {Number} newSize the new width or height
36465 * Fires when the splitter is moved
36466 * @param {Roo.bootstrap.SplitBar} this
36467 * @param {Number} newSize the new width or height
36471 * @event beforeresize
36472 * Fires before the splitter is dragged
36473 * @param {Roo.bootstrap.SplitBar} this
36475 "beforeresize" : true,
36477 "beforeapply" : true
36480 Roo.util.Observable.call(this);
36483 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36484 onStartProxyDrag : function(x, y){
36485 this.fireEvent("beforeresize", this);
36487 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36489 o.enableDisplayMode("block");
36490 // all splitbars share the same overlay
36491 Roo.bootstrap.SplitBar.prototype.overlay = o;
36493 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36494 this.overlay.show();
36495 Roo.get(this.proxy).setDisplayed("block");
36496 var size = this.adapter.getElementSize(this);
36497 this.activeMinSize = this.getMinimumSize();;
36498 this.activeMaxSize = this.getMaximumSize();;
36499 var c1 = size - this.activeMinSize;
36500 var c2 = Math.max(this.activeMaxSize - size, 0);
36501 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36502 this.dd.resetConstraints();
36503 this.dd.setXConstraint(
36504 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36505 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36507 this.dd.setYConstraint(0, 0);
36509 this.dd.resetConstraints();
36510 this.dd.setXConstraint(0, 0);
36511 this.dd.setYConstraint(
36512 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36513 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36516 this.dragSpecs.startSize = size;
36517 this.dragSpecs.startPoint = [x, y];
36518 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36522 * @private Called after the drag operation by the DDProxy
36524 onEndProxyDrag : function(e){
36525 Roo.get(this.proxy).setDisplayed(false);
36526 var endPoint = Roo.lib.Event.getXY(e);
36528 this.overlay.hide();
36531 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36532 newSize = this.dragSpecs.startSize +
36533 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36534 endPoint[0] - this.dragSpecs.startPoint[0] :
36535 this.dragSpecs.startPoint[0] - endPoint[0]
36538 newSize = this.dragSpecs.startSize +
36539 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36540 endPoint[1] - this.dragSpecs.startPoint[1] :
36541 this.dragSpecs.startPoint[1] - endPoint[1]
36544 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36545 if(newSize != this.dragSpecs.startSize){
36546 if(this.fireEvent('beforeapply', this, newSize) !== false){
36547 this.adapter.setElementSize(this, newSize);
36548 this.fireEvent("moved", this, newSize);
36549 this.fireEvent("resize", this, newSize);
36555 * Get the adapter this SplitBar uses
36556 * @return The adapter object
36558 getAdapter : function(){
36559 return this.adapter;
36563 * Set the adapter this SplitBar uses
36564 * @param {Object} adapter A SplitBar adapter object
36566 setAdapter : function(adapter){
36567 this.adapter = adapter;
36568 this.adapter.init(this);
36572 * Gets the minimum size for the resizing element
36573 * @return {Number} The minimum size
36575 getMinimumSize : function(){
36576 return this.minSize;
36580 * Sets the minimum size for the resizing element
36581 * @param {Number} minSize The minimum size
36583 setMinimumSize : function(minSize){
36584 this.minSize = minSize;
36588 * Gets the maximum size for the resizing element
36589 * @return {Number} The maximum size
36591 getMaximumSize : function(){
36592 return this.maxSize;
36596 * Sets the maximum size for the resizing element
36597 * @param {Number} maxSize The maximum size
36599 setMaximumSize : function(maxSize){
36600 this.maxSize = maxSize;
36604 * Sets the initialize size for the resizing element
36605 * @param {Number} size The initial size
36607 setCurrentSize : function(size){
36608 var oldAnimate = this.animate;
36609 this.animate = false;
36610 this.adapter.setElementSize(this, size);
36611 this.animate = oldAnimate;
36615 * Destroy this splitbar.
36616 * @param {Boolean} removeEl True to remove the element
36618 destroy : function(removeEl){
36620 this.shim.remove();
36623 this.proxy.parentNode.removeChild(this.proxy);
36631 * @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.
36633 Roo.bootstrap.SplitBar.createProxy = function(dir){
36634 var proxy = new Roo.Element(document.createElement("div"));
36635 proxy.unselectable();
36636 var cls = 'roo-splitbar-proxy';
36637 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36638 document.body.appendChild(proxy.dom);
36643 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36644 * Default Adapter. It assumes the splitter and resizing element are not positioned
36645 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36647 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36650 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36651 // do nothing for now
36652 init : function(s){
36656 * Called before drag operations to get the current size of the resizing element.
36657 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36659 getElementSize : function(s){
36660 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36661 return s.resizingEl.getWidth();
36663 return s.resizingEl.getHeight();
36668 * Called after drag operations to set the size of the resizing element.
36669 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36670 * @param {Number} newSize The new size to set
36671 * @param {Function} onComplete A function to be invoked when resizing is complete
36673 setElementSize : function(s, newSize, onComplete){
36674 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36676 s.resizingEl.setWidth(newSize);
36678 onComplete(s, newSize);
36681 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36686 s.resizingEl.setHeight(newSize);
36688 onComplete(s, newSize);
36691 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36698 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36699 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36700 * Adapter that moves the splitter element to align with the resized sizing element.
36701 * Used with an absolute positioned SplitBar.
36702 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36703 * document.body, make sure you assign an id to the body element.
36705 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36706 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36707 this.container = Roo.get(container);
36710 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36711 init : function(s){
36712 this.basic.init(s);
36715 getElementSize : function(s){
36716 return this.basic.getElementSize(s);
36719 setElementSize : function(s, newSize, onComplete){
36720 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36723 moveSplitter : function(s){
36724 var yes = Roo.bootstrap.SplitBar;
36725 switch(s.placement){
36727 s.el.setX(s.resizingEl.getRight());
36730 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36733 s.el.setY(s.resizingEl.getBottom());
36736 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36743 * Orientation constant - Create a vertical SplitBar
36747 Roo.bootstrap.SplitBar.VERTICAL = 1;
36750 * Orientation constant - Create a horizontal SplitBar
36754 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36757 * Placement constant - The resizing element is to the left of the splitter element
36761 Roo.bootstrap.SplitBar.LEFT = 1;
36764 * Placement constant - The resizing element is to the right of the splitter element
36768 Roo.bootstrap.SplitBar.RIGHT = 2;
36771 * Placement constant - The resizing element is positioned above the splitter element
36775 Roo.bootstrap.SplitBar.TOP = 3;
36778 * Placement constant - The resizing element is positioned under splitter element
36782 Roo.bootstrap.SplitBar.BOTTOM = 4;
36783 Roo.namespace("Roo.bootstrap.layout");/*
36785 * Ext JS Library 1.1.1
36786 * Copyright(c) 2006-2007, Ext JS, LLC.
36788 * Originally Released Under LGPL - original licence link has changed is not relivant.
36791 * <script type="text/javascript">
36795 * @class Roo.bootstrap.layout.Manager
36796 * @extends Roo.bootstrap.Component
36797 * Base class for layout managers.
36799 Roo.bootstrap.layout.Manager = function(config)
36801 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36807 /** false to disable window resize monitoring @type Boolean */
36808 this.monitorWindowResize = true;
36813 * Fires when a layout is performed.
36814 * @param {Roo.LayoutManager} this
36818 * @event regionresized
36819 * Fires when the user resizes a region.
36820 * @param {Roo.LayoutRegion} region The resized region
36821 * @param {Number} newSize The new size (width for east/west, height for north/south)
36823 "regionresized" : true,
36825 * @event regioncollapsed
36826 * Fires when a region is collapsed.
36827 * @param {Roo.LayoutRegion} region The collapsed region
36829 "regioncollapsed" : true,
36831 * @event regionexpanded
36832 * Fires when a region is expanded.
36833 * @param {Roo.LayoutRegion} region The expanded region
36835 "regionexpanded" : true
36837 this.updating = false;
36840 this.el = Roo.get(config.el);
36846 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36851 monitorWindowResize : true,
36857 onRender : function(ct, position)
36860 this.el = Roo.get(ct);
36863 //this.fireEvent('render',this);
36867 initEvents: function()
36871 // ie scrollbar fix
36872 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36873 document.body.scroll = "no";
36874 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36875 this.el.position('relative');
36877 this.id = this.el.id;
36878 this.el.addClass("roo-layout-container");
36879 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36880 if(this.el.dom != document.body ) {
36881 this.el.on('resize', this.layout,this);
36882 this.el.on('show', this.layout,this);
36888 * Returns true if this layout is currently being updated
36889 * @return {Boolean}
36891 isUpdating : function(){
36892 return this.updating;
36896 * Suspend the LayoutManager from doing auto-layouts while
36897 * making multiple add or remove calls
36899 beginUpdate : function(){
36900 this.updating = true;
36904 * Restore auto-layouts and optionally disable the manager from performing a layout
36905 * @param {Boolean} noLayout true to disable a layout update
36907 endUpdate : function(noLayout){
36908 this.updating = false;
36914 layout: function(){
36918 onRegionResized : function(region, newSize){
36919 this.fireEvent("regionresized", region, newSize);
36923 onRegionCollapsed : function(region){
36924 this.fireEvent("regioncollapsed", region);
36927 onRegionExpanded : function(region){
36928 this.fireEvent("regionexpanded", region);
36932 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36933 * performs box-model adjustments.
36934 * @return {Object} The size as an object {width: (the width), height: (the height)}
36936 getViewSize : function()
36939 if(this.el.dom != document.body){
36940 size = this.el.getSize();
36942 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36944 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36945 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36950 * Returns the Element this layout is bound to.
36951 * @return {Roo.Element}
36953 getEl : function(){
36958 * Returns the specified region.
36959 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36960 * @return {Roo.LayoutRegion}
36962 getRegion : function(target){
36963 return this.regions[target.toLowerCase()];
36966 onWindowResize : function(){
36967 if(this.monitorWindowResize){
36974 * Ext JS Library 1.1.1
36975 * Copyright(c) 2006-2007, Ext JS, LLC.
36977 * Originally Released Under LGPL - original licence link has changed is not relivant.
36980 * <script type="text/javascript">
36983 * @class Roo.bootstrap.layout.Border
36984 * @extends Roo.bootstrap.layout.Manager
36985 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36986 * please see: examples/bootstrap/nested.html<br><br>
36988 <b>The container the layout is rendered into can be either the body element or any other element.
36989 If it is not the body element, the container needs to either be an absolute positioned element,
36990 or you will need to add "position:relative" to the css of the container. You will also need to specify
36991 the container size if it is not the body element.</b>
36994 * Create a new Border
36995 * @param {Object} config Configuration options
36997 Roo.bootstrap.layout.Border = function(config){
36998 config = config || {};
36999 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37003 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37004 if(config[region]){
37005 config[region].region = region;
37006 this.addRegion(config[region]);
37012 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37014 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37016 parent : false, // this might point to a 'nest' or a ???
37019 * Creates and adds a new region if it doesn't already exist.
37020 * @param {String} target The target region key (north, south, east, west or center).
37021 * @param {Object} config The regions config object
37022 * @return {BorderLayoutRegion} The new region
37024 addRegion : function(config)
37026 if(!this.regions[config.region]){
37027 var r = this.factory(config);
37028 this.bindRegion(r);
37030 return this.regions[config.region];
37034 bindRegion : function(r){
37035 this.regions[r.config.region] = r;
37037 r.on("visibilitychange", this.layout, this);
37038 r.on("paneladded", this.layout, this);
37039 r.on("panelremoved", this.layout, this);
37040 r.on("invalidated", this.layout, this);
37041 r.on("resized", this.onRegionResized, this);
37042 r.on("collapsed", this.onRegionCollapsed, this);
37043 r.on("expanded", this.onRegionExpanded, this);
37047 * Performs a layout update.
37049 layout : function()
37051 if(this.updating) {
37055 // render all the rebions if they have not been done alreayd?
37056 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37057 if(this.regions[region] && !this.regions[region].bodyEl){
37058 this.regions[region].onRender(this.el)
37062 var size = this.getViewSize();
37063 var w = size.width;
37064 var h = size.height;
37069 //var x = 0, y = 0;
37071 var rs = this.regions;
37072 var north = rs["north"];
37073 var south = rs["south"];
37074 var west = rs["west"];
37075 var east = rs["east"];
37076 var center = rs["center"];
37077 //if(this.hideOnLayout){ // not supported anymore
37078 //c.el.setStyle("display", "none");
37080 if(north && north.isVisible()){
37081 var b = north.getBox();
37082 var m = north.getMargins();
37083 b.width = w - (m.left+m.right);
37086 centerY = b.height + b.y + m.bottom;
37087 centerH -= centerY;
37088 north.updateBox(this.safeBox(b));
37090 if(south && south.isVisible()){
37091 var b = south.getBox();
37092 var m = south.getMargins();
37093 b.width = w - (m.left+m.right);
37095 var totalHeight = (b.height + m.top + m.bottom);
37096 b.y = h - totalHeight + m.top;
37097 centerH -= totalHeight;
37098 south.updateBox(this.safeBox(b));
37100 if(west && west.isVisible()){
37101 var b = west.getBox();
37102 var m = west.getMargins();
37103 b.height = centerH - (m.top+m.bottom);
37105 b.y = centerY + m.top;
37106 var totalWidth = (b.width + m.left + m.right);
37107 centerX += totalWidth;
37108 centerW -= totalWidth;
37109 west.updateBox(this.safeBox(b));
37111 if(east && east.isVisible()){
37112 var b = east.getBox();
37113 var m = east.getMargins();
37114 b.height = centerH - (m.top+m.bottom);
37115 var totalWidth = (b.width + m.left + m.right);
37116 b.x = w - totalWidth + m.left;
37117 b.y = centerY + m.top;
37118 centerW -= totalWidth;
37119 east.updateBox(this.safeBox(b));
37122 var m = center.getMargins();
37124 x: centerX + m.left,
37125 y: centerY + m.top,
37126 width: centerW - (m.left+m.right),
37127 height: centerH - (m.top+m.bottom)
37129 //if(this.hideOnLayout){
37130 //center.el.setStyle("display", "block");
37132 center.updateBox(this.safeBox(centerBox));
37135 this.fireEvent("layout", this);
37139 safeBox : function(box){
37140 box.width = Math.max(0, box.width);
37141 box.height = Math.max(0, box.height);
37146 * Adds a ContentPanel (or subclass) to this layout.
37147 * @param {String} target The target region key (north, south, east, west or center).
37148 * @param {Roo.ContentPanel} panel The panel to add
37149 * @return {Roo.ContentPanel} The added panel
37151 add : function(target, panel){
37153 target = target.toLowerCase();
37154 return this.regions[target].add(panel);
37158 * Remove a ContentPanel (or subclass) to this layout.
37159 * @param {String} target The target region key (north, south, east, west or center).
37160 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37161 * @return {Roo.ContentPanel} The removed panel
37163 remove : function(target, panel){
37164 target = target.toLowerCase();
37165 return this.regions[target].remove(panel);
37169 * Searches all regions for a panel with the specified id
37170 * @param {String} panelId
37171 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37173 findPanel : function(panelId){
37174 var rs = this.regions;
37175 for(var target in rs){
37176 if(typeof rs[target] != "function"){
37177 var p = rs[target].getPanel(panelId);
37187 * Searches all regions for a panel with the specified id and activates (shows) it.
37188 * @param {String/ContentPanel} panelId The panels id or the panel itself
37189 * @return {Roo.ContentPanel} The shown panel or null
37191 showPanel : function(panelId) {
37192 var rs = this.regions;
37193 for(var target in rs){
37194 var r = rs[target];
37195 if(typeof r != "function"){
37196 if(r.hasPanel(panelId)){
37197 return r.showPanel(panelId);
37205 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37206 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37209 restoreState : function(provider){
37211 provider = Roo.state.Manager;
37213 var sm = new Roo.LayoutStateManager();
37214 sm.init(this, provider);
37220 * Adds a xtype elements to the layout.
37224 xtype : 'ContentPanel',
37231 xtype : 'NestedLayoutPanel',
37237 items : [ ... list of content panels or nested layout panels.. ]
37241 * @param {Object} cfg Xtype definition of item to add.
37243 addxtype : function(cfg)
37245 // basically accepts a pannel...
37246 // can accept a layout region..!?!?
37247 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37250 // theory? children can only be panels??
37252 //if (!cfg.xtype.match(/Panel$/)) {
37257 if (typeof(cfg.region) == 'undefined') {
37258 Roo.log("Failed to add Panel, region was not set");
37262 var region = cfg.region;
37268 xitems = cfg.items;
37273 if ( region == 'center') {
37274 Roo.log("Center: " + cfg.title);
37280 case 'Content': // ContentPanel (el, cfg)
37281 case 'Scroll': // ContentPanel (el, cfg)
37283 cfg.autoCreate = cfg.autoCreate || true;
37284 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37286 // var el = this.el.createChild();
37287 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37290 this.add(region, ret);
37294 case 'TreePanel': // our new panel!
37295 cfg.el = this.el.createChild();
37296 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37297 this.add(region, ret);
37302 // create a new Layout (which is a Border Layout...
37304 var clayout = cfg.layout;
37305 clayout.el = this.el.createChild();
37306 clayout.items = clayout.items || [];
37310 // replace this exitems with the clayout ones..
37311 xitems = clayout.items;
37313 // force background off if it's in center...
37314 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37315 cfg.background = false;
37317 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37320 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37321 //console.log('adding nested layout panel ' + cfg.toSource());
37322 this.add(region, ret);
37323 nb = {}; /// find first...
37328 // needs grid and region
37330 //var el = this.getRegion(region).el.createChild();
37332 *var el = this.el.createChild();
37333 // create the grid first...
37334 cfg.grid.container = el;
37335 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37338 if (region == 'center' && this.active ) {
37339 cfg.background = false;
37342 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37344 this.add(region, ret);
37346 if (cfg.background) {
37347 // render grid on panel activation (if panel background)
37348 ret.on('activate', function(gp) {
37349 if (!gp.grid.rendered) {
37350 // gp.grid.render(el);
37354 // cfg.grid.render(el);
37360 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37361 // it was the old xcomponent building that caused this before.
37362 // espeically if border is the top element in the tree.
37372 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37374 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37375 this.add(region, ret);
37379 throw "Can not add '" + cfg.xtype + "' to Border";
37385 this.beginUpdate();
37389 Roo.each(xitems, function(i) {
37390 region = nb && i.region ? i.region : false;
37392 var add = ret.addxtype(i);
37395 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37396 if (!i.background) {
37397 abn[region] = nb[region] ;
37404 // make the last non-background panel active..
37405 //if (nb) { Roo.log(abn); }
37408 for(var r in abn) {
37409 region = this.getRegion(r);
37411 // tried using nb[r], but it does not work..
37413 region.showPanel(abn[r]);
37424 factory : function(cfg)
37427 var validRegions = Roo.bootstrap.layout.Border.regions;
37429 var target = cfg.region;
37432 var r = Roo.bootstrap.layout;
37436 return new r.North(cfg);
37438 return new r.South(cfg);
37440 return new r.East(cfg);
37442 return new r.West(cfg);
37444 return new r.Center(cfg);
37446 throw 'Layout region "'+target+'" not supported.';
37453 * Ext JS Library 1.1.1
37454 * Copyright(c) 2006-2007, Ext JS, LLC.
37456 * Originally Released Under LGPL - original licence link has changed is not relivant.
37459 * <script type="text/javascript">
37463 * @class Roo.bootstrap.layout.Basic
37464 * @extends Roo.util.Observable
37465 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37466 * and does not have a titlebar, tabs or any other features. All it does is size and position
37467 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37468 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37469 * @cfg {string} region the region that it inhabits..
37470 * @cfg {bool} skipConfig skip config?
37474 Roo.bootstrap.layout.Basic = function(config){
37476 this.mgr = config.mgr;
37478 this.position = config.region;
37480 var skipConfig = config.skipConfig;
37484 * @scope Roo.BasicLayoutRegion
37488 * @event beforeremove
37489 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37490 * @param {Roo.LayoutRegion} this
37491 * @param {Roo.ContentPanel} panel The panel
37492 * @param {Object} e The cancel event object
37494 "beforeremove" : true,
37496 * @event invalidated
37497 * Fires when the layout for this region is changed.
37498 * @param {Roo.LayoutRegion} this
37500 "invalidated" : true,
37502 * @event visibilitychange
37503 * Fires when this region is shown or hidden
37504 * @param {Roo.LayoutRegion} this
37505 * @param {Boolean} visibility true or false
37507 "visibilitychange" : true,
37509 * @event paneladded
37510 * Fires when a panel is added.
37511 * @param {Roo.LayoutRegion} this
37512 * @param {Roo.ContentPanel} panel The panel
37514 "paneladded" : true,
37516 * @event panelremoved
37517 * Fires when a panel is removed.
37518 * @param {Roo.LayoutRegion} this
37519 * @param {Roo.ContentPanel} panel The panel
37521 "panelremoved" : true,
37523 * @event beforecollapse
37524 * Fires when this region before collapse.
37525 * @param {Roo.LayoutRegion} this
37527 "beforecollapse" : true,
37530 * Fires when this region is collapsed.
37531 * @param {Roo.LayoutRegion} this
37533 "collapsed" : true,
37536 * Fires when this region is expanded.
37537 * @param {Roo.LayoutRegion} this
37542 * Fires when this region is slid into view.
37543 * @param {Roo.LayoutRegion} this
37545 "slideshow" : true,
37548 * Fires when this region slides out of view.
37549 * @param {Roo.LayoutRegion} this
37551 "slidehide" : true,
37553 * @event panelactivated
37554 * Fires when a panel is activated.
37555 * @param {Roo.LayoutRegion} this
37556 * @param {Roo.ContentPanel} panel The activated panel
37558 "panelactivated" : true,
37561 * Fires when the user resizes this region.
37562 * @param {Roo.LayoutRegion} this
37563 * @param {Number} newSize The new size (width for east/west, height for north/south)
37567 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37568 this.panels = new Roo.util.MixedCollection();
37569 this.panels.getKey = this.getPanelId.createDelegate(this);
37571 this.activePanel = null;
37572 // ensure listeners are added...
37574 if (config.listeners || config.events) {
37575 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37576 listeners : config.listeners || {},
37577 events : config.events || {}
37581 if(skipConfig !== true){
37582 this.applyConfig(config);
37586 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37588 getPanelId : function(p){
37592 applyConfig : function(config){
37593 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37594 this.config = config;
37599 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37600 * the width, for horizontal (north, south) the height.
37601 * @param {Number} newSize The new width or height
37603 resizeTo : function(newSize){
37604 var el = this.el ? this.el :
37605 (this.activePanel ? this.activePanel.getEl() : null);
37607 switch(this.position){
37610 el.setWidth(newSize);
37611 this.fireEvent("resized", this, newSize);
37615 el.setHeight(newSize);
37616 this.fireEvent("resized", this, newSize);
37622 getBox : function(){
37623 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37626 getMargins : function(){
37627 return this.margins;
37630 updateBox : function(box){
37632 var el = this.activePanel.getEl();
37633 el.dom.style.left = box.x + "px";
37634 el.dom.style.top = box.y + "px";
37635 this.activePanel.setSize(box.width, box.height);
37639 * Returns the container element for this region.
37640 * @return {Roo.Element}
37642 getEl : function(){
37643 return this.activePanel;
37647 * Returns true if this region is currently visible.
37648 * @return {Boolean}
37650 isVisible : function(){
37651 return this.activePanel ? true : false;
37654 setActivePanel : function(panel){
37655 panel = this.getPanel(panel);
37656 if(this.activePanel && this.activePanel != panel){
37657 this.activePanel.setActiveState(false);
37658 this.activePanel.getEl().setLeftTop(-10000,-10000);
37660 this.activePanel = panel;
37661 panel.setActiveState(true);
37663 panel.setSize(this.box.width, this.box.height);
37665 this.fireEvent("panelactivated", this, panel);
37666 this.fireEvent("invalidated");
37670 * Show the specified panel.
37671 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37672 * @return {Roo.ContentPanel} The shown panel or null
37674 showPanel : function(panel){
37675 panel = this.getPanel(panel);
37677 this.setActivePanel(panel);
37683 * Get the active panel for this region.
37684 * @return {Roo.ContentPanel} The active panel or null
37686 getActivePanel : function(){
37687 return this.activePanel;
37691 * Add the passed ContentPanel(s)
37692 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37693 * @return {Roo.ContentPanel} The panel added (if only one was added)
37695 add : function(panel){
37696 if(arguments.length > 1){
37697 for(var i = 0, len = arguments.length; i < len; i++) {
37698 this.add(arguments[i]);
37702 if(this.hasPanel(panel)){
37703 this.showPanel(panel);
37706 var el = panel.getEl();
37707 if(el.dom.parentNode != this.mgr.el.dom){
37708 this.mgr.el.dom.appendChild(el.dom);
37710 if(panel.setRegion){
37711 panel.setRegion(this);
37713 this.panels.add(panel);
37714 el.setStyle("position", "absolute");
37715 if(!panel.background){
37716 this.setActivePanel(panel);
37717 if(this.config.initialSize && this.panels.getCount()==1){
37718 this.resizeTo(this.config.initialSize);
37721 this.fireEvent("paneladded", this, panel);
37726 * Returns true if the panel is in this region.
37727 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37728 * @return {Boolean}
37730 hasPanel : function(panel){
37731 if(typeof panel == "object"){ // must be panel obj
37732 panel = panel.getId();
37734 return this.getPanel(panel) ? true : false;
37738 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37739 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37740 * @param {Boolean} preservePanel Overrides the config preservePanel option
37741 * @return {Roo.ContentPanel} The panel that was removed
37743 remove : function(panel, preservePanel){
37744 panel = this.getPanel(panel);
37749 this.fireEvent("beforeremove", this, panel, e);
37750 if(e.cancel === true){
37753 var panelId = panel.getId();
37754 this.panels.removeKey(panelId);
37759 * Returns the panel specified or null if it's not in this region.
37760 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37761 * @return {Roo.ContentPanel}
37763 getPanel : function(id){
37764 if(typeof id == "object"){ // must be panel obj
37767 return this.panels.get(id);
37771 * Returns this regions position (north/south/east/west/center).
37774 getPosition: function(){
37775 return this.position;
37779 * Ext JS Library 1.1.1
37780 * Copyright(c) 2006-2007, Ext JS, LLC.
37782 * Originally Released Under LGPL - original licence link has changed is not relivant.
37785 * <script type="text/javascript">
37789 * @class Roo.bootstrap.layout.Region
37790 * @extends Roo.bootstrap.layout.Basic
37791 * This class represents a region in a layout manager.
37793 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37794 * @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})
37795 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37796 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37797 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37798 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37799 * @cfg {String} title The title for the region (overrides panel titles)
37800 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37801 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37802 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37803 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37804 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37805 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37806 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37807 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37808 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37809 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37811 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37812 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37813 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37814 * @cfg {Number} width For East/West panels
37815 * @cfg {Number} height For North/South panels
37816 * @cfg {Boolean} split To show the splitter
37817 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37819 * @cfg {string} cls Extra CSS classes to add to region
37821 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37822 * @cfg {string} region the region that it inhabits..
37825 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37826 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37828 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37829 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37830 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37832 Roo.bootstrap.layout.Region = function(config)
37834 this.applyConfig(config);
37836 var mgr = config.mgr;
37837 var pos = config.region;
37838 config.skipConfig = true;
37839 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37842 this.onRender(mgr.el);
37845 this.visible = true;
37846 this.collapsed = false;
37847 this.unrendered_panels = [];
37850 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37852 position: '', // set by wrapper (eg. north/south etc..)
37853 unrendered_panels : null, // unrendered panels.
37855 tabPosition : false,
37857 mgr: false, // points to 'Border'
37860 createBody : function(){
37861 /** This region's body element
37862 * @type Roo.Element */
37863 this.bodyEl = this.el.createChild({
37865 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37869 onRender: function(ctr, pos)
37871 var dh = Roo.DomHelper;
37872 /** This region's container element
37873 * @type Roo.Element */
37874 this.el = dh.append(ctr.dom, {
37876 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37878 /** This region's title element
37879 * @type Roo.Element */
37881 this.titleEl = dh.append(this.el.dom, {
37883 unselectable: "on",
37884 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37886 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37887 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37891 this.titleEl.enableDisplayMode();
37892 /** This region's title text element
37893 * @type HTMLElement */
37894 this.titleTextEl = this.titleEl.dom.firstChild;
37895 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37897 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37898 this.closeBtn.enableDisplayMode();
37899 this.closeBtn.on("click", this.closeClicked, this);
37900 this.closeBtn.hide();
37902 this.createBody(this.config);
37903 if(this.config.hideWhenEmpty){
37905 this.on("paneladded", this.validateVisibility, this);
37906 this.on("panelremoved", this.validateVisibility, this);
37908 if(this.autoScroll){
37909 this.bodyEl.setStyle("overflow", "auto");
37911 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37913 //if(c.titlebar !== false){
37914 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37915 this.titleEl.hide();
37917 this.titleEl.show();
37918 if(this.config.title){
37919 this.titleTextEl.innerHTML = this.config.title;
37923 if(this.config.collapsed){
37924 this.collapse(true);
37926 if(this.config.hidden){
37930 if (this.unrendered_panels && this.unrendered_panels.length) {
37931 for (var i =0;i< this.unrendered_panels.length; i++) {
37932 this.add(this.unrendered_panels[i]);
37934 this.unrendered_panels = null;
37940 applyConfig : function(c)
37943 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37944 var dh = Roo.DomHelper;
37945 if(c.titlebar !== false){
37946 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37947 this.collapseBtn.on("click", this.collapse, this);
37948 this.collapseBtn.enableDisplayMode();
37950 if(c.showPin === true || this.showPin){
37951 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37952 this.stickBtn.enableDisplayMode();
37953 this.stickBtn.on("click", this.expand, this);
37954 this.stickBtn.hide();
37959 /** This region's collapsed element
37960 * @type Roo.Element */
37963 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37964 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37967 if(c.floatable !== false){
37968 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37969 this.collapsedEl.on("click", this.collapseClick, this);
37972 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37973 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37974 id: "message", unselectable: "on", style:{"float":"left"}});
37975 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37977 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37978 this.expandBtn.on("click", this.expand, this);
37982 if(this.collapseBtn){
37983 this.collapseBtn.setVisible(c.collapsible == true);
37986 this.cmargins = c.cmargins || this.cmargins ||
37987 (this.position == "west" || this.position == "east" ?
37988 {top: 0, left: 2, right:2, bottom: 0} :
37989 {top: 2, left: 0, right:0, bottom: 2});
37991 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37994 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37996 this.autoScroll = c.autoScroll || false;
38001 this.duration = c.duration || .30;
38002 this.slideDuration = c.slideDuration || .45;
38007 * Returns true if this region is currently visible.
38008 * @return {Boolean}
38010 isVisible : function(){
38011 return this.visible;
38015 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38016 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38018 //setCollapsedTitle : function(title){
38019 // title = title || " ";
38020 // if(this.collapsedTitleTextEl){
38021 // this.collapsedTitleTextEl.innerHTML = title;
38025 getBox : function(){
38027 // if(!this.collapsed){
38028 b = this.el.getBox(false, true);
38030 // b = this.collapsedEl.getBox(false, true);
38035 getMargins : function(){
38036 return this.margins;
38037 //return this.collapsed ? this.cmargins : this.margins;
38040 highlight : function(){
38041 this.el.addClass("x-layout-panel-dragover");
38044 unhighlight : function(){
38045 this.el.removeClass("x-layout-panel-dragover");
38048 updateBox : function(box)
38050 if (!this.bodyEl) {
38051 return; // not rendered yet..
38055 if(!this.collapsed){
38056 this.el.dom.style.left = box.x + "px";
38057 this.el.dom.style.top = box.y + "px";
38058 this.updateBody(box.width, box.height);
38060 this.collapsedEl.dom.style.left = box.x + "px";
38061 this.collapsedEl.dom.style.top = box.y + "px";
38062 this.collapsedEl.setSize(box.width, box.height);
38065 this.tabs.autoSizeTabs();
38069 updateBody : function(w, h)
38072 this.el.setWidth(w);
38073 w -= this.el.getBorderWidth("rl");
38074 if(this.config.adjustments){
38075 w += this.config.adjustments[0];
38078 if(h !== null && h > 0){
38079 this.el.setHeight(h);
38080 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38081 h -= this.el.getBorderWidth("tb");
38082 if(this.config.adjustments){
38083 h += this.config.adjustments[1];
38085 this.bodyEl.setHeight(h);
38087 h = this.tabs.syncHeight(h);
38090 if(this.panelSize){
38091 w = w !== null ? w : this.panelSize.width;
38092 h = h !== null ? h : this.panelSize.height;
38094 if(this.activePanel){
38095 var el = this.activePanel.getEl();
38096 w = w !== null ? w : el.getWidth();
38097 h = h !== null ? h : el.getHeight();
38098 this.panelSize = {width: w, height: h};
38099 this.activePanel.setSize(w, h);
38101 if(Roo.isIE && this.tabs){
38102 this.tabs.el.repaint();
38107 * Returns the container element for this region.
38108 * @return {Roo.Element}
38110 getEl : function(){
38115 * Hides this region.
38118 //if(!this.collapsed){
38119 this.el.dom.style.left = "-2000px";
38122 // this.collapsedEl.dom.style.left = "-2000px";
38123 // this.collapsedEl.hide();
38125 this.visible = false;
38126 this.fireEvent("visibilitychange", this, false);
38130 * Shows this region if it was previously hidden.
38133 //if(!this.collapsed){
38136 // this.collapsedEl.show();
38138 this.visible = true;
38139 this.fireEvent("visibilitychange", this, true);
38142 closeClicked : function(){
38143 if(this.activePanel){
38144 this.remove(this.activePanel);
38148 collapseClick : function(e){
38150 e.stopPropagation();
38153 e.stopPropagation();
38159 * Collapses this region.
38160 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38163 collapse : function(skipAnim, skipCheck = false){
38164 if(this.collapsed) {
38168 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38170 this.collapsed = true;
38172 this.split.el.hide();
38174 if(this.config.animate && skipAnim !== true){
38175 this.fireEvent("invalidated", this);
38176 this.animateCollapse();
38178 this.el.setLocation(-20000,-20000);
38180 this.collapsedEl.show();
38181 this.fireEvent("collapsed", this);
38182 this.fireEvent("invalidated", this);
38188 animateCollapse : function(){
38193 * Expands this region if it was previously collapsed.
38194 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38195 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38198 expand : function(e, skipAnim){
38200 e.stopPropagation();
38202 if(!this.collapsed || this.el.hasActiveFx()) {
38206 this.afterSlideIn();
38209 this.collapsed = false;
38210 if(this.config.animate && skipAnim !== true){
38211 this.animateExpand();
38215 this.split.el.show();
38217 this.collapsedEl.setLocation(-2000,-2000);
38218 this.collapsedEl.hide();
38219 this.fireEvent("invalidated", this);
38220 this.fireEvent("expanded", this);
38224 animateExpand : function(){
38228 initTabs : function()
38230 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38232 var ts = new Roo.bootstrap.panel.Tabs({
38233 el: this.bodyEl.dom,
38235 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38236 disableTooltips: this.config.disableTabTips,
38237 toolbar : this.config.toolbar
38240 if(this.config.hideTabs){
38241 ts.stripWrap.setDisplayed(false);
38244 ts.resizeTabs = this.config.resizeTabs === true;
38245 ts.minTabWidth = this.config.minTabWidth || 40;
38246 ts.maxTabWidth = this.config.maxTabWidth || 250;
38247 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38248 ts.monitorResize = false;
38249 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38250 ts.bodyEl.addClass('roo-layout-tabs-body');
38251 this.panels.each(this.initPanelAsTab, this);
38254 initPanelAsTab : function(panel){
38255 var ti = this.tabs.addTab(
38259 this.config.closeOnTab && panel.isClosable(),
38262 if(panel.tabTip !== undefined){
38263 ti.setTooltip(panel.tabTip);
38265 ti.on("activate", function(){
38266 this.setActivePanel(panel);
38269 if(this.config.closeOnTab){
38270 ti.on("beforeclose", function(t, e){
38272 this.remove(panel);
38276 panel.tabItem = ti;
38281 updatePanelTitle : function(panel, title)
38283 if(this.activePanel == panel){
38284 this.updateTitle(title);
38287 var ti = this.tabs.getTab(panel.getEl().id);
38289 if(panel.tabTip !== undefined){
38290 ti.setTooltip(panel.tabTip);
38295 updateTitle : function(title){
38296 if(this.titleTextEl && !this.config.title){
38297 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38301 setActivePanel : function(panel)
38303 panel = this.getPanel(panel);
38304 if(this.activePanel && this.activePanel != panel){
38305 if(this.activePanel.setActiveState(false) === false){
38309 this.activePanel = panel;
38310 panel.setActiveState(true);
38311 if(this.panelSize){
38312 panel.setSize(this.panelSize.width, this.panelSize.height);
38315 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38317 this.updateTitle(panel.getTitle());
38319 this.fireEvent("invalidated", this);
38321 this.fireEvent("panelactivated", this, panel);
38325 * Shows the specified panel.
38326 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38327 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38329 showPanel : function(panel)
38331 panel = this.getPanel(panel);
38334 var tab = this.tabs.getTab(panel.getEl().id);
38335 if(tab.isHidden()){
38336 this.tabs.unhideTab(tab.id);
38340 this.setActivePanel(panel);
38347 * Get the active panel for this region.
38348 * @return {Roo.ContentPanel} The active panel or null
38350 getActivePanel : function(){
38351 return this.activePanel;
38354 validateVisibility : function(){
38355 if(this.panels.getCount() < 1){
38356 this.updateTitle(" ");
38357 this.closeBtn.hide();
38360 if(!this.isVisible()){
38367 * Adds the passed ContentPanel(s) to this region.
38368 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38369 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38371 add : function(panel)
38373 if(arguments.length > 1){
38374 for(var i = 0, len = arguments.length; i < len; i++) {
38375 this.add(arguments[i]);
38380 // if we have not been rendered yet, then we can not really do much of this..
38381 if (!this.bodyEl) {
38382 this.unrendered_panels.push(panel);
38389 if(this.hasPanel(panel)){
38390 this.showPanel(panel);
38393 panel.setRegion(this);
38394 this.panels.add(panel);
38395 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38396 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38397 // and hide them... ???
38398 this.bodyEl.dom.appendChild(panel.getEl().dom);
38399 if(panel.background !== true){
38400 this.setActivePanel(panel);
38402 this.fireEvent("paneladded", this, panel);
38409 this.initPanelAsTab(panel);
38413 if(panel.background !== true){
38414 this.tabs.activate(panel.getEl().id);
38416 this.fireEvent("paneladded", this, panel);
38421 * Hides the tab for the specified panel.
38422 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38424 hidePanel : function(panel){
38425 if(this.tabs && (panel = this.getPanel(panel))){
38426 this.tabs.hideTab(panel.getEl().id);
38431 * Unhides the tab for a previously hidden panel.
38432 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38434 unhidePanel : function(panel){
38435 if(this.tabs && (panel = this.getPanel(panel))){
38436 this.tabs.unhideTab(panel.getEl().id);
38440 clearPanels : function(){
38441 while(this.panels.getCount() > 0){
38442 this.remove(this.panels.first());
38447 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38448 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38449 * @param {Boolean} preservePanel Overrides the config preservePanel option
38450 * @return {Roo.ContentPanel} The panel that was removed
38452 remove : function(panel, preservePanel)
38454 panel = this.getPanel(panel);
38459 this.fireEvent("beforeremove", this, panel, e);
38460 if(e.cancel === true){
38463 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38464 var panelId = panel.getId();
38465 this.panels.removeKey(panelId);
38467 document.body.appendChild(panel.getEl().dom);
38470 this.tabs.removeTab(panel.getEl().id);
38471 }else if (!preservePanel){
38472 this.bodyEl.dom.removeChild(panel.getEl().dom);
38474 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38475 var p = this.panels.first();
38476 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38477 tempEl.appendChild(p.getEl().dom);
38478 this.bodyEl.update("");
38479 this.bodyEl.dom.appendChild(p.getEl().dom);
38481 this.updateTitle(p.getTitle());
38483 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38484 this.setActivePanel(p);
38486 panel.setRegion(null);
38487 if(this.activePanel == panel){
38488 this.activePanel = null;
38490 if(this.config.autoDestroy !== false && preservePanel !== true){
38491 try{panel.destroy();}catch(e){}
38493 this.fireEvent("panelremoved", this, panel);
38498 * Returns the TabPanel component used by this region
38499 * @return {Roo.TabPanel}
38501 getTabs : function(){
38505 createTool : function(parentEl, className){
38506 var btn = Roo.DomHelper.append(parentEl, {
38508 cls: "x-layout-tools-button",
38511 cls: "roo-layout-tools-button-inner " + className,
38515 btn.addClassOnOver("roo-layout-tools-button-over");
38520 * Ext JS Library 1.1.1
38521 * Copyright(c) 2006-2007, Ext JS, LLC.
38523 * Originally Released Under LGPL - original licence link has changed is not relivant.
38526 * <script type="text/javascript">
38532 * @class Roo.SplitLayoutRegion
38533 * @extends Roo.LayoutRegion
38534 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38536 Roo.bootstrap.layout.Split = function(config){
38537 this.cursor = config.cursor;
38538 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38541 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38543 splitTip : "Drag to resize.",
38544 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38545 useSplitTips : false,
38547 applyConfig : function(config){
38548 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38551 onRender : function(ctr,pos) {
38553 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38554 if(!this.config.split){
38559 var splitEl = Roo.DomHelper.append(ctr.dom, {
38561 id: this.el.id + "-split",
38562 cls: "roo-layout-split roo-layout-split-"+this.position,
38565 /** The SplitBar for this region
38566 * @type Roo.SplitBar */
38567 // does not exist yet...
38568 Roo.log([this.position, this.orientation]);
38570 this.split = new Roo.bootstrap.SplitBar({
38571 dragElement : splitEl,
38572 resizingElement: this.el,
38573 orientation : this.orientation
38576 this.split.on("moved", this.onSplitMove, this);
38577 this.split.useShim = this.config.useShim === true;
38578 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38579 if(this.useSplitTips){
38580 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38582 //if(config.collapsible){
38583 // this.split.el.on("dblclick", this.collapse, this);
38586 if(typeof this.config.minSize != "undefined"){
38587 this.split.minSize = this.config.minSize;
38589 if(typeof this.config.maxSize != "undefined"){
38590 this.split.maxSize = this.config.maxSize;
38592 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38593 this.hideSplitter();
38598 getHMaxSize : function(){
38599 var cmax = this.config.maxSize || 10000;
38600 var center = this.mgr.getRegion("center");
38601 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38604 getVMaxSize : function(){
38605 var cmax = this.config.maxSize || 10000;
38606 var center = this.mgr.getRegion("center");
38607 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38610 onSplitMove : function(split, newSize){
38611 this.fireEvent("resized", this, newSize);
38615 * Returns the {@link Roo.SplitBar} for this region.
38616 * @return {Roo.SplitBar}
38618 getSplitBar : function(){
38623 this.hideSplitter();
38624 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38627 hideSplitter : function(){
38629 this.split.el.setLocation(-2000,-2000);
38630 this.split.el.hide();
38636 this.split.el.show();
38638 Roo.bootstrap.layout.Split.superclass.show.call(this);
38641 beforeSlide: function(){
38642 if(Roo.isGecko){// firefox overflow auto bug workaround
38643 this.bodyEl.clip();
38645 this.tabs.bodyEl.clip();
38647 if(this.activePanel){
38648 this.activePanel.getEl().clip();
38650 if(this.activePanel.beforeSlide){
38651 this.activePanel.beforeSlide();
38657 afterSlide : function(){
38658 if(Roo.isGecko){// firefox overflow auto bug workaround
38659 this.bodyEl.unclip();
38661 this.tabs.bodyEl.unclip();
38663 if(this.activePanel){
38664 this.activePanel.getEl().unclip();
38665 if(this.activePanel.afterSlide){
38666 this.activePanel.afterSlide();
38672 initAutoHide : function(){
38673 if(this.autoHide !== false){
38674 if(!this.autoHideHd){
38675 var st = new Roo.util.DelayedTask(this.slideIn, this);
38676 this.autoHideHd = {
38677 "mouseout": function(e){
38678 if(!e.within(this.el, true)){
38682 "mouseover" : function(e){
38688 this.el.on(this.autoHideHd);
38692 clearAutoHide : function(){
38693 if(this.autoHide !== false){
38694 this.el.un("mouseout", this.autoHideHd.mouseout);
38695 this.el.un("mouseover", this.autoHideHd.mouseover);
38699 clearMonitor : function(){
38700 Roo.get(document).un("click", this.slideInIf, this);
38703 // these names are backwards but not changed for compat
38704 slideOut : function(){
38705 if(this.isSlid || this.el.hasActiveFx()){
38708 this.isSlid = true;
38709 if(this.collapseBtn){
38710 this.collapseBtn.hide();
38712 this.closeBtnState = this.closeBtn.getStyle('display');
38713 this.closeBtn.hide();
38715 this.stickBtn.show();
38718 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38719 this.beforeSlide();
38720 this.el.setStyle("z-index", 10001);
38721 this.el.slideIn(this.getSlideAnchor(), {
38722 callback: function(){
38724 this.initAutoHide();
38725 Roo.get(document).on("click", this.slideInIf, this);
38726 this.fireEvent("slideshow", this);
38733 afterSlideIn : function(){
38734 this.clearAutoHide();
38735 this.isSlid = false;
38736 this.clearMonitor();
38737 this.el.setStyle("z-index", "");
38738 if(this.collapseBtn){
38739 this.collapseBtn.show();
38741 this.closeBtn.setStyle('display', this.closeBtnState);
38743 this.stickBtn.hide();
38745 this.fireEvent("slidehide", this);
38748 slideIn : function(cb){
38749 if(!this.isSlid || this.el.hasActiveFx()){
38753 this.isSlid = false;
38754 this.beforeSlide();
38755 this.el.slideOut(this.getSlideAnchor(), {
38756 callback: function(){
38757 this.el.setLeftTop(-10000, -10000);
38759 this.afterSlideIn();
38767 slideInIf : function(e){
38768 if(!e.within(this.el)){
38773 animateCollapse : function(){
38774 this.beforeSlide();
38775 this.el.setStyle("z-index", 20000);
38776 var anchor = this.getSlideAnchor();
38777 this.el.slideOut(anchor, {
38778 callback : function(){
38779 this.el.setStyle("z-index", "");
38780 this.collapsedEl.slideIn(anchor, {duration:.3});
38782 this.el.setLocation(-10000,-10000);
38784 this.fireEvent("collapsed", this);
38791 animateExpand : function(){
38792 this.beforeSlide();
38793 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38794 this.el.setStyle("z-index", 20000);
38795 this.collapsedEl.hide({
38798 this.el.slideIn(this.getSlideAnchor(), {
38799 callback : function(){
38800 this.el.setStyle("z-index", "");
38803 this.split.el.show();
38805 this.fireEvent("invalidated", this);
38806 this.fireEvent("expanded", this);
38834 getAnchor : function(){
38835 return this.anchors[this.position];
38838 getCollapseAnchor : function(){
38839 return this.canchors[this.position];
38842 getSlideAnchor : function(){
38843 return this.sanchors[this.position];
38846 getAlignAdj : function(){
38847 var cm = this.cmargins;
38848 switch(this.position){
38864 getExpandAdj : function(){
38865 var c = this.collapsedEl, cm = this.cmargins;
38866 switch(this.position){
38868 return [-(cm.right+c.getWidth()+cm.left), 0];
38871 return [cm.right+c.getWidth()+cm.left, 0];
38874 return [0, -(cm.top+cm.bottom+c.getHeight())];
38877 return [0, cm.top+cm.bottom+c.getHeight()];
38883 * Ext JS Library 1.1.1
38884 * Copyright(c) 2006-2007, Ext JS, LLC.
38886 * Originally Released Under LGPL - original licence link has changed is not relivant.
38889 * <script type="text/javascript">
38892 * These classes are private internal classes
38894 Roo.bootstrap.layout.Center = function(config){
38895 config.region = "center";
38896 Roo.bootstrap.layout.Region.call(this, config);
38897 this.visible = true;
38898 this.minWidth = config.minWidth || 20;
38899 this.minHeight = config.minHeight || 20;
38902 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38904 // center panel can't be hidden
38908 // center panel can't be hidden
38911 getMinWidth: function(){
38912 return this.minWidth;
38915 getMinHeight: function(){
38916 return this.minHeight;
38930 Roo.bootstrap.layout.North = function(config)
38932 config.region = 'north';
38933 config.cursor = 'n-resize';
38935 Roo.bootstrap.layout.Split.call(this, config);
38939 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38940 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38941 this.split.el.addClass("roo-layout-split-v");
38943 var size = config.initialSize || config.height;
38944 if(typeof size != "undefined"){
38945 this.el.setHeight(size);
38948 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38950 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38954 getBox : function(){
38955 if(this.collapsed){
38956 return this.collapsedEl.getBox();
38958 var box = this.el.getBox();
38960 box.height += this.split.el.getHeight();
38965 updateBox : function(box){
38966 if(this.split && !this.collapsed){
38967 box.height -= this.split.el.getHeight();
38968 this.split.el.setLeft(box.x);
38969 this.split.el.setTop(box.y+box.height);
38970 this.split.el.setWidth(box.width);
38972 if(this.collapsed){
38973 this.updateBody(box.width, null);
38975 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38983 Roo.bootstrap.layout.South = function(config){
38984 config.region = 'south';
38985 config.cursor = 's-resize';
38986 Roo.bootstrap.layout.Split.call(this, config);
38988 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38989 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38990 this.split.el.addClass("roo-layout-split-v");
38992 var size = config.initialSize || config.height;
38993 if(typeof size != "undefined"){
38994 this.el.setHeight(size);
38998 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38999 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39000 getBox : function(){
39001 if(this.collapsed){
39002 return this.collapsedEl.getBox();
39004 var box = this.el.getBox();
39006 var sh = this.split.el.getHeight();
39013 updateBox : function(box){
39014 if(this.split && !this.collapsed){
39015 var sh = this.split.el.getHeight();
39018 this.split.el.setLeft(box.x);
39019 this.split.el.setTop(box.y-sh);
39020 this.split.el.setWidth(box.width);
39022 if(this.collapsed){
39023 this.updateBody(box.width, null);
39025 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39029 Roo.bootstrap.layout.East = function(config){
39030 config.region = "east";
39031 config.cursor = "e-resize";
39032 Roo.bootstrap.layout.Split.call(this, config);
39034 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39035 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39036 this.split.el.addClass("roo-layout-split-h");
39038 var size = config.initialSize || config.width;
39039 if(typeof size != "undefined"){
39040 this.el.setWidth(size);
39043 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39044 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39045 getBox : function(){
39046 if(this.collapsed){
39047 return this.collapsedEl.getBox();
39049 var box = this.el.getBox();
39051 var sw = this.split.el.getWidth();
39058 updateBox : function(box){
39059 if(this.split && !this.collapsed){
39060 var sw = this.split.el.getWidth();
39062 this.split.el.setLeft(box.x);
39063 this.split.el.setTop(box.y);
39064 this.split.el.setHeight(box.height);
39067 if(this.collapsed){
39068 this.updateBody(null, box.height);
39070 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39074 Roo.bootstrap.layout.West = function(config){
39075 config.region = "west";
39076 config.cursor = "w-resize";
39078 Roo.bootstrap.layout.Split.call(this, config);
39080 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39081 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39082 this.split.el.addClass("roo-layout-split-h");
39086 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39087 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39089 onRender: function(ctr, pos)
39091 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39092 var size = this.config.initialSize || this.config.width;
39093 if(typeof size != "undefined"){
39094 this.el.setWidth(size);
39098 getBox : function(){
39099 if(this.collapsed){
39100 return this.collapsedEl.getBox();
39102 var box = this.el.getBox();
39104 box.width += this.split.el.getWidth();
39109 updateBox : function(box){
39110 if(this.split && !this.collapsed){
39111 var sw = this.split.el.getWidth();
39113 this.split.el.setLeft(box.x+box.width);
39114 this.split.el.setTop(box.y);
39115 this.split.el.setHeight(box.height);
39117 if(this.collapsed){
39118 this.updateBody(null, box.height);
39120 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39122 });Roo.namespace("Roo.bootstrap.panel");/*
39124 * Ext JS Library 1.1.1
39125 * Copyright(c) 2006-2007, Ext JS, LLC.
39127 * Originally Released Under LGPL - original licence link has changed is not relivant.
39130 * <script type="text/javascript">
39133 * @class Roo.ContentPanel
39134 * @extends Roo.util.Observable
39135 * A basic ContentPanel element.
39136 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39137 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39138 * @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
39139 * @cfg {Boolean} closable True if the panel can be closed/removed
39140 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39141 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39142 * @cfg {Toolbar} toolbar A toolbar for this panel
39143 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39144 * @cfg {String} title The title for this panel
39145 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39146 * @cfg {String} url Calls {@link #setUrl} with this value
39147 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39148 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39149 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39150 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39151 * @cfg {Boolean} badges render the badges
39154 * Create a new ContentPanel.
39155 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39156 * @param {String/Object} config A string to set only the title or a config object
39157 * @param {String} content (optional) Set the HTML content for this panel
39158 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39160 Roo.bootstrap.panel.Content = function( config){
39162 this.tpl = config.tpl || false;
39164 var el = config.el;
39165 var content = config.content;
39167 if(config.autoCreate){ // xtype is available if this is called from factory
39170 this.el = Roo.get(el);
39171 if(!this.el && config && config.autoCreate){
39172 if(typeof config.autoCreate == "object"){
39173 if(!config.autoCreate.id){
39174 config.autoCreate.id = config.id||el;
39176 this.el = Roo.DomHelper.append(document.body,
39177 config.autoCreate, true);
39179 var elcfg = { tag: "div",
39180 cls: "roo-layout-inactive-content",
39184 elcfg.html = config.html;
39188 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39191 this.closable = false;
39192 this.loaded = false;
39193 this.active = false;
39196 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39198 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39200 this.wrapEl = this.el; //this.el.wrap();
39202 if (config.toolbar.items) {
39203 ti = config.toolbar.items ;
39204 delete config.toolbar.items ;
39208 this.toolbar.render(this.wrapEl, 'before');
39209 for(var i =0;i < ti.length;i++) {
39210 // Roo.log(['add child', items[i]]);
39211 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39213 this.toolbar.items = nitems;
39214 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39215 delete config.toolbar;
39219 // xtype created footer. - not sure if will work as we normally have to render first..
39220 if (this.footer && !this.footer.el && this.footer.xtype) {
39221 if (!this.wrapEl) {
39222 this.wrapEl = this.el.wrap();
39225 this.footer.container = this.wrapEl.createChild();
39227 this.footer = Roo.factory(this.footer, Roo);
39232 if(typeof config == "string"){
39233 this.title = config;
39235 Roo.apply(this, config);
39239 this.resizeEl = Roo.get(this.resizeEl, true);
39241 this.resizeEl = this.el;
39243 // handle view.xtype
39251 * Fires when this panel is activated.
39252 * @param {Roo.ContentPanel} this
39256 * @event deactivate
39257 * Fires when this panel is activated.
39258 * @param {Roo.ContentPanel} this
39260 "deactivate" : true,
39264 * Fires when this panel is resized if fitToFrame is true.
39265 * @param {Roo.ContentPanel} this
39266 * @param {Number} width The width after any component adjustments
39267 * @param {Number} height The height after any component adjustments
39273 * Fires when this tab is created
39274 * @param {Roo.ContentPanel} this
39285 if(this.autoScroll){
39286 this.resizeEl.setStyle("overflow", "auto");
39288 // fix randome scrolling
39289 //this.el.on('scroll', function() {
39290 // Roo.log('fix random scolling');
39291 // this.scrollTo('top',0);
39294 content = content || this.content;
39296 this.setContent(content);
39298 if(config && config.url){
39299 this.setUrl(this.url, this.params, this.loadOnce);
39304 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39306 if (this.view && typeof(this.view.xtype) != 'undefined') {
39307 this.view.el = this.el.appendChild(document.createElement("div"));
39308 this.view = Roo.factory(this.view);
39309 this.view.render && this.view.render(false, '');
39313 this.fireEvent('render', this);
39316 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39320 setRegion : function(region){
39321 this.region = region;
39322 this.setActiveClass(region && !this.background);
39326 setActiveClass: function(state)
39329 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39330 this.el.setStyle('position','relative');
39332 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39333 this.el.setStyle('position', 'absolute');
39338 * Returns the toolbar for this Panel if one was configured.
39339 * @return {Roo.Toolbar}
39341 getToolbar : function(){
39342 return this.toolbar;
39345 setActiveState : function(active)
39347 this.active = active;
39348 this.setActiveClass(active);
39350 if(this.fireEvent("deactivate", this) === false){
39355 this.fireEvent("activate", this);
39359 * Updates this panel's element
39360 * @param {String} content The new content
39361 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39363 setContent : function(content, loadScripts){
39364 this.el.update(content, loadScripts);
39367 ignoreResize : function(w, h){
39368 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39371 this.lastSize = {width: w, height: h};
39376 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39377 * @return {Roo.UpdateManager} The UpdateManager
39379 getUpdateManager : function(){
39380 return this.el.getUpdateManager();
39383 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39384 * @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:
39387 url: "your-url.php",
39388 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39389 callback: yourFunction,
39390 scope: yourObject, //(optional scope)
39393 text: "Loading...",
39398 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39399 * 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.
39400 * @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}
39401 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39402 * @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.
39403 * @return {Roo.ContentPanel} this
39406 var um = this.el.getUpdateManager();
39407 um.update.apply(um, arguments);
39413 * 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.
39414 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39415 * @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)
39416 * @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)
39417 * @return {Roo.UpdateManager} The UpdateManager
39419 setUrl : function(url, params, loadOnce){
39420 if(this.refreshDelegate){
39421 this.removeListener("activate", this.refreshDelegate);
39423 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39424 this.on("activate", this.refreshDelegate);
39425 return this.el.getUpdateManager();
39428 _handleRefresh : function(url, params, loadOnce){
39429 if(!loadOnce || !this.loaded){
39430 var updater = this.el.getUpdateManager();
39431 updater.update(url, params, this._setLoaded.createDelegate(this));
39435 _setLoaded : function(){
39436 this.loaded = true;
39440 * Returns this panel's id
39443 getId : function(){
39448 * Returns this panel's element - used by regiosn to add.
39449 * @return {Roo.Element}
39451 getEl : function(){
39452 return this.wrapEl || this.el;
39457 adjustForComponents : function(width, height)
39459 //Roo.log('adjustForComponents ');
39460 if(this.resizeEl != this.el){
39461 width -= this.el.getFrameWidth('lr');
39462 height -= this.el.getFrameWidth('tb');
39465 var te = this.toolbar.getEl();
39466 te.setWidth(width);
39467 height -= te.getHeight();
39470 var te = this.footer.getEl();
39471 te.setWidth(width);
39472 height -= te.getHeight();
39476 if(this.adjustments){
39477 width += this.adjustments[0];
39478 height += this.adjustments[1];
39480 return {"width": width, "height": height};
39483 setSize : function(width, height){
39484 if(this.fitToFrame && !this.ignoreResize(width, height)){
39485 if(this.fitContainer && this.resizeEl != this.el){
39486 this.el.setSize(width, height);
39488 var size = this.adjustForComponents(width, height);
39489 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39490 this.fireEvent('resize', this, size.width, size.height);
39495 * Returns this panel's title
39498 getTitle : function(){
39500 if (typeof(this.title) != 'object') {
39505 for (var k in this.title) {
39506 if (!this.title.hasOwnProperty(k)) {
39510 if (k.indexOf('-') >= 0) {
39511 var s = k.split('-');
39512 for (var i = 0; i<s.length; i++) {
39513 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39516 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39523 * Set this panel's title
39524 * @param {String} title
39526 setTitle : function(title){
39527 this.title = title;
39529 this.region.updatePanelTitle(this, title);
39534 * Returns true is this panel was configured to be closable
39535 * @return {Boolean}
39537 isClosable : function(){
39538 return this.closable;
39541 beforeSlide : function(){
39543 this.resizeEl.clip();
39546 afterSlide : function(){
39548 this.resizeEl.unclip();
39552 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39553 * Will fail silently if the {@link #setUrl} method has not been called.
39554 * This does not activate the panel, just updates its content.
39556 refresh : function(){
39557 if(this.refreshDelegate){
39558 this.loaded = false;
39559 this.refreshDelegate();
39564 * Destroys this panel
39566 destroy : function(){
39567 this.el.removeAllListeners();
39568 var tempEl = document.createElement("span");
39569 tempEl.appendChild(this.el.dom);
39570 tempEl.innerHTML = "";
39576 * form - if the content panel contains a form - this is a reference to it.
39577 * @type {Roo.form.Form}
39581 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39582 * This contains a reference to it.
39588 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39598 * @param {Object} cfg Xtype definition of item to add.
39602 getChildContainer: function () {
39603 return this.getEl();
39608 var ret = new Roo.factory(cfg);
39613 if (cfg.xtype.match(/^Form$/)) {
39616 //if (this.footer) {
39617 // el = this.footer.container.insertSibling(false, 'before');
39619 el = this.el.createChild();
39622 this.form = new Roo.form.Form(cfg);
39625 if ( this.form.allItems.length) {
39626 this.form.render(el.dom);
39630 // should only have one of theses..
39631 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39632 // views.. should not be just added - used named prop 'view''
39634 cfg.el = this.el.appendChild(document.createElement("div"));
39637 var ret = new Roo.factory(cfg);
39639 ret.render && ret.render(false, ''); // render blank..
39649 * @class Roo.bootstrap.panel.Grid
39650 * @extends Roo.bootstrap.panel.Content
39652 * Create a new GridPanel.
39653 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39654 * @param {Object} config A the config object
39660 Roo.bootstrap.panel.Grid = function(config)
39664 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39665 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39667 config.el = this.wrapper;
39668 //this.el = this.wrapper;
39670 if (config.container) {
39671 // ctor'ed from a Border/panel.grid
39674 this.wrapper.setStyle("overflow", "hidden");
39675 this.wrapper.addClass('roo-grid-container');
39680 if(config.toolbar){
39681 var tool_el = this.wrapper.createChild();
39682 this.toolbar = Roo.factory(config.toolbar);
39684 if (config.toolbar.items) {
39685 ti = config.toolbar.items ;
39686 delete config.toolbar.items ;
39690 this.toolbar.render(tool_el);
39691 for(var i =0;i < ti.length;i++) {
39692 // Roo.log(['add child', items[i]]);
39693 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39695 this.toolbar.items = nitems;
39697 delete config.toolbar;
39700 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39701 config.grid.scrollBody = true;;
39702 config.grid.monitorWindowResize = false; // turn off autosizing
39703 config.grid.autoHeight = false;
39704 config.grid.autoWidth = false;
39706 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39708 if (config.background) {
39709 // render grid on panel activation (if panel background)
39710 this.on('activate', function(gp) {
39711 if (!gp.grid.rendered) {
39712 gp.grid.render(this.wrapper);
39713 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39718 this.grid.render(this.wrapper);
39719 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39722 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39723 // ??? needed ??? config.el = this.wrapper;
39728 // xtype created footer. - not sure if will work as we normally have to render first..
39729 if (this.footer && !this.footer.el && this.footer.xtype) {
39731 var ctr = this.grid.getView().getFooterPanel(true);
39732 this.footer.dataSource = this.grid.dataSource;
39733 this.footer = Roo.factory(this.footer, Roo);
39734 this.footer.render(ctr);
39744 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39745 getId : function(){
39746 return this.grid.id;
39750 * Returns the grid for this panel
39751 * @return {Roo.bootstrap.Table}
39753 getGrid : function(){
39757 setSize : function(width, height){
39758 if(!this.ignoreResize(width, height)){
39759 var grid = this.grid;
39760 var size = this.adjustForComponents(width, height);
39761 // tfoot is not a footer?
39764 var gridel = grid.getGridEl();
39765 gridel.setSize(size.width, size.height);
39767 var tbd = grid.getGridEl().select('tbody', true).first();
39768 var thd = grid.getGridEl().select('thead',true).first();
39769 var tbf= grid.getGridEl().select('tfoot', true).first();
39772 size.height -= thd.getHeight();
39775 size.height -= thd.getHeight();
39778 tbd.setSize(size.width, size.height );
39779 // this is for the account management tab -seems to work there.
39780 var thd = grid.getGridEl().select('thead',true).first();
39782 // tbd.setSize(size.width, size.height - thd.getHeight());
39791 beforeSlide : function(){
39792 this.grid.getView().scroller.clip();
39795 afterSlide : function(){
39796 this.grid.getView().scroller.unclip();
39799 destroy : function(){
39800 this.grid.destroy();
39802 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39807 * @class Roo.bootstrap.panel.Nest
39808 * @extends Roo.bootstrap.panel.Content
39810 * Create a new Panel, that can contain a layout.Border.
39813 * @param {Roo.BorderLayout} layout The layout for this panel
39814 * @param {String/Object} config A string to set only the title or a config object
39816 Roo.bootstrap.panel.Nest = function(config)
39818 // construct with only one argument..
39819 /* FIXME - implement nicer consturctors
39820 if (layout.layout) {
39822 layout = config.layout;
39823 delete config.layout;
39825 if (layout.xtype && !layout.getEl) {
39826 // then layout needs constructing..
39827 layout = Roo.factory(layout, Roo);
39831 config.el = config.layout.getEl();
39833 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39835 config.layout.monitorWindowResize = false; // turn off autosizing
39836 this.layout = config.layout;
39837 this.layout.getEl().addClass("roo-layout-nested-layout");
39838 this.layout.parent = this;
39845 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39847 setSize : function(width, height){
39848 if(!this.ignoreResize(width, height)){
39849 var size = this.adjustForComponents(width, height);
39850 var el = this.layout.getEl();
39851 if (size.height < 1) {
39852 el.setWidth(size.width);
39854 el.setSize(size.width, size.height);
39856 var touch = el.dom.offsetWidth;
39857 this.layout.layout();
39858 // ie requires a double layout on the first pass
39859 if(Roo.isIE && !this.initialized){
39860 this.initialized = true;
39861 this.layout.layout();
39866 // activate all subpanels if not currently active..
39868 setActiveState : function(active){
39869 this.active = active;
39870 this.setActiveClass(active);
39873 this.fireEvent("deactivate", this);
39877 this.fireEvent("activate", this);
39878 // not sure if this should happen before or after..
39879 if (!this.layout) {
39880 return; // should not happen..
39883 for (var r in this.layout.regions) {
39884 reg = this.layout.getRegion(r);
39885 if (reg.getActivePanel()) {
39886 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39887 reg.setActivePanel(reg.getActivePanel());
39890 if (!reg.panels.length) {
39893 reg.showPanel(reg.getPanel(0));
39902 * Returns the nested BorderLayout for this panel
39903 * @return {Roo.BorderLayout}
39905 getLayout : function(){
39906 return this.layout;
39910 * Adds a xtype elements to the layout of the nested panel
39914 xtype : 'ContentPanel',
39921 xtype : 'NestedLayoutPanel',
39927 items : [ ... list of content panels or nested layout panels.. ]
39931 * @param {Object} cfg Xtype definition of item to add.
39933 addxtype : function(cfg) {
39934 return this.layout.addxtype(cfg);
39939 * Ext JS Library 1.1.1
39940 * Copyright(c) 2006-2007, Ext JS, LLC.
39942 * Originally Released Under LGPL - original licence link has changed is not relivant.
39945 * <script type="text/javascript">
39948 * @class Roo.TabPanel
39949 * @extends Roo.util.Observable
39950 * A lightweight tab container.
39954 // basic tabs 1, built from existing content
39955 var tabs = new Roo.TabPanel("tabs1");
39956 tabs.addTab("script", "View Script");
39957 tabs.addTab("markup", "View Markup");
39958 tabs.activate("script");
39960 // more advanced tabs, built from javascript
39961 var jtabs = new Roo.TabPanel("jtabs");
39962 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39964 // set up the UpdateManager
39965 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39966 var updater = tab2.getUpdateManager();
39967 updater.setDefaultUrl("ajax1.htm");
39968 tab2.on('activate', updater.refresh, updater, true);
39970 // Use setUrl for Ajax loading
39971 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39972 tab3.setUrl("ajax2.htm", null, true);
39975 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39978 jtabs.activate("jtabs-1");
39981 * Create a new TabPanel.
39982 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39983 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39985 Roo.bootstrap.panel.Tabs = function(config){
39987 * The container element for this TabPanel.
39988 * @type Roo.Element
39990 this.el = Roo.get(config.el);
39993 if(typeof config == "boolean"){
39994 this.tabPosition = config ? "bottom" : "top";
39996 Roo.apply(this, config);
40000 if(this.tabPosition == "bottom"){
40001 // if tabs are at the bottom = create the body first.
40002 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40003 this.el.addClass("roo-tabs-bottom");
40005 // next create the tabs holders
40007 if (this.tabPosition == "west"){
40009 var reg = this.region; // fake it..
40011 if (!reg.mgr.parent) {
40014 reg = reg.mgr.parent.region;
40016 Roo.log("got nest?");
40018 if (reg.mgr.getRegion('west')) {
40019 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40020 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40021 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40022 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40023 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40031 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40032 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40033 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40034 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40039 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40042 // finally - if tabs are at the top, then create the body last..
40043 if(this.tabPosition != "bottom"){
40044 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40045 * @type Roo.Element
40047 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40048 this.el.addClass("roo-tabs-top");
40052 this.bodyEl.setStyle("position", "relative");
40054 this.active = null;
40055 this.activateDelegate = this.activate.createDelegate(this);
40060 * Fires when the active tab changes
40061 * @param {Roo.TabPanel} this
40062 * @param {Roo.TabPanelItem} activePanel The new active tab
40066 * @event beforetabchange
40067 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40068 * @param {Roo.TabPanel} this
40069 * @param {Object} e Set cancel to true on this object to cancel the tab change
40070 * @param {Roo.TabPanelItem} tab The tab being changed to
40072 "beforetabchange" : true
40075 Roo.EventManager.onWindowResize(this.onResize, this);
40076 this.cpad = this.el.getPadding("lr");
40077 this.hiddenCount = 0;
40080 // toolbar on the tabbar support...
40081 if (this.toolbar) {
40082 alert("no toolbar support yet");
40083 this.toolbar = false;
40085 var tcfg = this.toolbar;
40086 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40087 this.toolbar = new Roo.Toolbar(tcfg);
40088 if (Roo.isSafari) {
40089 var tbl = tcfg.container.child('table', true);
40090 tbl.setAttribute('width', '100%');
40098 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40101 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40103 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40105 tabPosition : "top",
40107 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40109 currentTabWidth : 0,
40111 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40115 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40119 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40121 preferredTabWidth : 175,
40123 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40125 resizeTabs : false,
40127 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40129 monitorResize : true,
40131 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40133 toolbar : false, // set by caller..
40135 region : false, /// set by caller
40137 disableTooltips : true, // not used yet...
40140 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40141 * @param {String} id The id of the div to use <b>or create</b>
40142 * @param {String} text The text for the tab
40143 * @param {String} content (optional) Content to put in the TabPanelItem body
40144 * @param {Boolean} closable (optional) True to create a close icon on the tab
40145 * @return {Roo.TabPanelItem} The created TabPanelItem
40147 addTab : function(id, text, content, closable, tpl)
40149 var item = new Roo.bootstrap.panel.TabItem({
40153 closable : closable,
40156 this.addTabItem(item);
40158 item.setContent(content);
40164 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40165 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40166 * @return {Roo.TabPanelItem}
40168 getTab : function(id){
40169 return this.items[id];
40173 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40174 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40176 hideTab : function(id){
40177 var t = this.items[id];
40180 this.hiddenCount++;
40181 this.autoSizeTabs();
40186 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40187 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40189 unhideTab : function(id){
40190 var t = this.items[id];
40192 t.setHidden(false);
40193 this.hiddenCount--;
40194 this.autoSizeTabs();
40199 * Adds an existing {@link Roo.TabPanelItem}.
40200 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40202 addTabItem : function(item)
40204 this.items[item.id] = item;
40205 this.items.push(item);
40206 this.autoSizeTabs();
40207 // if(this.resizeTabs){
40208 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40209 // this.autoSizeTabs();
40211 // item.autoSize();
40216 * Removes a {@link Roo.TabPanelItem}.
40217 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40219 removeTab : function(id){
40220 var items = this.items;
40221 var tab = items[id];
40222 if(!tab) { return; }
40223 var index = items.indexOf(tab);
40224 if(this.active == tab && items.length > 1){
40225 var newTab = this.getNextAvailable(index);
40230 this.stripEl.dom.removeChild(tab.pnode.dom);
40231 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40232 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40234 items.splice(index, 1);
40235 delete this.items[tab.id];
40236 tab.fireEvent("close", tab);
40237 tab.purgeListeners();
40238 this.autoSizeTabs();
40241 getNextAvailable : function(start){
40242 var items = this.items;
40244 // look for a next tab that will slide over to
40245 // replace the one being removed
40246 while(index < items.length){
40247 var item = items[++index];
40248 if(item && !item.isHidden()){
40252 // if one isn't found select the previous tab (on the left)
40255 var item = items[--index];
40256 if(item && !item.isHidden()){
40264 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40265 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40267 disableTab : function(id){
40268 var tab = this.items[id];
40269 if(tab && this.active != tab){
40275 * Enables a {@link Roo.TabPanelItem} that is disabled.
40276 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40278 enableTab : function(id){
40279 var tab = this.items[id];
40284 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40285 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40286 * @return {Roo.TabPanelItem} The TabPanelItem.
40288 activate : function(id)
40290 //Roo.log('activite:' + id);
40292 var tab = this.items[id];
40296 if(tab == this.active || tab.disabled){
40300 this.fireEvent("beforetabchange", this, e, tab);
40301 if(e.cancel !== true && !tab.disabled){
40303 this.active.hide();
40305 this.active = this.items[id];
40306 this.active.show();
40307 this.fireEvent("tabchange", this, this.active);
40313 * Gets the active {@link Roo.TabPanelItem}.
40314 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40316 getActiveTab : function(){
40317 return this.active;
40321 * Updates the tab body element to fit the height of the container element
40322 * for overflow scrolling
40323 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40325 syncHeight : function(targetHeight){
40326 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40327 var bm = this.bodyEl.getMargins();
40328 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40329 this.bodyEl.setHeight(newHeight);
40333 onResize : function(){
40334 if(this.monitorResize){
40335 this.autoSizeTabs();
40340 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40342 beginUpdate : function(){
40343 this.updating = true;
40347 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40349 endUpdate : function(){
40350 this.updating = false;
40351 this.autoSizeTabs();
40355 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40357 autoSizeTabs : function()
40359 var count = this.items.length;
40360 var vcount = count - this.hiddenCount;
40363 this.stripEl.hide();
40365 this.stripEl.show();
40368 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40373 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40374 var availWidth = Math.floor(w / vcount);
40375 var b = this.stripBody;
40376 if(b.getWidth() > w){
40377 var tabs = this.items;
40378 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40379 if(availWidth < this.minTabWidth){
40380 /*if(!this.sleft){ // incomplete scrolling code
40381 this.createScrollButtons();
40384 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40387 if(this.currentTabWidth < this.preferredTabWidth){
40388 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40394 * Returns the number of tabs in this TabPanel.
40397 getCount : function(){
40398 return this.items.length;
40402 * Resizes all the tabs to the passed width
40403 * @param {Number} The new width
40405 setTabWidth : function(width){
40406 this.currentTabWidth = width;
40407 for(var i = 0, len = this.items.length; i < len; i++) {
40408 if(!this.items[i].isHidden()) {
40409 this.items[i].setWidth(width);
40415 * Destroys this TabPanel
40416 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40418 destroy : function(removeEl){
40419 Roo.EventManager.removeResizeListener(this.onResize, this);
40420 for(var i = 0, len = this.items.length; i < len; i++){
40421 this.items[i].purgeListeners();
40423 if(removeEl === true){
40424 this.el.update("");
40429 createStrip : function(container)
40431 var strip = document.createElement("nav");
40432 strip.className = Roo.bootstrap.version == 4 ?
40433 "navbar-light bg-light" :
40434 "navbar navbar-default"; //"x-tabs-wrap";
40435 container.appendChild(strip);
40439 createStripList : function(strip)
40441 // div wrapper for retard IE
40442 // returns the "tr" element.
40443 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40444 //'<div class="x-tabs-strip-wrap">'+
40445 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40446 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40447 return strip.firstChild; //.firstChild.firstChild.firstChild;
40449 createBody : function(container)
40451 var body = document.createElement("div");
40452 Roo.id(body, "tab-body");
40453 //Roo.fly(body).addClass("x-tabs-body");
40454 Roo.fly(body).addClass("tab-content");
40455 container.appendChild(body);
40458 createItemBody :function(bodyEl, id){
40459 var body = Roo.getDom(id);
40461 body = document.createElement("div");
40464 //Roo.fly(body).addClass("x-tabs-item-body");
40465 Roo.fly(body).addClass("tab-pane");
40466 bodyEl.insertBefore(body, bodyEl.firstChild);
40470 createStripElements : function(stripEl, text, closable, tpl)
40472 var td = document.createElement("li"); // was td..
40473 td.className = 'nav-item';
40475 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40478 stripEl.appendChild(td);
40480 td.className = "x-tabs-closable";
40481 if(!this.closeTpl){
40482 this.closeTpl = new Roo.Template(
40483 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40484 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40485 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40488 var el = this.closeTpl.overwrite(td, {"text": text});
40489 var close = el.getElementsByTagName("div")[0];
40490 var inner = el.getElementsByTagName("em")[0];
40491 return {"el": el, "close": close, "inner": inner};
40494 // not sure what this is..
40495 // if(!this.tabTpl){
40496 //this.tabTpl = new Roo.Template(
40497 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40498 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40500 // this.tabTpl = new Roo.Template(
40501 // '<a href="#">' +
40502 // '<span unselectable="on"' +
40503 // (this.disableTooltips ? '' : ' title="{text}"') +
40504 // ' >{text}</span></a>'
40510 var template = tpl || this.tabTpl || false;
40513 template = new Roo.Template(
40514 Roo.bootstrap.version == 4 ?
40516 '<a class="nav-link" href="#" unselectable="on"' +
40517 (this.disableTooltips ? '' : ' title="{text}"') +
40520 '<a class="nav-link" href="#">' +
40521 '<span unselectable="on"' +
40522 (this.disableTooltips ? '' : ' title="{text}"') +
40523 ' >{text}</span></a>'
40528 switch (typeof(template)) {
40532 template = new Roo.Template(template);
40538 var el = template.overwrite(td, {"text": text});
40540 var inner = el.getElementsByTagName("span")[0];
40542 return {"el": el, "inner": inner};
40550 * @class Roo.TabPanelItem
40551 * @extends Roo.util.Observable
40552 * Represents an individual item (tab plus body) in a TabPanel.
40553 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40554 * @param {String} id The id of this TabPanelItem
40555 * @param {String} text The text for the tab of this TabPanelItem
40556 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40558 Roo.bootstrap.panel.TabItem = function(config){
40560 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40561 * @type Roo.TabPanel
40563 this.tabPanel = config.panel;
40565 * The id for this TabPanelItem
40568 this.id = config.id;
40570 this.disabled = false;
40572 this.text = config.text;
40574 this.loaded = false;
40575 this.closable = config.closable;
40578 * The body element for this TabPanelItem.
40579 * @type Roo.Element
40581 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40582 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40583 this.bodyEl.setStyle("display", "block");
40584 this.bodyEl.setStyle("zoom", "1");
40585 //this.hideAction();
40587 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40589 this.el = Roo.get(els.el);
40590 this.inner = Roo.get(els.inner, true);
40591 this.textEl = Roo.bootstrap.version == 4 ?
40592 this.el : Roo.get(this.el.dom.firstChild, true);
40594 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40595 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40598 // this.el.on("mousedown", this.onTabMouseDown, this);
40599 this.el.on("click", this.onTabClick, this);
40601 if(config.closable){
40602 var c = Roo.get(els.close, true);
40603 c.dom.title = this.closeText;
40604 c.addClassOnOver("close-over");
40605 c.on("click", this.closeClick, this);
40611 * Fires when this tab becomes the active tab.
40612 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40613 * @param {Roo.TabPanelItem} this
40617 * @event beforeclose
40618 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40619 * @param {Roo.TabPanelItem} this
40620 * @param {Object} e Set cancel to true on this object to cancel the close.
40622 "beforeclose": true,
40625 * Fires when this tab is closed.
40626 * @param {Roo.TabPanelItem} this
40630 * @event deactivate
40631 * Fires when this tab is no longer the active tab.
40632 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40633 * @param {Roo.TabPanelItem} this
40635 "deactivate" : true
40637 this.hidden = false;
40639 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40642 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40644 purgeListeners : function(){
40645 Roo.util.Observable.prototype.purgeListeners.call(this);
40646 this.el.removeAllListeners();
40649 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40652 this.status_node.addClass("active");
40655 this.tabPanel.stripWrap.repaint();
40657 this.fireEvent("activate", this.tabPanel, this);
40661 * Returns true if this tab is the active tab.
40662 * @return {Boolean}
40664 isActive : function(){
40665 return this.tabPanel.getActiveTab() == this;
40669 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40672 this.status_node.removeClass("active");
40674 this.fireEvent("deactivate", this.tabPanel, this);
40677 hideAction : function(){
40678 this.bodyEl.hide();
40679 this.bodyEl.setStyle("position", "absolute");
40680 this.bodyEl.setLeft("-20000px");
40681 this.bodyEl.setTop("-20000px");
40684 showAction : function(){
40685 this.bodyEl.setStyle("position", "relative");
40686 this.bodyEl.setTop("");
40687 this.bodyEl.setLeft("");
40688 this.bodyEl.show();
40692 * Set the tooltip for the tab.
40693 * @param {String} tooltip The tab's tooltip
40695 setTooltip : function(text){
40696 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40697 this.textEl.dom.qtip = text;
40698 this.textEl.dom.removeAttribute('title');
40700 this.textEl.dom.title = text;
40704 onTabClick : function(e){
40705 e.preventDefault();
40706 this.tabPanel.activate(this.id);
40709 onTabMouseDown : function(e){
40710 e.preventDefault();
40711 this.tabPanel.activate(this.id);
40714 getWidth : function(){
40715 return this.inner.getWidth();
40718 setWidth : function(width){
40719 var iwidth = width - this.linode.getPadding("lr");
40720 this.inner.setWidth(iwidth);
40721 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40722 this.linode.setWidth(width);
40726 * Show or hide the tab
40727 * @param {Boolean} hidden True to hide or false to show.
40729 setHidden : function(hidden){
40730 this.hidden = hidden;
40731 this.linode.setStyle("display", hidden ? "none" : "");
40735 * Returns true if this tab is "hidden"
40736 * @return {Boolean}
40738 isHidden : function(){
40739 return this.hidden;
40743 * Returns the text for this tab
40746 getText : function(){
40750 autoSize : function(){
40751 //this.el.beginMeasure();
40752 this.textEl.setWidth(1);
40754 * #2804 [new] Tabs in Roojs
40755 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40757 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40758 //this.el.endMeasure();
40762 * Sets the text for the tab (Note: this also sets the tooltip text)
40763 * @param {String} text The tab's text and tooltip
40765 setText : function(text){
40767 this.textEl.update(text);
40768 this.setTooltip(text);
40769 //if(!this.tabPanel.resizeTabs){
40770 // this.autoSize();
40774 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40776 activate : function(){
40777 this.tabPanel.activate(this.id);
40781 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40783 disable : function(){
40784 if(this.tabPanel.active != this){
40785 this.disabled = true;
40786 this.status_node.addClass("disabled");
40791 * Enables this TabPanelItem if it was previously disabled.
40793 enable : function(){
40794 this.disabled = false;
40795 this.status_node.removeClass("disabled");
40799 * Sets the content for this TabPanelItem.
40800 * @param {String} content The content
40801 * @param {Boolean} loadScripts true to look for and load scripts
40803 setContent : function(content, loadScripts){
40804 this.bodyEl.update(content, loadScripts);
40808 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40809 * @return {Roo.UpdateManager} The UpdateManager
40811 getUpdateManager : function(){
40812 return this.bodyEl.getUpdateManager();
40816 * Set a URL to be used to load the content for this TabPanelItem.
40817 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40818 * @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)
40819 * @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)
40820 * @return {Roo.UpdateManager} The UpdateManager
40822 setUrl : function(url, params, loadOnce){
40823 if(this.refreshDelegate){
40824 this.un('activate', this.refreshDelegate);
40826 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40827 this.on("activate", this.refreshDelegate);
40828 return this.bodyEl.getUpdateManager();
40832 _handleRefresh : function(url, params, loadOnce){
40833 if(!loadOnce || !this.loaded){
40834 var updater = this.bodyEl.getUpdateManager();
40835 updater.update(url, params, this._setLoaded.createDelegate(this));
40840 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40841 * Will fail silently if the setUrl method has not been called.
40842 * This does not activate the panel, just updates its content.
40844 refresh : function(){
40845 if(this.refreshDelegate){
40846 this.loaded = false;
40847 this.refreshDelegate();
40852 _setLoaded : function(){
40853 this.loaded = true;
40857 closeClick : function(e){
40860 this.fireEvent("beforeclose", this, o);
40861 if(o.cancel !== true){
40862 this.tabPanel.removeTab(this.id);
40866 * The text displayed in the tooltip for the close icon.
40869 closeText : "Close this tab"
40872 * This script refer to:
40873 * Title: International Telephone Input
40874 * Author: Jack O'Connor
40875 * Code version: v12.1.12
40876 * Availability: https://github.com/jackocnr/intl-tel-input.git
40879 Roo.bootstrap.PhoneInputData = function() {
40882 "Afghanistan (افغانستان)",
40887 "Albania (Shqipëri)",
40892 "Algeria (الجزائر)",
40917 "Antigua and Barbuda",
40927 "Armenia (Հայաստան)",
40943 "Austria (Österreich)",
40948 "Azerbaijan (Azərbaycan)",
40958 "Bahrain (البحرين)",
40963 "Bangladesh (বাংলাদেশ)",
40973 "Belarus (Беларусь)",
40978 "Belgium (België)",
41008 "Bosnia and Herzegovina (Босна и Херцеговина)",
41023 "British Indian Ocean Territory",
41028 "British Virgin Islands",
41038 "Bulgaria (България)",
41048 "Burundi (Uburundi)",
41053 "Cambodia (កម្ពុជា)",
41058 "Cameroon (Cameroun)",
41067 ["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"]
41070 "Cape Verde (Kabu Verdi)",
41075 "Caribbean Netherlands",
41086 "Central African Republic (République centrafricaine)",
41106 "Christmas Island",
41112 "Cocos (Keeling) Islands",
41123 "Comoros (جزر القمر)",
41128 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41133 "Congo (Republic) (Congo-Brazzaville)",
41153 "Croatia (Hrvatska)",
41174 "Czech Republic (Česká republika)",
41179 "Denmark (Danmark)",
41194 "Dominican Republic (República Dominicana)",
41198 ["809", "829", "849"]
41216 "Equatorial Guinea (Guinea Ecuatorial)",
41236 "Falkland Islands (Islas Malvinas)",
41241 "Faroe Islands (Føroyar)",
41262 "French Guiana (Guyane française)",
41267 "French Polynesia (Polynésie française)",
41282 "Georgia (საქართველო)",
41287 "Germany (Deutschland)",
41307 "Greenland (Kalaallit Nunaat)",
41344 "Guinea-Bissau (Guiné Bissau)",
41369 "Hungary (Magyarország)",
41374 "Iceland (Ísland)",
41394 "Iraq (العراق)",
41410 "Israel (ישראל)",
41437 "Jordan (الأردن)",
41442 "Kazakhstan (Казахстан)",
41463 "Kuwait (الكويت)",
41468 "Kyrgyzstan (Кыргызстан)",
41478 "Latvia (Latvija)",
41483 "Lebanon (لبنان)",
41498 "Libya (ليبيا)",
41508 "Lithuania (Lietuva)",
41523 "Macedonia (FYROM) (Македонија)",
41528 "Madagascar (Madagasikara)",
41558 "Marshall Islands",
41568 "Mauritania (موريتانيا)",
41573 "Mauritius (Moris)",
41594 "Moldova (Republica Moldova)",
41604 "Mongolia (Монгол)",
41609 "Montenegro (Crna Gora)",
41619 "Morocco (المغرب)",
41625 "Mozambique (Moçambique)",
41630 "Myanmar (Burma) (မြန်မာ)",
41635 "Namibia (Namibië)",
41650 "Netherlands (Nederland)",
41655 "New Caledonia (Nouvelle-Calédonie)",
41690 "North Korea (조선 민주주의 인민 공화국)",
41695 "Northern Mariana Islands",
41711 "Pakistan (پاکستان)",
41721 "Palestine (فلسطين)",
41731 "Papua New Guinea",
41773 "Réunion (La Réunion)",
41779 "Romania (România)",
41795 "Saint Barthélemy",
41806 "Saint Kitts and Nevis",
41816 "Saint Martin (Saint-Martin (partie française))",
41822 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41827 "Saint Vincent and the Grenadines",
41842 "São Tomé and Príncipe (São Tomé e Príncipe)",
41847 "Saudi Arabia (المملكة العربية السعودية)",
41852 "Senegal (Sénégal)",
41882 "Slovakia (Slovensko)",
41887 "Slovenia (Slovenija)",
41897 "Somalia (Soomaaliya)",
41907 "South Korea (대한민국)",
41912 "South Sudan (جنوب السودان)",
41922 "Sri Lanka (ශ්රී ලංකාව)",
41927 "Sudan (السودان)",
41937 "Svalbard and Jan Mayen",
41948 "Sweden (Sverige)",
41953 "Switzerland (Schweiz)",
41958 "Syria (سوريا)",
42003 "Trinidad and Tobago",
42008 "Tunisia (تونس)",
42013 "Turkey (Türkiye)",
42023 "Turks and Caicos Islands",
42033 "U.S. Virgin Islands",
42043 "Ukraine (Україна)",
42048 "United Arab Emirates (الإمارات العربية المتحدة)",
42070 "Uzbekistan (Oʻzbekiston)",
42080 "Vatican City (Città del Vaticano)",
42091 "Vietnam (Việt Nam)",
42096 "Wallis and Futuna (Wallis-et-Futuna)",
42101 "Western Sahara (الصحراء الغربية)",
42107 "Yemen (اليمن)",
42131 * This script refer to:
42132 * Title: International Telephone Input
42133 * Author: Jack O'Connor
42134 * Code version: v12.1.12
42135 * Availability: https://github.com/jackocnr/intl-tel-input.git
42139 * @class Roo.bootstrap.PhoneInput
42140 * @extends Roo.bootstrap.TriggerField
42141 * An input with International dial-code selection
42143 * @cfg {String} defaultDialCode default '+852'
42144 * @cfg {Array} preferedCountries default []
42147 * Create a new PhoneInput.
42148 * @param {Object} config Configuration options
42151 Roo.bootstrap.PhoneInput = function(config) {
42152 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42155 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42157 listWidth: undefined,
42159 selectedClass: 'active',
42161 invalidClass : "has-warning",
42163 validClass: 'has-success',
42165 allowed: '0123456789',
42170 * @cfg {String} defaultDialCode The default dial code when initializing the input
42172 defaultDialCode: '+852',
42175 * @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
42177 preferedCountries: false,
42179 getAutoCreate : function()
42181 var data = Roo.bootstrap.PhoneInputData();
42182 var align = this.labelAlign || this.parentLabelAlign();
42185 this.allCountries = [];
42186 this.dialCodeMapping = [];
42188 for (var i = 0; i < data.length; i++) {
42190 this.allCountries[i] = {
42194 priority: c[3] || 0,
42195 areaCodes: c[4] || null
42197 this.dialCodeMapping[c[2]] = {
42200 priority: c[3] || 0,
42201 areaCodes: c[4] || null
42213 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42214 maxlength: this.max_length,
42215 cls : 'form-control tel-input',
42216 autocomplete: 'new-password'
42219 var hiddenInput = {
42222 cls: 'hidden-tel-input'
42226 hiddenInput.name = this.name;
42229 if (this.disabled) {
42230 input.disabled = true;
42233 var flag_container = {
42250 cls: this.hasFeedback ? 'has-feedback' : '',
42256 cls: 'dial-code-holder',
42263 cls: 'roo-select2-container input-group',
42270 if (this.fieldLabel.length) {
42273 tooltip: 'This field is required'
42279 cls: 'control-label',
42285 html: this.fieldLabel
42288 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42294 if(this.indicatorpos == 'right') {
42295 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42302 if(align == 'left') {
42310 if(this.labelWidth > 12){
42311 label.style = "width: " + this.labelWidth + 'px';
42313 if(this.labelWidth < 13 && this.labelmd == 0){
42314 this.labelmd = this.labelWidth;
42316 if(this.labellg > 0){
42317 label.cls += ' col-lg-' + this.labellg;
42318 input.cls += ' col-lg-' + (12 - this.labellg);
42320 if(this.labelmd > 0){
42321 label.cls += ' col-md-' + this.labelmd;
42322 container.cls += ' col-md-' + (12 - this.labelmd);
42324 if(this.labelsm > 0){
42325 label.cls += ' col-sm-' + this.labelsm;
42326 container.cls += ' col-sm-' + (12 - this.labelsm);
42328 if(this.labelxs > 0){
42329 label.cls += ' col-xs-' + this.labelxs;
42330 container.cls += ' col-xs-' + (12 - this.labelxs);
42340 var settings = this;
42342 ['xs','sm','md','lg'].map(function(size){
42343 if (settings[size]) {
42344 cfg.cls += ' col-' + size + '-' + settings[size];
42348 this.store = new Roo.data.Store({
42349 proxy : new Roo.data.MemoryProxy({}),
42350 reader : new Roo.data.JsonReader({
42361 'name' : 'dialCode',
42365 'name' : 'priority',
42369 'name' : 'areaCodes',
42376 if(!this.preferedCountries) {
42377 this.preferedCountries = [
42384 var p = this.preferedCountries.reverse();
42387 for (var i = 0; i < p.length; i++) {
42388 for (var j = 0; j < this.allCountries.length; j++) {
42389 if(this.allCountries[j].iso2 == p[i]) {
42390 var t = this.allCountries[j];
42391 this.allCountries.splice(j,1);
42392 this.allCountries.unshift(t);
42398 this.store.proxy.data = {
42400 data: this.allCountries
42406 initEvents : function()
42409 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42411 this.indicator = this.indicatorEl();
42412 this.flag = this.flagEl();
42413 this.dialCodeHolder = this.dialCodeHolderEl();
42415 this.trigger = this.el.select('div.flag-box',true).first();
42416 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42421 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42422 _this.list.setWidth(lw);
42425 this.list.on('mouseover', this.onViewOver, this);
42426 this.list.on('mousemove', this.onViewMove, this);
42427 this.inputEl().on("keyup", this.onKeyUp, this);
42428 this.inputEl().on("keypress", this.onKeyPress, this);
42430 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42432 this.view = new Roo.View(this.list, this.tpl, {
42433 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42436 this.view.on('click', this.onViewClick, this);
42437 this.setValue(this.defaultDialCode);
42440 onTriggerClick : function(e)
42442 Roo.log('trigger click');
42447 if(this.isExpanded()){
42449 this.hasFocus = false;
42451 this.store.load({});
42452 this.hasFocus = true;
42457 isExpanded : function()
42459 return this.list.isVisible();
42462 collapse : function()
42464 if(!this.isExpanded()){
42468 Roo.get(document).un('mousedown', this.collapseIf, this);
42469 Roo.get(document).un('mousewheel', this.collapseIf, this);
42470 this.fireEvent('collapse', this);
42474 expand : function()
42478 if(this.isExpanded() || !this.hasFocus){
42482 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42483 this.list.setWidth(lw);
42486 this.restrictHeight();
42488 Roo.get(document).on('mousedown', this.collapseIf, this);
42489 Roo.get(document).on('mousewheel', this.collapseIf, this);
42491 this.fireEvent('expand', this);
42494 restrictHeight : function()
42496 this.list.alignTo(this.inputEl(), this.listAlign);
42497 this.list.alignTo(this.inputEl(), this.listAlign);
42500 onViewOver : function(e, t)
42502 if(this.inKeyMode){
42505 var item = this.view.findItemFromChild(t);
42508 var index = this.view.indexOf(item);
42509 this.select(index, false);
42514 onViewClick : function(view, doFocus, el, e)
42516 var index = this.view.getSelectedIndexes()[0];
42518 var r = this.store.getAt(index);
42521 this.onSelect(r, index);
42523 if(doFocus !== false && !this.blockFocus){
42524 this.inputEl().focus();
42528 onViewMove : function(e, t)
42530 this.inKeyMode = false;
42533 select : function(index, scrollIntoView)
42535 this.selectedIndex = index;
42536 this.view.select(index);
42537 if(scrollIntoView !== false){
42538 var el = this.view.getNode(index);
42540 this.list.scrollChildIntoView(el, false);
42545 createList : function()
42547 this.list = Roo.get(document.body).createChild({
42549 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42550 style: 'display:none'
42553 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42556 collapseIf : function(e)
42558 var in_combo = e.within(this.el);
42559 var in_list = e.within(this.list);
42560 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42562 if (in_combo || in_list || is_list) {
42568 onSelect : function(record, index)
42570 if(this.fireEvent('beforeselect', this, record, index) !== false){
42572 this.setFlagClass(record.data.iso2);
42573 this.setDialCode(record.data.dialCode);
42574 this.hasFocus = false;
42576 this.fireEvent('select', this, record, index);
42580 flagEl : function()
42582 var flag = this.el.select('div.flag',true).first();
42589 dialCodeHolderEl : function()
42591 var d = this.el.select('input.dial-code-holder',true).first();
42598 setDialCode : function(v)
42600 this.dialCodeHolder.dom.value = '+'+v;
42603 setFlagClass : function(n)
42605 this.flag.dom.className = 'flag '+n;
42608 getValue : function()
42610 var v = this.inputEl().getValue();
42611 if(this.dialCodeHolder) {
42612 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42617 setValue : function(v)
42619 var d = this.getDialCode(v);
42621 //invalid dial code
42622 if(v.length == 0 || !d || d.length == 0) {
42624 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42625 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42631 this.setFlagClass(this.dialCodeMapping[d].iso2);
42632 this.setDialCode(d);
42633 this.inputEl().dom.value = v.replace('+'+d,'');
42634 this.hiddenEl().dom.value = this.getValue();
42639 getDialCode : function(v)
42643 if (v.length == 0) {
42644 return this.dialCodeHolder.dom.value;
42648 if (v.charAt(0) != "+") {
42651 var numericChars = "";
42652 for (var i = 1; i < v.length; i++) {
42653 var c = v.charAt(i);
42656 if (this.dialCodeMapping[numericChars]) {
42657 dialCode = v.substr(1, i);
42659 if (numericChars.length == 4) {
42669 this.setValue(this.defaultDialCode);
42673 hiddenEl : function()
42675 return this.el.select('input.hidden-tel-input',true).first();
42678 // after setting val
42679 onKeyUp : function(e){
42680 this.setValue(this.getValue());
42683 onKeyPress : function(e){
42684 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42691 * @class Roo.bootstrap.MoneyField
42692 * @extends Roo.bootstrap.ComboBox
42693 * Bootstrap MoneyField class
42696 * Create a new MoneyField.
42697 * @param {Object} config Configuration options
42700 Roo.bootstrap.MoneyField = function(config) {
42702 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42706 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42709 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42711 allowDecimals : true,
42713 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42715 decimalSeparator : ".",
42717 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42719 decimalPrecision : 0,
42721 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42723 allowNegative : true,
42725 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42729 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42731 minValue : Number.NEGATIVE_INFINITY,
42733 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42735 maxValue : Number.MAX_VALUE,
42737 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42739 minText : "The minimum value for this field is {0}",
42741 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42743 maxText : "The maximum value for this field is {0}",
42745 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42746 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42748 nanText : "{0} is not a valid number",
42750 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42754 * @cfg {String} defaults currency of the MoneyField
42755 * value should be in lkey
42757 defaultCurrency : false,
42759 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42761 thousandsDelimiter : false,
42763 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42774 getAutoCreate : function()
42776 var align = this.labelAlign || this.parentLabelAlign();
42788 cls : 'form-control roo-money-amount-input',
42789 autocomplete: 'new-password'
42792 var hiddenInput = {
42796 cls: 'hidden-number-input'
42799 if(this.max_length) {
42800 input.maxlength = this.max_length;
42804 hiddenInput.name = this.name;
42807 if (this.disabled) {
42808 input.disabled = true;
42811 var clg = 12 - this.inputlg;
42812 var cmd = 12 - this.inputmd;
42813 var csm = 12 - this.inputsm;
42814 var cxs = 12 - this.inputxs;
42818 cls : 'row roo-money-field',
42822 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42826 cls: 'roo-select2-container input-group',
42830 cls : 'form-control roo-money-currency-input',
42831 autocomplete: 'new-password',
42833 name : this.currencyName
42837 cls : 'input-group-addon',
42851 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42855 cls: this.hasFeedback ? 'has-feedback' : '',
42866 if (this.fieldLabel.length) {
42869 tooltip: 'This field is required'
42875 cls: 'control-label',
42881 html: this.fieldLabel
42884 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42890 if(this.indicatorpos == 'right') {
42891 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42898 if(align == 'left') {
42906 if(this.labelWidth > 12){
42907 label.style = "width: " + this.labelWidth + 'px';
42909 if(this.labelWidth < 13 && this.labelmd == 0){
42910 this.labelmd = this.labelWidth;
42912 if(this.labellg > 0){
42913 label.cls += ' col-lg-' + this.labellg;
42914 input.cls += ' col-lg-' + (12 - this.labellg);
42916 if(this.labelmd > 0){
42917 label.cls += ' col-md-' + this.labelmd;
42918 container.cls += ' col-md-' + (12 - this.labelmd);
42920 if(this.labelsm > 0){
42921 label.cls += ' col-sm-' + this.labelsm;
42922 container.cls += ' col-sm-' + (12 - this.labelsm);
42924 if(this.labelxs > 0){
42925 label.cls += ' col-xs-' + this.labelxs;
42926 container.cls += ' col-xs-' + (12 - this.labelxs);
42937 var settings = this;
42939 ['xs','sm','md','lg'].map(function(size){
42940 if (settings[size]) {
42941 cfg.cls += ' col-' + size + '-' + settings[size];
42948 initEvents : function()
42950 this.indicator = this.indicatorEl();
42952 this.initCurrencyEvent();
42954 this.initNumberEvent();
42957 initCurrencyEvent : function()
42960 throw "can not find store for combo";
42963 this.store = Roo.factory(this.store, Roo.data);
42964 this.store.parent = this;
42968 this.triggerEl = this.el.select('.input-group-addon', true).first();
42970 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42975 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42976 _this.list.setWidth(lw);
42979 this.list.on('mouseover', this.onViewOver, this);
42980 this.list.on('mousemove', this.onViewMove, this);
42981 this.list.on('scroll', this.onViewScroll, this);
42984 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42987 this.view = new Roo.View(this.list, this.tpl, {
42988 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42991 this.view.on('click', this.onViewClick, this);
42993 this.store.on('beforeload', this.onBeforeLoad, this);
42994 this.store.on('load', this.onLoad, this);
42995 this.store.on('loadexception', this.onLoadException, this);
42997 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42998 "up" : function(e){
42999 this.inKeyMode = true;
43003 "down" : function(e){
43004 if(!this.isExpanded()){
43005 this.onTriggerClick();
43007 this.inKeyMode = true;
43012 "enter" : function(e){
43015 if(this.fireEvent("specialkey", this, e)){
43016 this.onViewClick(false);
43022 "esc" : function(e){
43026 "tab" : function(e){
43029 if(this.fireEvent("specialkey", this, e)){
43030 this.onViewClick(false);
43038 doRelay : function(foo, bar, hname){
43039 if(hname == 'down' || this.scope.isExpanded()){
43040 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43048 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43052 initNumberEvent : function(e)
43054 this.inputEl().on("keydown" , this.fireKey, this);
43055 this.inputEl().on("focus", this.onFocus, this);
43056 this.inputEl().on("blur", this.onBlur, this);
43058 this.inputEl().relayEvent('keyup', this);
43060 if(this.indicator){
43061 this.indicator.addClass('invisible');
43064 this.originalValue = this.getValue();
43066 if(this.validationEvent == 'keyup'){
43067 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43068 this.inputEl().on('keyup', this.filterValidation, this);
43070 else if(this.validationEvent !== false){
43071 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43074 if(this.selectOnFocus){
43075 this.on("focus", this.preFocus, this);
43078 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43079 this.inputEl().on("keypress", this.filterKeys, this);
43081 this.inputEl().relayEvent('keypress', this);
43084 var allowed = "0123456789";
43086 if(this.allowDecimals){
43087 allowed += this.decimalSeparator;
43090 if(this.allowNegative){
43094 if(this.thousandsDelimiter) {
43098 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43100 var keyPress = function(e){
43102 var k = e.getKey();
43104 var c = e.getCharCode();
43107 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43108 allowed.indexOf(String.fromCharCode(c)) === -1
43114 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43118 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43123 this.inputEl().on("keypress", keyPress, this);
43127 onTriggerClick : function(e)
43134 this.loadNext = false;
43136 if(this.isExpanded()){
43141 this.hasFocus = true;
43143 if(this.triggerAction == 'all') {
43144 this.doQuery(this.allQuery, true);
43148 this.doQuery(this.getRawValue());
43151 getCurrency : function()
43153 var v = this.currencyEl().getValue();
43158 restrictHeight : function()
43160 this.list.alignTo(this.currencyEl(), this.listAlign);
43161 this.list.alignTo(this.currencyEl(), this.listAlign);
43164 onViewClick : function(view, doFocus, el, e)
43166 var index = this.view.getSelectedIndexes()[0];
43168 var r = this.store.getAt(index);
43171 this.onSelect(r, index);
43175 onSelect : function(record, index){
43177 if(this.fireEvent('beforeselect', this, record, index) !== false){
43179 this.setFromCurrencyData(index > -1 ? record.data : false);
43183 this.fireEvent('select', this, record, index);
43187 setFromCurrencyData : function(o)
43191 this.lastCurrency = o;
43193 if (this.currencyField) {
43194 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43196 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43199 this.lastSelectionText = currency;
43201 //setting default currency
43202 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43203 this.setCurrency(this.defaultCurrency);
43207 this.setCurrency(currency);
43210 setFromData : function(o)
43214 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43216 this.setFromCurrencyData(c);
43221 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43223 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43226 this.setValue(value);
43230 setCurrency : function(v)
43232 this.currencyValue = v;
43235 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43240 setValue : function(v)
43242 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43248 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43250 this.inputEl().dom.value = (v == '') ? '' :
43251 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43253 if(!this.allowZero && v === '0') {
43254 this.hiddenEl().dom.value = '';
43255 this.inputEl().dom.value = '';
43262 getRawValue : function()
43264 var v = this.inputEl().getValue();
43269 getValue : function()
43271 return this.fixPrecision(this.parseValue(this.getRawValue()));
43274 parseValue : function(value)
43276 if(this.thousandsDelimiter) {
43278 r = new RegExp(",", "g");
43279 value = value.replace(r, "");
43282 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43283 return isNaN(value) ? '' : value;
43287 fixPrecision : function(value)
43289 if(this.thousandsDelimiter) {
43291 r = new RegExp(",", "g");
43292 value = value.replace(r, "");
43295 var nan = isNaN(value);
43297 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43298 return nan ? '' : value;
43300 return parseFloat(value).toFixed(this.decimalPrecision);
43303 decimalPrecisionFcn : function(v)
43305 return Math.floor(v);
43308 validateValue : function(value)
43310 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43314 var num = this.parseValue(value);
43317 this.markInvalid(String.format(this.nanText, value));
43321 if(num < this.minValue){
43322 this.markInvalid(String.format(this.minText, this.minValue));
43326 if(num > this.maxValue){
43327 this.markInvalid(String.format(this.maxText, this.maxValue));
43334 validate : function()
43336 if(this.disabled || this.allowBlank){
43341 var currency = this.getCurrency();
43343 if(this.validateValue(this.getRawValue()) && currency.length){
43348 this.markInvalid();
43352 getName: function()
43357 beforeBlur : function()
43363 var v = this.parseValue(this.getRawValue());
43370 onBlur : function()
43374 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43375 //this.el.removeClass(this.focusClass);
43378 this.hasFocus = false;
43380 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43384 var v = this.getValue();
43386 if(String(v) !== String(this.startValue)){
43387 this.fireEvent('change', this, v, this.startValue);
43390 this.fireEvent("blur", this);
43393 inputEl : function()
43395 return this.el.select('.roo-money-amount-input', true).first();
43398 currencyEl : function()
43400 return this.el.select('.roo-money-currency-input', true).first();
43403 hiddenEl : function()
43405 return this.el.select('input.hidden-number-input',true).first();
43409 * @class Roo.bootstrap.BezierSignature
43410 * @extends Roo.bootstrap.Component
43411 * Bootstrap BezierSignature class
43412 * This script refer to:
43413 * Title: Signature Pad
43415 * Availability: https://github.com/szimek/signature_pad
43418 * Create a new BezierSignature
43419 * @param {Object} config The config object
43422 Roo.bootstrap.BezierSignature = function(config){
43423 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43429 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43436 mouse_btn_down: true,
43439 * @cfg {int} canvas height
43441 canvas_height: '200px',
43444 * @cfg {float|function} Radius of a single dot.
43449 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43454 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43459 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43464 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43469 * @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.
43471 bg_color: 'rgba(0, 0, 0, 0)',
43474 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43476 dot_color: 'black',
43479 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43481 velocity_filter_weight: 0.7,
43484 * @cfg {function} Callback when stroke begin.
43489 * @cfg {function} Callback when stroke end.
43493 getAutoCreate : function()
43495 var cls = 'roo-signature column';
43498 cls += ' ' + this.cls;
43508 for(var i = 0; i < col_sizes.length; i++) {
43509 if(this[col_sizes[i]]) {
43510 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43520 cls: 'roo-signature-body',
43524 cls: 'roo-signature-body-canvas',
43525 height: this.canvas_height,
43526 width: this.canvas_width
43533 style: 'display: none'
43541 initEvents: function()
43543 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43545 var canvas = this.canvasEl();
43547 // mouse && touch event swapping...
43548 canvas.dom.style.touchAction = 'none';
43549 canvas.dom.style.msTouchAction = 'none';
43551 this.mouse_btn_down = false;
43552 canvas.on('mousedown', this._handleMouseDown, this);
43553 canvas.on('mousemove', this._handleMouseMove, this);
43554 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43556 if (window.PointerEvent) {
43557 canvas.on('pointerdown', this._handleMouseDown, this);
43558 canvas.on('pointermove', this._handleMouseMove, this);
43559 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43562 if ('ontouchstart' in window) {
43563 canvas.on('touchstart', this._handleTouchStart, this);
43564 canvas.on('touchmove', this._handleTouchMove, this);
43565 canvas.on('touchend', this._handleTouchEnd, this);
43568 Roo.EventManager.onWindowResize(this.resize, this, true);
43570 // file input event
43571 this.fileEl().on('change', this.uploadImage, this);
43578 resize: function(){
43580 var canvas = this.canvasEl().dom;
43581 var ctx = this.canvasElCtx();
43582 var img_data = false;
43584 if(canvas.width > 0) {
43585 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43587 // setting canvas width will clean img data
43590 var style = window.getComputedStyle ?
43591 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43593 var padding_left = parseInt(style.paddingLeft) || 0;
43594 var padding_right = parseInt(style.paddingRight) || 0;
43596 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43599 ctx.putImageData(img_data, 0, 0);
43603 _handleMouseDown: function(e)
43605 if (e.browserEvent.which === 1) {
43606 this.mouse_btn_down = true;
43607 this.strokeBegin(e);
43611 _handleMouseMove: function (e)
43613 if (this.mouse_btn_down) {
43614 this.strokeMoveUpdate(e);
43618 _handleMouseUp: function (e)
43620 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43621 this.mouse_btn_down = false;
43626 _handleTouchStart: function (e) {
43628 e.preventDefault();
43629 if (e.browserEvent.targetTouches.length === 1) {
43630 // var touch = e.browserEvent.changedTouches[0];
43631 // this.strokeBegin(touch);
43633 this.strokeBegin(e); // assume e catching the correct xy...
43637 _handleTouchMove: function (e) {
43638 e.preventDefault();
43639 // var touch = event.targetTouches[0];
43640 // _this._strokeMoveUpdate(touch);
43641 this.strokeMoveUpdate(e);
43644 _handleTouchEnd: function (e) {
43645 var wasCanvasTouched = e.target === this.canvasEl().dom;
43646 if (wasCanvasTouched) {
43647 e.preventDefault();
43648 // var touch = event.changedTouches[0];
43649 // _this._strokeEnd(touch);
43654 reset: function () {
43655 this._lastPoints = [];
43656 this._lastVelocity = 0;
43657 this._lastWidth = (this.min_width + this.max_width) / 2;
43658 this.canvasElCtx().fillStyle = this.dot_color;
43661 strokeMoveUpdate: function(e)
43663 this.strokeUpdate(e);
43665 if (this.throttle) {
43666 this.throttleStroke(this.strokeUpdate, this.throttle);
43669 this.strokeUpdate(e);
43673 strokeBegin: function(e)
43675 var newPointGroup = {
43676 color: this.dot_color,
43680 if (typeof this.onBegin === 'function') {
43684 this.curve_data.push(newPointGroup);
43686 this.strokeUpdate(e);
43689 strokeUpdate: function(e)
43691 var rect = this.canvasEl().dom.getBoundingClientRect();
43692 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43693 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43694 var lastPoints = lastPointGroup.points;
43695 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43696 var isLastPointTooClose = lastPoint
43697 ? point.distanceTo(lastPoint) <= this.min_distance
43699 var color = lastPointGroup.color;
43700 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43701 var curve = this.addPoint(point);
43703 this.drawDot({color: color, point: point});
43706 this.drawCurve({color: color, curve: curve});
43716 strokeEnd: function(e)
43718 this.strokeUpdate(e);
43719 if (typeof this.onEnd === 'function') {
43724 addPoint: function (point) {
43725 var _lastPoints = this._lastPoints;
43726 _lastPoints.push(point);
43727 if (_lastPoints.length > 2) {
43728 if (_lastPoints.length === 3) {
43729 _lastPoints.unshift(_lastPoints[0]);
43731 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43732 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43733 _lastPoints.shift();
43739 calculateCurveWidths: function (startPoint, endPoint) {
43740 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43741 (1 - this.velocity_filter_weight) * this._lastVelocity;
43743 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43746 start: this._lastWidth
43749 this._lastVelocity = velocity;
43750 this._lastWidth = newWidth;
43754 drawDot: function (_a) {
43755 var color = _a.color, point = _a.point;
43756 var ctx = this.canvasElCtx();
43757 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43759 this.drawCurveSegment(point.x, point.y, width);
43761 ctx.fillStyle = color;
43765 drawCurve: function (_a) {
43766 var color = _a.color, curve = _a.curve;
43767 var ctx = this.canvasElCtx();
43768 var widthDelta = curve.endWidth - curve.startWidth;
43769 var drawSteps = Math.floor(curve.length()) * 2;
43771 ctx.fillStyle = color;
43772 for (var i = 0; i < drawSteps; i += 1) {
43773 var t = i / drawSteps;
43779 var x = uuu * curve.startPoint.x;
43780 x += 3 * uu * t * curve.control1.x;
43781 x += 3 * u * tt * curve.control2.x;
43782 x += ttt * curve.endPoint.x;
43783 var y = uuu * curve.startPoint.y;
43784 y += 3 * uu * t * curve.control1.y;
43785 y += 3 * u * tt * curve.control2.y;
43786 y += ttt * curve.endPoint.y;
43787 var width = curve.startWidth + ttt * widthDelta;
43788 this.drawCurveSegment(x, y, width);
43794 drawCurveSegment: function (x, y, width) {
43795 var ctx = this.canvasElCtx();
43797 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43798 this.is_empty = false;
43803 var ctx = this.canvasElCtx();
43804 var canvas = this.canvasEl().dom;
43805 ctx.fillStyle = this.bg_color;
43806 ctx.clearRect(0, 0, canvas.width, canvas.height);
43807 ctx.fillRect(0, 0, canvas.width, canvas.height);
43808 this.curve_data = [];
43810 this.is_empty = true;
43815 return this.el.select('input',true).first();
43818 canvasEl: function()
43820 return this.el.select('canvas',true).first();
43823 canvasElCtx: function()
43825 return this.el.select('canvas',true).first().dom.getContext('2d');
43828 getImage: function(type)
43830 if(this.is_empty) {
43835 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43838 drawFromImage: function(img_src)
43840 var img = new Image();
43842 img.onload = function(){
43843 this.canvasElCtx().drawImage(img, 0, 0);
43848 this.is_empty = false;
43851 selectImage: function()
43853 this.fileEl().dom.click();
43856 uploadImage: function(e)
43858 var reader = new FileReader();
43860 reader.onload = function(e){
43861 var img = new Image();
43862 img.onload = function(){
43864 this.canvasElCtx().drawImage(img, 0, 0);
43866 img.src = e.target.result;
43869 reader.readAsDataURL(e.target.files[0]);
43872 // Bezier Point Constructor
43873 Point: (function () {
43874 function Point(x, y, time) {
43877 this.time = time || Date.now();
43879 Point.prototype.distanceTo = function (start) {
43880 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43882 Point.prototype.equals = function (other) {
43883 return this.x === other.x && this.y === other.y && this.time === other.time;
43885 Point.prototype.velocityFrom = function (start) {
43886 return this.time !== start.time
43887 ? this.distanceTo(start) / (this.time - start.time)
43894 // Bezier Constructor
43895 Bezier: (function () {
43896 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43897 this.startPoint = startPoint;
43898 this.control2 = control2;
43899 this.control1 = control1;
43900 this.endPoint = endPoint;
43901 this.startWidth = startWidth;
43902 this.endWidth = endWidth;
43904 Bezier.fromPoints = function (points, widths, scope) {
43905 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43906 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43907 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43909 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43910 var dx1 = s1.x - s2.x;
43911 var dy1 = s1.y - s2.y;
43912 var dx2 = s2.x - s3.x;
43913 var dy2 = s2.y - s3.y;
43914 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43915 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43916 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43917 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43918 var dxm = m1.x - m2.x;
43919 var dym = m1.y - m2.y;
43920 var k = l2 / (l1 + l2);
43921 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43922 var tx = s2.x - cm.x;
43923 var ty = s2.y - cm.y;
43925 c1: new scope.Point(m1.x + tx, m1.y + ty),
43926 c2: new scope.Point(m2.x + tx, m2.y + ty)
43929 Bezier.prototype.length = function () {
43934 for (var i = 0; i <= steps; i += 1) {
43936 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43937 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43939 var xdiff = cx - px;
43940 var ydiff = cy - py;
43941 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43948 Bezier.prototype.point = function (t, start, c1, c2, end) {
43949 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43950 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43951 + (3.0 * c2 * (1.0 - t) * t * t)
43952 + (end * t * t * t);
43957 throttleStroke: function(fn, wait) {
43958 if (wait === void 0) { wait = 250; }
43960 var timeout = null;
43964 var later = function () {
43965 previous = Date.now();
43967 result = fn.apply(storedContext, storedArgs);
43969 storedContext = null;
43973 return function wrapper() {
43975 for (var _i = 0; _i < arguments.length; _i++) {
43976 args[_i] = arguments[_i];
43978 var now = Date.now();
43979 var remaining = wait - (now - previous);
43980 storedContext = this;
43982 if (remaining <= 0 || remaining > wait) {
43984 clearTimeout(timeout);
43988 result = fn.apply(storedContext, storedArgs);
43990 storedContext = null;
43994 else if (!timeout) {
43995 timeout = window.setTimeout(later, remaining);