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
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <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>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2523 var dom = move_card.el.dom;
2524 dom.parentNode.removeChild(dom);
2525 dom.style.width = ''; // clear with - which is set by drag.
2527 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528 var cardel = next_to_card.el.dom;
2530 if (position == 'above' ) {
2531 cardel.parentNode.insertBefore(dom, cardel);
2532 } else if (cardel.nextSibling) {
2533 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535 cardel.parentNode.append(dom);
2538 // card container???
2539 this.containerEl.dom.append(dom);
2542 //FIXME HANDLE card = true
2544 // add this to the correct place in items.
2548 // remove Card from items.
2550 var old_parent = move_card.parent();
2552 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2554 if (this.items.length) {
2556 //Roo.log([info.items_n, info.position, this.items.length]);
2557 for (var i =0; i < this.items.length; i++) {
2558 if (i == to_items_n && position == 'above') {
2559 nitems.push(move_card);
2561 nitems.push(this.items[i]);
2562 if (i == to_items_n && position == 'below') {
2563 nitems.push(move_card);
2566 this.items = nitems;
2567 Roo.log(this.items);
2569 this.items.push(move_card);
2572 move_card.parentId = this.id;
2580 /** Decide whether to drop above or below a View node. */
2581 getDropPoint : function(e, n, dd)
2586 if (n == this.containerEl.dom) {
2589 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590 var c = t + (b - t) / 2;
2591 var y = Roo.lib.Event.getPageY(e);
2598 onToggleCollapse : function(e)
2600 if (this.collapsed) {
2601 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602 this.collapsableEl.addClass('show');
2603 this.collapsed = false;
2606 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607 this.collapsableEl.removeClass('show');
2608 this.collapsed = true;
2613 onToggleRotate : function(e)
2615 this.collapsableEl.removeClass('show');
2616 this.footerEl.removeClass('d-none');
2617 this.el.removeClass('roo-card-rotated');
2618 this.el.removeClass('d-none');
2621 this.collapsableEl.addClass('show');
2622 this.rotated = false;
2623 this.fireEvent('rotate', this, this.rotated);
2626 this.el.addClass('roo-card-rotated');
2627 this.footerEl.addClass('d-none');
2628 this.el.select('.roo-collapsable').removeClass('show');
2630 this.rotated = true;
2631 this.fireEvent('rotate', this, this.rotated);
2635 dropPlaceHolder: function (action, info, data)
2637 if (this.dropEl === false) {
2638 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2642 this.dropEl.removeClass(['d-none', 'd-block']);
2643 if (action == 'hide') {
2645 this.dropEl.addClass('d-none');
2648 // FIXME - info.card == true!!!
2649 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2651 if (info.card !== true) {
2652 var cardel = info.card.el.dom;
2654 if (info.position == 'above') {
2655 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656 } else if (cardel.nextSibling) {
2657 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2659 cardel.parentNode.append(this.dropEl.dom);
2662 // card container???
2663 this.containerEl.dom.append(this.dropEl.dom);
2666 this.dropEl.addClass('d-block roo-card-dropzone');
2668 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2675 setHeaderText: function(html)
2677 this.headerContainerEl.dom.innerHTML = html;
2686 * Card header - holder for the card header elements.
2691 * @class Roo.bootstrap.CardHeader
2692 * @extends Roo.bootstrap.Element
2693 * Bootstrap CardHeader class
2695 * Create a new Card Header - that you can embed children into
2696 * @param {Object} config The config object
2699 Roo.bootstrap.CardHeader = function(config){
2700 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2706 container_method : 'getCardHeader'
2719 * Card footer - holder for the card footer elements.
2724 * @class Roo.bootstrap.CardFooter
2725 * @extends Roo.bootstrap.Element
2726 * Bootstrap CardFooter class
2728 * Create a new Card Footer - that you can embed children into
2729 * @param {Object} config The config object
2732 Roo.bootstrap.CardFooter = function(config){
2733 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2739 container_method : 'getCardFooter'
2752 * Card header - holder for the card header elements.
2757 * @class Roo.bootstrap.CardImageTop
2758 * @extends Roo.bootstrap.Element
2759 * Bootstrap CardImageTop class
2761 * Create a new Card Image Top container
2762 * @param {Object} config The config object
2765 Roo.bootstrap.CardImageTop = function(config){
2766 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2772 container_method : 'getCardImageTop'
2790 * @class Roo.bootstrap.Img
2791 * @extends Roo.bootstrap.Component
2792 * Bootstrap Img class
2793 * @cfg {Boolean} imgResponsive false | true
2794 * @cfg {String} border rounded | circle | thumbnail
2795 * @cfg {String} src image source
2796 * @cfg {String} alt image alternative text
2797 * @cfg {String} href a tag href
2798 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799 * @cfg {String} xsUrl xs image source
2800 * @cfg {String} smUrl sm image source
2801 * @cfg {String} mdUrl md image source
2802 * @cfg {String} lgUrl lg image source
2805 * Create a new Input
2806 * @param {Object} config The config object
2809 Roo.bootstrap.Img = function(config){
2810 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816 * The img click event for the img.
2817 * @param {Roo.EventObject} e
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2825 imgResponsive: true,
2835 getAutoCreate : function()
2837 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838 return this.createSingleImg();
2843 cls: 'roo-image-responsive-group',
2848 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2850 if(!_this[size + 'Url']){
2856 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857 html: _this.html || cfg.html,
2858 src: _this[size + 'Url']
2861 img.cls += ' roo-image-responsive-' + size;
2863 var s = ['xs', 'sm', 'md', 'lg'];
2865 s.splice(s.indexOf(size), 1);
2867 Roo.each(s, function(ss){
2868 img.cls += ' hidden-' + ss;
2871 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872 cfg.cls += ' img-' + _this.border;
2876 cfg.alt = _this.alt;
2889 a.target = _this.target;
2893 cfg.cn.push((_this.href) ? a : img);
2900 createSingleImg : function()
2904 cls: (this.imgResponsive) ? 'img-responsive' : '',
2906 src : 'about:blank' // just incase src get's set to undefined?!?
2909 cfg.html = this.html || cfg.html;
2911 cfg.src = this.src || cfg.src;
2913 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914 cfg.cls += ' img-' + this.border;
2931 a.target = this.target;
2936 return (this.href) ? a : cfg;
2939 initEvents: function()
2942 this.el.on('click', this.onClick, this);
2947 onClick : function(e)
2949 Roo.log('img onclick');
2950 this.fireEvent('click', this, e);
2953 * Sets the url of the image - used to update it
2954 * @param {String} url the url of the image
2957 setSrc : function(url)
2961 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962 this.el.dom.src = url;
2966 this.el.select('img', true).first().dom.src = url;
2982 * @class Roo.bootstrap.Link
2983 * @extends Roo.bootstrap.Component
2984 * Bootstrap Link Class
2985 * @cfg {String} alt image alternative text
2986 * @cfg {String} href a tag href
2987 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988 * @cfg {String} html the content of the link.
2989 * @cfg {String} anchor name for the anchor link
2990 * @cfg {String} fa - favicon
2992 * @cfg {Boolean} preventDefault (true | false) default false
2996 * Create a new Input
2997 * @param {Object} config The config object
3000 Roo.bootstrap.Link = function(config){
3001 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007 * The img click event for the img.
3008 * @param {Roo.EventObject} e
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3018 preventDefault: false,
3024 getAutoCreate : function()
3026 var html = this.html || '';
3028 if (this.fa !== false) {
3029 html = '<i class="fa fa-' + this.fa + '"></i>';
3034 // anchor's do not require html/href...
3035 if (this.anchor === false) {
3037 cfg.href = this.href || '#';
3039 cfg.name = this.anchor;
3040 if (this.html !== false || this.fa !== false) {
3043 if (this.href !== false) {
3044 cfg.href = this.href;
3048 if(this.alt !== false){
3053 if(this.target !== false) {
3054 cfg.target = this.target;
3060 initEvents: function() {
3062 if(!this.href || this.preventDefault){
3063 this.el.on('click', this.onClick, this);
3067 onClick : function(e)
3069 if(this.preventDefault){
3072 //Roo.log('img onclick');
3073 this.fireEvent('click', this, e);
3086 * @class Roo.bootstrap.Header
3087 * @extends Roo.bootstrap.Component
3088 * Bootstrap Header class
3089 * @cfg {String} html content of header
3090 * @cfg {Number} level (1|2|3|4|5|6) default 1
3093 * Create a new Header
3094 * @param {Object} config The config object
3098 Roo.bootstrap.Header = function(config){
3099 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3110 getAutoCreate : function(){
3115 tag: 'h' + (1 *this.level),
3116 html: this.html || ''
3128 * Ext JS Library 1.1.1
3129 * Copyright(c) 2006-2007, Ext JS, LLC.
3131 * Originally Released Under LGPL - original licence link has changed is not relivant.
3134 * <script type="text/javascript">
3138 * @class Roo.bootstrap.MenuMgr
3139 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3142 Roo.bootstrap.MenuMgr = function(){
3143 var menus, active, groups = {}, attached = false, lastShow = new Date();
3145 // private - called when first menu is created
3148 active = new Roo.util.MixedCollection();
3149 Roo.get(document).addKeyListener(27, function(){
3150 if(active.length > 0){
3158 if(active && active.length > 0){
3159 var c = active.clone();
3169 if(active.length < 1){
3170 Roo.get(document).un("mouseup", onMouseDown);
3178 var last = active.last();
3179 lastShow = new Date();
3182 Roo.get(document).on("mouseup", onMouseDown);
3187 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188 m.parentMenu.activeChild = m;
3189 }else if(last && last.isVisible()){
3190 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3195 function onBeforeHide(m){
3197 m.activeChild.hide();
3199 if(m.autoHideTimer){
3200 clearTimeout(m.autoHideTimer);
3201 delete m.autoHideTimer;
3206 function onBeforeShow(m){
3207 var pm = m.parentMenu;
3208 if(!pm && !m.allowOtherMenus){
3210 }else if(pm && pm.activeChild && active != m){
3211 pm.activeChild.hide();
3215 // private this should really trigger on mouseup..
3216 function onMouseDown(e){
3217 Roo.log("on Mouse Up");
3219 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220 Roo.log("MenuManager hideAll");
3229 function onBeforeCheck(mi, state){
3231 var g = groups[mi.group];
3232 for(var i = 0, l = g.length; i < l; i++){
3234 g[i].setChecked(false);
3243 * Hides all menus that are currently visible
3245 hideAll : function(){
3250 register : function(menu){
3254 menus[menu.id] = menu;
3255 menu.on("beforehide", onBeforeHide);
3256 menu.on("hide", onHide);
3257 menu.on("beforeshow", onBeforeShow);
3258 menu.on("show", onShow);
3260 if(g && menu.events["checkchange"]){
3264 groups[g].push(menu);
3265 menu.on("checkchange", onCheck);
3270 * Returns a {@link Roo.menu.Menu} object
3271 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272 * be used to generate and return a new Menu instance.
3274 get : function(menu){
3275 if(typeof menu == "string"){ // menu id
3277 }else if(menu.events){ // menu instance
3280 /*else if(typeof menu.length == 'number'){ // array of menu items?
3281 return new Roo.bootstrap.Menu({items:menu});
3282 }else{ // otherwise, must be a config
3283 return new Roo.bootstrap.Menu(menu);
3290 unregister : function(menu){
3291 delete menus[menu.id];
3292 menu.un("beforehide", onBeforeHide);
3293 menu.un("hide", onHide);
3294 menu.un("beforeshow", onBeforeShow);
3295 menu.un("show", onShow);
3297 if(g && menu.events["checkchange"]){
3298 groups[g].remove(menu);
3299 menu.un("checkchange", onCheck);
3304 registerCheckable : function(menuItem){
3305 var g = menuItem.group;
3310 groups[g].push(menuItem);
3311 menuItem.on("beforecheckchange", onBeforeCheck);
3316 unregisterCheckable : function(menuItem){
3317 var g = menuItem.group;
3319 groups[g].remove(menuItem);
3320 menuItem.un("beforecheckchange", onBeforeCheck);
3332 * @class Roo.bootstrap.Menu
3333 * @extends Roo.bootstrap.Component
3334 * Bootstrap Menu class - container for MenuItems
3335 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336 * @cfg {bool} hidden if the menu should be hidden when rendered.
3337 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3338 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3342 * @param {Object} config The config object
3346 Roo.bootstrap.Menu = function(config){
3347 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348 if (this.registerMenu && this.type != 'treeview') {
3349 Roo.bootstrap.MenuMgr.register(this);
3356 * Fires before this menu is displayed (return false to block)
3357 * @param {Roo.menu.Menu} this
3362 * Fires before this menu is hidden (return false to block)
3363 * @param {Roo.menu.Menu} this
3368 * Fires after this menu is displayed
3369 * @param {Roo.menu.Menu} this
3374 * Fires after this menu is hidden
3375 * @param {Roo.menu.Menu} this
3380 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381 * @param {Roo.menu.Menu} this
3382 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383 * @param {Roo.EventObject} e
3388 * Fires when the mouse is hovering over this menu
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.EventObject} e
3391 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3396 * Fires when the mouse exits this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when a menu item contained in this menu is clicked
3405 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406 * @param {Roo.EventObject} e
3410 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3417 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3420 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3422 registerMenu : true,
3424 menuItems :false, // stores the menu items..
3434 getChildContainer : function() {
3438 getAutoCreate : function(){
3440 //if (['right'].indexOf(this.align)!==-1) {
3441 // cfg.cn[1].cls += ' pull-right'
3447 cls : 'dropdown-menu' ,
3448 style : 'z-index:1000'
3452 if (this.type === 'submenu') {
3453 cfg.cls = 'submenu active';
3455 if (this.type === 'treeview') {
3456 cfg.cls = 'treeview-menu';
3461 initEvents : function() {
3463 // Roo.log("ADD event");
3464 // Roo.log(this.triggerEl.dom);
3466 this.triggerEl.on('click', this.onTriggerClick, this);
3468 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3471 if (this.triggerEl.hasClass('nav-item')) {
3472 // dropdown toggle on the 'a' in BS4?
3473 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3475 this.triggerEl.addClass('dropdown-toggle');
3478 this.el.on('touchstart' , this.onTouch, this);
3480 this.el.on('click' , this.onClick, this);
3482 this.el.on("mouseover", this.onMouseOver, this);
3483 this.el.on("mouseout", this.onMouseOut, this);
3487 findTargetItem : function(e)
3489 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3493 //Roo.log(t); Roo.log(t.id);
3495 //Roo.log(this.menuitems);
3496 return this.menuitems.get(t.id);
3498 //return this.items.get(t.menuItemId);
3504 onTouch : function(e)
3506 Roo.log("menu.onTouch");
3507 //e.stopEvent(); this make the user popdown broken
3511 onClick : function(e)
3513 Roo.log("menu.onClick");
3515 var t = this.findTargetItem(e);
3516 if(!t || t.isContainer){
3521 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3522 if(t == this.activeItem && t.shouldDeactivate(e)){
3523 this.activeItem.deactivate();
3524 delete this.activeItem;
3528 this.setActiveItem(t, true);
3536 Roo.log('pass click event');
3540 this.fireEvent("click", this, t, e);
3544 if(!t.href.length || t.href == '#'){
3545 (function() { _this.hide(); }).defer(100);
3550 onMouseOver : function(e){
3551 var t = this.findTargetItem(e);
3554 // if(t.canActivate && !t.disabled){
3555 // this.setActiveItem(t, true);
3559 this.fireEvent("mouseover", this, e, t);
3561 isVisible : function(){
3562 return !this.hidden;
3564 onMouseOut : function(e){
3565 var t = this.findTargetItem(e);
3568 // if(t == this.activeItem && t.shouldDeactivate(e)){
3569 // this.activeItem.deactivate();
3570 // delete this.activeItem;
3573 this.fireEvent("mouseout", this, e, t);
3578 * Displays this menu relative to another element
3579 * @param {String/HTMLElement/Roo.Element} element The element to align to
3580 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581 * the element (defaults to this.defaultAlign)
3582 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3584 show : function(el, pos, parentMenu)
3586 if (false === this.fireEvent("beforeshow", this)) {
3587 Roo.log("show canceled");
3590 this.parentMenu = parentMenu;
3595 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3598 * Displays this menu at a specific xy position
3599 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3602 showAt : function(xy, parentMenu, /* private: */_e){
3603 this.parentMenu = parentMenu;
3608 this.fireEvent("beforeshow", this);
3609 //xy = this.el.adjustForConstraints(xy);
3613 this.hideMenuItems();
3614 this.hidden = false;
3615 this.triggerEl.addClass('open');
3616 this.el.addClass('show');
3618 // reassign x when hitting right
3619 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3623 // reassign y when hitting bottom
3624 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3628 // but the list may align on trigger left or trigger top... should it be a properity?
3630 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3635 this.fireEvent("show", this);
3641 this.doFocus.defer(50, this);
3645 doFocus : function(){
3647 this.focusEl.focus();
3652 * Hides this menu and optionally all parent menus
3653 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3655 hide : function(deep)
3657 if (false === this.fireEvent("beforehide", this)) {
3658 Roo.log("hide canceled");
3661 this.hideMenuItems();
3662 if(this.el && this.isVisible()){
3664 if(this.activeItem){
3665 this.activeItem.deactivate();
3666 this.activeItem = null;
3668 this.triggerEl.removeClass('open');;
3669 this.el.removeClass('show');
3671 this.fireEvent("hide", this);
3673 if(deep === true && this.parentMenu){
3674 this.parentMenu.hide(true);
3678 onTriggerClick : function(e)
3680 Roo.log('trigger click');
3682 var target = e.getTarget();
3684 Roo.log(target.nodeName.toLowerCase());
3686 if(target.nodeName.toLowerCase() === 'i'){
3692 onTriggerPress : function(e)
3694 Roo.log('trigger press');
3695 //Roo.log(e.getTarget());
3696 // Roo.log(this.triggerEl.dom);
3698 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699 var pel = Roo.get(e.getTarget());
3700 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701 Roo.log('is treeview or dropdown?');
3705 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3709 if (this.isVisible()) {
3714 this.show(this.triggerEl, '?', false);
3717 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3724 hideMenuItems : function()
3726 Roo.log("hide Menu Items");
3731 this.el.select('.open',true).each(function(aa) {
3733 aa.removeClass('open');
3737 addxtypeChild : function (tree, cntr) {
3738 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3740 this.menuitems.add(comp);
3752 this.getEl().dom.innerHTML = '';
3753 this.menuitems.clear();
3767 * @class Roo.bootstrap.MenuItem
3768 * @extends Roo.bootstrap.Component
3769 * Bootstrap MenuItem class
3770 * @cfg {String} html the menu label
3771 * @cfg {String} href the link
3772 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774 * @cfg {Boolean} active used on sidebars to highlight active itesm
3775 * @cfg {String} fa favicon to show on left of menu item.
3776 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3780 * Create a new MenuItem
3781 * @param {Object} config The config object
3785 Roo.bootstrap.MenuItem = function(config){
3786 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3791 * The raw click event for the entire grid.
3792 * @param {Roo.bootstrap.MenuItem} this
3793 * @param {Roo.EventObject} e
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3803 preventDefault: false,
3804 isContainer : false,
3808 getAutoCreate : function(){
3810 if(this.isContainer){
3813 cls: 'dropdown-menu-item '
3823 cls : 'dropdown-item',
3828 if (this.fa !== false) {
3831 cls : 'fa fa-' + this.fa
3840 cls: 'dropdown-menu-item',
3843 if (this.parent().type == 'treeview') {
3844 cfg.cls = 'treeview-menu';
3847 cfg.cls += ' active';
3852 anc.href = this.href || cfg.cn[0].href ;
3853 ctag.html = this.html || cfg.cn[0].html ;
3857 initEvents: function()
3859 if (this.parent().type == 'treeview') {
3860 this.el.select('a').on('click', this.onClick, this);
3864 this.menu.parentType = this.xtype;
3865 this.menu.triggerEl = this.el;
3866 this.menu = this.addxtype(Roo.apply({}, this.menu));
3870 onClick : function(e)
3872 Roo.log('item on click ');
3874 if(this.preventDefault){
3877 //this.parent().hideMenuItems();
3879 this.fireEvent('click', this, e);
3898 * @class Roo.bootstrap.MenuSeparator
3899 * @extends Roo.bootstrap.Component
3900 * Bootstrap MenuSeparator class
3903 * Create a new MenuItem
3904 * @param {Object} config The config object
3908 Roo.bootstrap.MenuSeparator = function(config){
3909 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3914 getAutoCreate : function(){
3933 * @class Roo.bootstrap.Modal
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap Modal class
3936 * @cfg {String} title Title of dialog
3937 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3939 * @cfg {Boolean} specificTitle default false
3940 * @cfg {Array} buttons Array of buttons or standard button set..
3941 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942 * @cfg {Boolean} animate default true
3943 * @cfg {Boolean} allow_close default true
3944 * @cfg {Boolean} fitwindow default false
3945 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947 * @cfg {String} size (sm|lg|xl) default empty
3948 * @cfg {Number} max_width set the max width of modal
3949 * @cfg {Boolean} editableTitle can the title be edited
3954 * Create a new Modal Dialog
3955 * @param {Object} config The config object
3958 Roo.bootstrap.Modal = function(config){
3959 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3964 * The raw btnclick event for the button
3965 * @param {Roo.EventObject} e
3970 * Fire when dialog resize
3971 * @param {Roo.bootstrap.Modal} this
3972 * @param {Roo.EventObject} e
3976 * @event titlechanged
3977 * Fire when the editable title has been changed
3978 * @param {Roo.bootstrap.Modal} this
3979 * @param {Roo.EventObject} value
3981 "titlechanged" : true
3984 this.buttons = this.buttons || [];
3987 this.tmpl = Roo.factory(this.tmpl);
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3994 title : 'test dialog',
4004 specificTitle: false,
4006 buttonPosition: 'right',
4028 editableTitle : false,
4030 onRender : function(ct, position)
4032 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4035 var cfg = Roo.apply({}, this.getAutoCreate());
4038 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4040 //if (!cfg.name.length) {
4044 cfg.cls += ' ' + this.cls;
4047 cfg.style = this.style;
4049 this.el = Roo.get(document.body).createChild(cfg, position);
4051 //var type = this.el.dom.type;
4054 if(this.tabIndex !== undefined){
4055 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4058 this.dialogEl = this.el.select('.modal-dialog',true).first();
4059 this.bodyEl = this.el.select('.modal-body',true).first();
4060 this.closeEl = this.el.select('.modal-header .close', true).first();
4061 this.headerEl = this.el.select('.modal-header',true).first();
4062 this.titleEl = this.el.select('.modal-title',true).first();
4063 this.footerEl = this.el.select('.modal-footer',true).first();
4065 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4067 //this.el.addClass("x-dlg-modal");
4069 if (this.buttons.length) {
4070 Roo.each(this.buttons, function(bb) {
4071 var b = Roo.apply({}, bb);
4072 b.xns = b.xns || Roo.bootstrap;
4073 b.xtype = b.xtype || 'Button';
4074 if (typeof(b.listeners) == 'undefined') {
4075 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4078 var btn = Roo.factory(b);
4080 btn.render(this.getButtonContainer());
4084 // render the children.
4087 if(typeof(this.items) != 'undefined'){
4088 var items = this.items;
4091 for(var i =0;i < items.length;i++) {
4092 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4096 this.items = nitems;
4098 // where are these used - they used to be body/close/footer
4102 //this.el.addClass([this.fieldClass, this.cls]);
4106 getAutoCreate : function()
4108 // we will default to modal-body-overflow - might need to remove or make optional later.
4110 cls : 'modal-body enable-modal-body-overflow ',
4111 html : this.html || ''
4116 cls : 'modal-title',
4120 if(this.specificTitle){ // WTF is this?
4125 if (this.allow_close && Roo.bootstrap.version == 3) {
4135 if (this.editableTitle) {
4137 cls: 'form-control roo-editable-title d-none',
4143 if (this.allow_close && Roo.bootstrap.version == 4) {
4153 if(this.size.length){
4154 size = 'modal-' + this.size;
4157 var footer = Roo.bootstrap.version == 3 ?
4159 cls : 'modal-footer',
4163 cls: 'btn-' + this.buttonPosition
4168 { // BS4 uses mr-auto on left buttons....
4169 cls : 'modal-footer'
4180 cls: "modal-dialog " + size,
4183 cls : "modal-content",
4186 cls : 'modal-header',
4201 modal.cls += ' fade';
4207 getChildContainer : function() {
4212 getButtonContainer : function() {
4214 return Roo.bootstrap.version == 4 ?
4215 this.el.select('.modal-footer',true).first()
4216 : this.el.select('.modal-footer div',true).first();
4219 initEvents : function()
4221 if (this.allow_close) {
4222 this.closeEl.on('click', this.hide, this);
4224 Roo.EventManager.onWindowResize(this.resize, this, true);
4225 if (this.editableTitle) {
4226 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4227 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228 this.headerEditEl.on('keyup', function(e) {
4229 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230 this.toggleHeaderInput(false)
4233 this.headerEditEl.on('blur', function(e) {
4234 this.toggleHeaderInput(false)
4243 this.maskEl.setSize(
4244 Roo.lib.Dom.getViewWidth(true),
4245 Roo.lib.Dom.getViewHeight(true)
4248 if (this.fitwindow) {
4252 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4258 if(this.max_width !== 0) {
4260 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4263 this.setSize(w, this.height);
4267 if(this.max_height) {
4268 this.setSize(w,Math.min(
4270 Roo.lib.Dom.getViewportHeight(true) - 60
4276 if(!this.fit_content) {
4277 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4281 this.setSize(w, Math.min(
4283 this.headerEl.getHeight() +
4284 this.footerEl.getHeight() +
4285 this.getChildHeight(this.bodyEl.dom.childNodes),
4286 Roo.lib.Dom.getViewportHeight(true) - 60)
4292 setSize : function(w,h)
4303 if (!this.rendered) {
4306 this.toggleHeaderInput(false);
4307 //this.el.setStyle('display', 'block');
4308 this.el.removeClass('hideing');
4309 this.el.dom.style.display='block';
4311 Roo.get(document.body).addClass('modal-open');
4313 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4316 this.el.addClass('show');
4317 this.el.addClass('in');
4320 this.el.addClass('show');
4321 this.el.addClass('in');
4324 // not sure how we can show data in here..
4326 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4329 Roo.get(document.body).addClass("x-body-masked");
4331 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4332 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333 this.maskEl.dom.style.display = 'block';
4334 this.maskEl.addClass('show');
4339 this.fireEvent('show', this);
4341 // set zindex here - otherwise it appears to be ignored...
4342 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4345 this.items.forEach( function(e) {
4346 e.layout ? e.layout() : false;
4354 if(this.fireEvent("beforehide", this) !== false){
4356 this.maskEl.removeClass('show');
4358 this.maskEl.dom.style.display = '';
4359 Roo.get(document.body).removeClass("x-body-masked");
4360 this.el.removeClass('in');
4361 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4363 if(this.animate){ // why
4364 this.el.addClass('hideing');
4365 this.el.removeClass('show');
4367 if (!this.el.hasClass('hideing')) {
4368 return; // it's been shown again...
4371 this.el.dom.style.display='';
4373 Roo.get(document.body).removeClass('modal-open');
4374 this.el.removeClass('hideing');
4378 this.el.removeClass('show');
4379 this.el.dom.style.display='';
4380 Roo.get(document.body).removeClass('modal-open');
4383 this.fireEvent('hide', this);
4386 isVisible : function()
4389 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4393 addButton : function(str, cb)
4397 var b = Roo.apply({}, { html : str } );
4398 b.xns = b.xns || Roo.bootstrap;
4399 b.xtype = b.xtype || 'Button';
4400 if (typeof(b.listeners) == 'undefined') {
4401 b.listeners = { click : cb.createDelegate(this) };
4404 var btn = Roo.factory(b);
4406 btn.render(this.getButtonContainer());
4412 setDefaultButton : function(btn)
4414 //this.el.select('.modal-footer').()
4417 resizeTo: function(w,h)
4419 this.dialogEl.setWidth(w);
4421 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4423 this.bodyEl.setHeight(h - diff);
4425 this.fireEvent('resize', this);
4428 setContentSize : function(w, h)
4432 onButtonClick: function(btn,e)
4435 this.fireEvent('btnclick', btn.name, e);
4438 * Set the title of the Dialog
4439 * @param {String} str new Title
4441 setTitle: function(str) {
4442 this.titleEl.dom.innerHTML = str;
4446 * Set the body of the Dialog
4447 * @param {String} str new Title
4449 setBody: function(str) {
4450 this.bodyEl.dom.innerHTML = str;
4453 * Set the body of the Dialog using the template
4454 * @param {Obj} data - apply this data to the template and replace the body contents.
4456 applyBody: function(obj)
4459 Roo.log("Error - using apply Body without a template");
4462 this.tmpl.overwrite(this.bodyEl, obj);
4465 getChildHeight : function(child_nodes)
4469 child_nodes.length == 0
4474 var child_height = 0;
4476 for(var i = 0; i < child_nodes.length; i++) {
4479 * for modal with tabs...
4480 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4482 var layout_childs = child_nodes[i].childNodes;
4484 for(var j = 0; j < layout_childs.length; j++) {
4486 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4488 var layout_body_childs = layout_childs[j].childNodes;
4490 for(var k = 0; k < layout_body_childs.length; k++) {
4492 if(layout_body_childs[k].classList.contains('navbar')) {
4493 child_height += layout_body_childs[k].offsetHeight;
4497 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4499 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4501 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4503 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4519 child_height += child_nodes[i].offsetHeight;
4520 // Roo.log(child_nodes[i].offsetHeight);
4523 return child_height;
4525 toggleHeaderInput : function(is_edit)
4527 if (!this.editableTitle) {
4528 return; // not editable.
4530 if (is_edit && this.is_header_editing) {
4531 return; // already editing..
4535 this.headerEditEl.dom.value = this.title;
4536 this.headerEditEl.removeClass('d-none');
4537 this.headerEditEl.dom.focus();
4538 this.titleEl.addClass('d-none');
4540 this.is_header_editing = true;
4543 // flip back to not editing.
4544 this.title = this.headerEditEl.dom.value;
4545 this.headerEditEl.addClass('d-none');
4546 this.titleEl.removeClass('d-none');
4547 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548 this.is_header_editing = false;
4549 this.fireEvent('titlechanged', this, this.title);
4558 Roo.apply(Roo.bootstrap.Modal, {
4560 * Button config that displays a single OK button
4569 * Button config that displays Yes and No buttons
4585 * Button config that displays OK and Cancel buttons
4600 * Button config that displays Yes, No and Cancel buttons
4625 * messagebox - can be used as a replace
4629 * @class Roo.MessageBox
4630 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4639 // process text value...
4643 // Show a dialog using config options:
4645 title:'Save Changes?',
4646 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647 buttons: Roo.Msg.YESNOCANCEL,
4654 Roo.bootstrap.MessageBox = function(){
4655 var dlg, opt, mask, waitTimer;
4656 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657 var buttons, activeTextEl, bwidth;
4661 var handleButton = function(button){
4663 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4667 var handleHide = function(){
4669 dlg.el.removeClass(opt.cls);
4672 // Roo.TaskMgr.stop(waitTimer);
4673 // waitTimer = null;
4678 var updateButtons = function(b){
4681 buttons["ok"].hide();
4682 buttons["cancel"].hide();
4683 buttons["yes"].hide();
4684 buttons["no"].hide();
4685 dlg.footerEl.hide();
4689 dlg.footerEl.show();
4690 for(var k in buttons){
4691 if(typeof buttons[k] != "function"){
4694 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695 width += buttons[k].el.getWidth()+15;
4705 var handleEsc = function(d, k, e){
4706 if(opt && opt.closable !== false){
4716 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717 * @return {Roo.BasicDialog} The BasicDialog element
4719 getDialog : function(){
4721 dlg = new Roo.bootstrap.Modal( {
4724 //constraintoviewport:false,
4726 //collapsible : false,
4731 //buttonAlign:"center",
4732 closeClick : function(){
4733 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4736 handleButton("cancel");
4741 dlg.on("hide", handleHide);
4743 //dlg.addKeyListener(27, handleEsc);
4745 this.buttons = buttons;
4746 var bt = this.buttonText;
4747 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4752 bodyEl = dlg.bodyEl.createChild({
4754 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755 '<textarea class="roo-mb-textarea"></textarea>' +
4756 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4758 msgEl = bodyEl.dom.firstChild;
4759 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760 textboxEl.enableDisplayMode();
4761 textboxEl.addKeyListener([10,13], function(){
4762 if(dlg.isVisible() && opt && opt.buttons){
4765 }else if(opt.buttons.yes){
4766 handleButton("yes");
4770 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771 textareaEl.enableDisplayMode();
4772 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773 progressEl.enableDisplayMode();
4775 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776 var pf = progressEl.dom.firstChild;
4778 pp = Roo.get(pf.firstChild);
4779 pp.setHeight(pf.offsetHeight);
4787 * Updates the message box body text
4788 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789 * the XHTML-compliant non-breaking space character '&#160;')
4790 * @return {Roo.MessageBox} This message box
4792 updateText : function(text)
4794 if(!dlg.isVisible() && !opt.width){
4795 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4798 msgEl.innerHTML = text || ' ';
4800 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4803 Math.min(opt.width || cw , this.maxWidth),
4804 Math.max(opt.minWidth || this.minWidth, bwidth)
4807 activeTextEl.setWidth(w);
4809 if(dlg.isVisible()){
4810 dlg.fixedcenter = false;
4812 // to big, make it scroll. = But as usual stupid IE does not support
4815 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4819 bodyEl.dom.style.height = '';
4820 bodyEl.dom.style.overflowY = '';
4823 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.overflowX = '';
4828 dlg.setContentSize(w, bodyEl.getHeight());
4829 if(dlg.isVisible()){
4830 dlg.fixedcenter = true;
4836 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4837 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840 * @return {Roo.MessageBox} This message box
4842 updateProgress : function(value, text){
4844 this.updateText(text);
4847 if (pp) { // weird bug on my firefox - for some reason this is not defined
4848 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855 * Returns true if the message box is currently displayed
4856 * @return {Boolean} True if the message box is visible, else false
4858 isVisible : function(){
4859 return dlg && dlg.isVisible();
4863 * Hides the message box if it is displayed
4866 if(this.isVisible()){
4872 * Displays a new message box, or reinitializes an existing message box, based on the config options
4873 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874 * The following config object properties are supported:
4876 Property Type Description
4877 ---------- --------------- ------------------------------------------------------------------------------------
4878 animEl String/Element An id or Element from which the message box should animate as it opens and
4879 closes (defaults to undefined)
4880 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable Boolean False to hide the top-right close button (defaults to true). Note that
4883 progress and wait dialogs will ignore this property and always hide the
4884 close button as they can only be closed programmatically.
4885 cls String A custom CSS class to apply to the message box element
4886 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4887 displayed (defaults to 75)
4888 fn Function A callback function to execute after closing the dialog. The arguments to the
4889 function will be btn (the name of the button that was clicked, if applicable,
4890 e.g. "ok"), and text (the value of the active text field, if applicable).
4891 Progress and wait dialogs will ignore this option since they do not respond to
4892 user actions and can only be closed programmatically, so any required function
4893 should be called by the same code after it closes the dialog.
4894 icon String A CSS class that provides a background image to be used as an icon for
4895 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4897 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4898 modal Boolean False to allow user interaction with the page while the message box is
4899 displayed (defaults to true)
4900 msg String A string that will replace the existing message box body text (defaults
4901 to the XHTML-compliant non-breaking space character ' ')
4902 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4903 progress Boolean True to display a progress bar (defaults to false)
4904 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4907 title String The title text
4908 value String The string value to set into the active textbox element if displayed
4909 wait Boolean True to display a progress bar (defaults to false)
4910 width Number The width of the dialog in pixels
4917 msg: 'Please enter your address:',
4919 buttons: Roo.MessageBox.OKCANCEL,
4922 animEl: 'addAddressBtn'
4925 * @param {Object} config Configuration options
4926 * @return {Roo.MessageBox} This message box
4928 show : function(options)
4931 // this causes nightmares if you show one dialog after another
4932 // especially on callbacks..
4934 if(this.isVisible()){
4937 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4939 Roo.log("New Dialog Message:" + options.msg )
4940 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4944 var d = this.getDialog();
4946 d.setTitle(opt.title || " ");
4947 d.closeEl.setDisplayed(opt.closable !== false);
4948 activeTextEl = textboxEl;
4949 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4954 textareaEl.setHeight(typeof opt.multiline == "number" ?
4955 opt.multiline : this.defaultTextHeight);
4956 activeTextEl = textareaEl;
4965 progressEl.setDisplayed(opt.progress === true);
4967 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4969 this.updateProgress(0);
4970 activeTextEl.dom.value = opt.value || "";
4972 dlg.setDefaultButton(activeTextEl);
4974 var bs = opt.buttons;
4978 }else if(bs && bs.yes){
4979 db = buttons["yes"];
4981 dlg.setDefaultButton(db);
4983 bwidth = updateButtons(opt.buttons);
4984 this.updateText(opt.msg);
4986 d.el.addClass(opt.cls);
4988 d.proxyDrag = opt.proxyDrag === true;
4989 d.modal = opt.modal !== false;
4990 d.mask = opt.modal !== false ? mask : false;
4992 // force it to the end of the z-index stack so it gets a cursor in FF
4993 document.body.appendChild(dlg.el.dom);
4994 d.animateTarget = null;
4995 d.show(options.animEl);
5001 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5002 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003 * and closing the message box when the process is complete.
5004 * @param {String} title The title bar text
5005 * @param {String} msg The message box body text
5006 * @return {Roo.MessageBox} This message box
5008 progress : function(title, msg){
5015 minWidth: this.minProgressWidth,
5022 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023 * If a callback function is passed it will be called after the user clicks the button, and the
5024 * id of the button that was clicked will be passed as the only parameter to the callback
5025 * (could also be the top-right close button).
5026 * @param {String} title The title bar text
5027 * @param {String} msg The message box body text
5028 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029 * @param {Object} scope (optional) The scope of the callback function
5030 * @return {Roo.MessageBox} This message box
5032 alert : function(title, msg, fn, scope)
5047 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5048 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049 * You are responsible for closing the message box when the process is complete.
5050 * @param {String} msg The message box body text
5051 * @param {String} title (optional) The title bar text
5052 * @return {Roo.MessageBox} This message box
5054 wait : function(msg, title){
5065 waitTimer = Roo.TaskMgr.start({
5067 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5075 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078 * @param {String} title The title bar text
5079 * @param {String} msg The message box body text
5080 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081 * @param {Object} scope (optional) The scope of the callback function
5082 * @return {Roo.MessageBox} This message box
5084 confirm : function(title, msg, fn, scope){
5088 buttons: this.YESNO,
5097 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5099 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100 * (could also be the top-right close button) and the text that was entered will be passed as the two
5101 * parameters to the callback.
5102 * @param {String} title The title bar text
5103 * @param {String} msg The message box body text
5104 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105 * @param {Object} scope (optional) The scope of the callback function
5106 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108 * @return {Roo.MessageBox} This message box
5110 prompt : function(title, msg, fn, scope, multiline){
5114 buttons: this.OKCANCEL,
5119 multiline: multiline,
5126 * Button config that displays a single OK button
5131 * Button config that displays Yes and No buttons
5134 YESNO : {yes:true, no:true},
5136 * Button config that displays OK and Cancel buttons
5139 OKCANCEL : {ok:true, cancel:true},
5141 * Button config that displays Yes, No and Cancel buttons
5144 YESNOCANCEL : {yes:true, no:true, cancel:true},
5147 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5150 defaultTextHeight : 75,
5152 * The maximum width in pixels of the message box (defaults to 600)
5157 * The minimum width in pixels of the message box (defaults to 100)
5162 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5163 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5166 minProgressWidth : 250,
5168 * An object containing the default button text strings that can be overriden for localized language support.
5169 * Supported properties are: ok, cancel, yes and no.
5170 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5183 * Shorthand for {@link Roo.MessageBox}
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5195 * @class Roo.bootstrap.Navbar
5196 * @extends Roo.bootstrap.Component
5197 * Bootstrap Navbar class
5200 * Create a new Navbar
5201 * @param {Object} config The config object
5205 Roo.bootstrap.Navbar = function(config){
5206 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5210 * @event beforetoggle
5211 * Fire before toggle the menu
5212 * @param {Roo.EventObject} e
5214 "beforetoggle" : true
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5227 getAutoCreate : function(){
5230 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5234 initEvents :function ()
5236 //Roo.log(this.el.select('.navbar-toggle',true));
5237 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5246 var size = this.el.getSize();
5247 this.maskEl.setSize(size.width, size.height);
5248 this.maskEl.enableDisplayMode("block");
5257 getChildContainer : function()
5259 if (this.el && this.el.select('.collapse').getCount()) {
5260 return this.el.select('.collapse',true).first();
5275 onToggle : function()
5278 if(this.fireEvent('beforetoggle', this) === false){
5281 var ce = this.el.select('.navbar-collapse',true).first();
5283 if (!ce.hasClass('show')) {
5293 * Expand the navbar pulldown
5295 expand : function ()
5298 var ce = this.el.select('.navbar-collapse',true).first();
5299 if (ce.hasClass('collapsing')) {
5302 ce.dom.style.height = '';
5304 ce.addClass('in'); // old...
5305 ce.removeClass('collapse');
5306 ce.addClass('show');
5307 var h = ce.getHeight();
5309 ce.removeClass('show');
5310 // at this point we should be able to see it..
5311 ce.addClass('collapsing');
5313 ce.setHeight(0); // resize it ...
5314 ce.on('transitionend', function() {
5315 //Roo.log('done transition');
5316 ce.removeClass('collapsing');
5317 ce.addClass('show');
5318 ce.removeClass('collapse');
5320 ce.dom.style.height = '';
5321 }, this, { single: true} );
5323 ce.dom.scrollTop = 0;
5326 * Collapse the navbar pulldown
5328 collapse : function()
5330 var ce = this.el.select('.navbar-collapse',true).first();
5332 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333 // it's collapsed or collapsing..
5336 ce.removeClass('in'); // old...
5337 ce.setHeight(ce.getHeight());
5338 ce.removeClass('show');
5339 ce.addClass('collapsing');
5341 ce.on('transitionend', function() {
5342 ce.dom.style.height = '';
5343 ce.removeClass('collapsing');
5344 ce.addClass('collapse');
5345 }, this, { single: true} );
5365 * @class Roo.bootstrap.NavSimplebar
5366 * @extends Roo.bootstrap.Navbar
5367 * Bootstrap Sidebar class
5369 * @cfg {Boolean} inverse is inverted color
5371 * @cfg {String} type (nav | pills | tabs)
5372 * @cfg {Boolean} arrangement stacked | justified
5373 * @cfg {String} align (left | right) alignment
5375 * @cfg {Boolean} main (true|false) main nav bar? default false
5376 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5378 * @cfg {String} tag (header|footer|nav|div) default is nav
5380 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5384 * Create a new Sidebar
5385 * @param {Object} config The config object
5389 Roo.bootstrap.NavSimplebar = function(config){
5390 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5409 getAutoCreate : function(){
5413 tag : this.tag || 'div',
5414 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5416 if (['light','white'].indexOf(this.weight) > -1) {
5417 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5419 cfg.cls += ' bg-' + this.weight;
5422 cfg.cls += ' navbar-inverse';
5426 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5428 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5437 cls: 'nav nav-' + this.xtype,
5443 this.type = this.type || 'nav';
5444 if (['tabs','pills'].indexOf(this.type) != -1) {
5445 cfg.cn[0].cls += ' nav-' + this.type
5449 if (this.type!=='nav') {
5450 Roo.log('nav type must be nav/tabs/pills')
5452 cfg.cn[0].cls += ' navbar-nav'
5458 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459 cfg.cn[0].cls += ' nav-' + this.arrangement;
5463 if (this.align === 'right') {
5464 cfg.cn[0].cls += ' navbar-right';
5489 * navbar-expand-md fixed-top
5493 * @class Roo.bootstrap.NavHeaderbar
5494 * @extends Roo.bootstrap.NavSimplebar
5495 * Bootstrap Sidebar class
5497 * @cfg {String} brand what is brand
5498 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499 * @cfg {String} brand_href href of the brand
5500 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5501 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5506 * Create a new Sidebar
5507 * @param {Object} config The config object
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5523 desktopCenter : false,
5526 getAutoCreate : function(){
5529 tag: this.nav || 'nav',
5530 cls: 'navbar navbar-expand-md',
5536 if (this.desktopCenter) {
5537 cn.push({cls : 'container', cn : []});
5545 cls: 'navbar-toggle navbar-toggler',
5546 'data-toggle': 'collapse',
5551 html: 'Toggle navigation'
5555 cls: 'icon-bar navbar-toggler-icon'
5568 cn.push( Roo.bootstrap.version == 4 ? btn : {
5570 cls: 'navbar-header',
5579 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5583 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5585 if (['light','white'].indexOf(this.weight) > -1) {
5586 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5588 cfg.cls += ' bg-' + this.weight;
5591 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5594 // tag can override this..
5596 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5599 if (this.brand !== '') {
5600 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5603 href: this.brand_href ? this.brand_href : '#',
5604 cls: 'navbar-brand',
5612 cfg.cls += ' main-nav';
5620 getHeaderChildContainer : function()
5622 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623 return this.el.select('.navbar-header',true).first();
5626 return this.getChildContainer();
5629 getChildContainer : function()
5632 return this.el.select('.roo-navbar-collapse',true).first();
5637 initEvents : function()
5639 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5641 if (this.autohide) {
5646 Roo.get(document).on('scroll',function(e) {
5647 var ns = Roo.get(document).getScroll().top;
5648 var os = prevScroll;
5652 ft.removeClass('slideDown');
5653 ft.addClass('slideUp');
5656 ft.removeClass('slideUp');
5657 ft.addClass('slideDown');
5678 * @class Roo.bootstrap.NavSidebar
5679 * @extends Roo.bootstrap.Navbar
5680 * Bootstrap Sidebar class
5683 * Create a new Sidebar
5684 * @param {Object} config The config object
5688 Roo.bootstrap.NavSidebar = function(config){
5689 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5694 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5696 getAutoCreate : function(){
5701 cls: 'sidebar sidebar-nav'
5723 * @class Roo.bootstrap.NavGroup
5724 * @extends Roo.bootstrap.Component
5725 * Bootstrap NavGroup class
5726 * @cfg {String} align (left|right)
5727 * @cfg {Boolean} inverse
5728 * @cfg {String} type (nav|pills|tab) default nav
5729 * @cfg {String} navId - reference Id for navbar.
5733 * Create a new nav group
5734 * @param {Object} config The config object
5737 Roo.bootstrap.NavGroup = function(config){
5738 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5741 Roo.bootstrap.NavGroup.register(this);
5745 * Fires when the active item changes
5746 * @param {Roo.bootstrap.NavGroup} this
5747 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5766 getAutoCreate : function()
5768 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774 if (Roo.bootstrap.version == 4) {
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cls += ' nav-' + this.type;
5778 // trying to remove so header bar can right align top?
5779 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780 // do not use on header bar...
5781 cfg.cls += ' navbar-nav';
5786 if (['tabs','pills'].indexOf(this.type) != -1) {
5787 cfg.cls += ' nav-' + this.type
5789 if (this.type !== 'nav') {
5790 Roo.log('nav type must be nav/tabs/pills')
5792 cfg.cls += ' navbar-nav'
5796 if (this.parent() && this.parent().sidebar) {
5799 cls: 'dashboard-menu sidebar-menu'
5805 if (this.form === true) {
5808 cls: 'navbar-form form-inline'
5810 //nav navbar-right ml-md-auto
5811 if (this.align === 'right') {
5812 cfg.cls += ' navbar-right ml-md-auto';
5814 cfg.cls += ' navbar-left';
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' mr-auto';
5825 cfg.cls += ' navbar-inverse';
5833 * sets the active Navigation item
5834 * @param {Roo.bootstrap.NavItem} the new current navitem
5836 setActiveItem : function(item)
5839 Roo.each(this.navItems, function(v){
5844 v.setActive(false, true);
5851 item.setActive(true, true);
5852 this.fireEvent('changed', this, item, prev);
5857 * gets the active Navigation item
5858 * @return {Roo.bootstrap.NavItem} the current navitem
5860 getActive : function()
5864 Roo.each(this.navItems, function(v){
5875 indexOfNav : function()
5879 Roo.each(this.navItems, function(v,i){
5890 * adds a Navigation item
5891 * @param {Roo.bootstrap.NavItem} the navitem to add
5893 addItem : function(cfg)
5895 if (this.form && Roo.bootstrap.version == 4) {
5898 var cn = new Roo.bootstrap.NavItem(cfg);
5900 cn.parentId = this.id;
5901 cn.onRender(this.el, null);
5905 * register a Navigation item
5906 * @param {Roo.bootstrap.NavItem} the navitem to add
5908 register : function(item)
5910 this.navItems.push( item);
5911 item.navId = this.navId;
5916 * clear all the Navigation item
5919 clearAll : function()
5922 this.el.dom.innerHTML = '';
5925 getNavItem: function(tabId)
5928 Roo.each(this.navItems, function(e) {
5929 if (e.tabId == tabId) {
5939 setActiveNext : function()
5941 var i = this.indexOfNav(this.getActive());
5942 if (i > this.navItems.length) {
5945 this.setActiveItem(this.navItems[i+1]);
5947 setActivePrev : function()
5949 var i = this.indexOfNav(this.getActive());
5953 this.setActiveItem(this.navItems[i-1]);
5955 clearWasActive : function(except) {
5956 Roo.each(this.navItems, function(e) {
5957 if (e.tabId != except.tabId && e.was_active) {
5958 e.was_active = false;
5965 getWasActive : function ()
5968 Roo.each(this.navItems, function(e) {
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5987 * register a Navigation Group
5988 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5990 register : function(navgrp)
5992 this.groups[navgrp.navId] = navgrp;
5996 * fetch a Navigation Group based on the navigation ID
5997 * @param {string} the navgroup to add
5998 * @returns {Roo.bootstrap.NavGroup} the navgroup
6000 get: function(navId) {
6001 if (typeof(this.groups[navId]) == 'undefined') {
6003 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6005 return this.groups[navId] ;
6020 * @class Roo.bootstrap.NavItem
6021 * @extends Roo.bootstrap.Component
6022 * Bootstrap Navbar.NavItem class
6023 * @cfg {String} href link to
6024 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6026 * @cfg {String} html content of button
6027 * @cfg {String} badge text inside badge
6028 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029 * @cfg {String} glyphicon DEPRICATED - use fa
6030 * @cfg {String} icon DEPRICATED - use fa
6031 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032 * @cfg {Boolean} active Is item active
6033 * @cfg {Boolean} disabled Is item disabled
6035 * @cfg {Boolean} preventDefault (true | false) default false
6036 * @cfg {String} tabId the tab that this item activates.
6037 * @cfg {String} tagtype (a|span) render as a href or span?
6038 * @cfg {Boolean} animateRef (true|false) link to element default false
6041 * Create a new Navbar Item
6042 * @param {Object} config The config object
6044 Roo.bootstrap.NavItem = function(config){
6045 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6050 * The raw click event for the entire grid.
6051 * @param {Roo.EventObject} e
6056 * Fires when the active item active state changes
6057 * @param {Roo.bootstrap.NavItem} this
6058 * @param {boolean} state the new state
6064 * Fires when scroll to element
6065 * @param {Roo.bootstrap.NavItem} this
6066 * @param {Object} options
6067 * @param {Roo.EventObject} e
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6084 preventDefault : false,
6092 button_outline : false,
6096 getAutoCreate : function(){
6104 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6106 if (this.disabled) {
6107 cfg.cls += ' disabled';
6111 if (this.button_weight.length) {
6112 cfg.tag = this.href ? 'a' : 'button';
6113 cfg.html = this.html || '';
6114 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6116 cfg.href = this.href;
6119 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6122 // menu .. should add dropdown-menu class - so no need for carat..
6124 if (this.badge !== '') {
6126 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6131 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6135 href : this.href || "#",
6136 html: this.html || ''
6139 if (this.tagtype == 'a') {
6140 cfg.cn[0].cls = 'nav-link';
6143 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6146 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6148 if(this.glyphicon) {
6149 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6154 cfg.cn[0].html += " <span class='caret'></span>";
6158 if (this.badge !== '') {
6160 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6168 onRender : function(ct, position)
6170 // Roo.log("Call onRender: " + this.xtype);
6171 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6175 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176 this.navLink = this.el.select('.nav-link',true).first();
6181 initEvents: function()
6183 if (typeof (this.menu) != 'undefined') {
6184 this.menu.parentType = this.xtype;
6185 this.menu.triggerEl = this.el;
6186 this.menu = this.addxtype(Roo.apply({}, this.menu));
6189 this.el.select('a',true).on('click', this.onClick, this);
6191 if(this.tagtype == 'span'){
6192 this.el.select('span',true).on('click', this.onClick, this);
6195 // at this point parent should be available..
6196 this.parent().register(this);
6199 onClick : function(e)
6201 if (e.getTarget('.dropdown-menu-item')) {
6202 // did you click on a menu itemm.... - then don't trigger onclick..
6207 this.preventDefault ||
6210 Roo.log("NavItem - prevent Default?");
6214 if (this.disabled) {
6218 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219 if (tg && tg.transition) {
6220 Roo.log("waiting for the transitionend");
6226 //Roo.log("fire event clicked");
6227 if(this.fireEvent('click', this, e) === false){
6231 if(this.tagtype == 'span'){
6235 //Roo.log(this.href);
6236 var ael = this.el.select('a',true).first();
6239 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242 return; // ignore... - it's a 'hash' to another page.
6244 Roo.log("NavItem - prevent Default?");
6246 this.scrollToElement(e);
6250 var p = this.parent();
6252 if (['tabs','pills'].indexOf(p.type)!==-1) {
6253 if (typeof(p.setActiveItem) !== 'undefined') {
6254 p.setActiveItem(this);
6258 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260 // remove the collapsed menu expand...
6261 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6265 isActive: function () {
6268 setActive : function(state, fire, is_was_active)
6270 if (this.active && !state && this.navId) {
6271 this.was_active = true;
6272 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6274 nv.clearWasActive(this);
6278 this.active = state;
6281 this.el.removeClass('active');
6282 this.navLink ? this.navLink.removeClass('active') : false;
6283 } else if (!this.el.hasClass('active')) {
6285 this.el.addClass('active');
6286 if (Roo.bootstrap.version == 4 && this.navLink ) {
6287 this.navLink.addClass('active');
6292 this.fireEvent('changed', this, state);
6295 // show a panel if it's registered and related..
6297 if (!this.navId || !this.tabId || !state || is_was_active) {
6301 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6305 var pan = tg.getPanelByName(this.tabId);
6309 // if we can not flip to new panel - go back to old nav highlight..
6310 if (false == tg.showPanel(pan)) {
6311 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6313 var onav = nv.getWasActive();
6315 onav.setActive(true, false, true);
6324 // this should not be here...
6325 setDisabled : function(state)
6327 this.disabled = state;
6329 this.el.removeClass('disabled');
6330 } else if (!this.el.hasClass('disabled')) {
6331 this.el.addClass('disabled');
6337 * Fetch the element to display the tooltip on.
6338 * @return {Roo.Element} defaults to this.el
6340 tooltipEl : function()
6342 return this.el.select('' + this.tagtype + '', true).first();
6345 scrollToElement : function(e)
6347 var c = document.body;
6350 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6352 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353 c = document.documentElement;
6356 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362 var o = target.calcOffsetsTo(c);
6369 this.fireEvent('scrollto', this, options, e);
6371 Roo.get(c).scrollTo('top', options.value, true);
6384 * <span> icon </span>
6385 * <span> text </span>
6386 * <span>badge </span>
6390 * @class Roo.bootstrap.NavSidebarItem
6391 * @extends Roo.bootstrap.NavItem
6392 * Bootstrap Navbar.NavSidebarItem class
6393 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394 * {Boolean} open is the menu open
6395 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397 * {String} buttonSize (sm|md|lg)the extra classes for the button
6398 * {Boolean} showArrow show arrow next to the text (default true)
6400 * Create a new Navbar Button
6401 * @param {Object} config The config object
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6409 * The raw click event for the entire grid.
6410 * @param {Roo.EventObject} e
6415 * Fires when the active item active state changes
6416 * @param {Roo.bootstrap.NavSidebarItem} this
6417 * @param {boolean} state the new state
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6427 badgeWeight : 'default',
6433 buttonWeight : 'default',
6439 getAutoCreate : function(){
6444 href : this.href || '#',
6450 if(this.buttonView){
6453 href : this.href || '#',
6454 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6467 cfg.cls += ' active';
6470 if (this.disabled) {
6471 cfg.cls += ' disabled';
6474 cfg.cls += ' open x-open';
6477 if (this.glyphicon || this.icon) {
6478 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6479 a.cn.push({ tag : 'i', cls : c }) ;
6482 if(!this.buttonView){
6485 html : this.html || ''
6492 if (this.badge !== '') {
6493 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6499 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6502 a.cls += ' dropdown-toggle treeview' ;
6508 initEvents : function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 if(this.badge !== ''){
6519 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6524 onClick : function(e)
6531 if(this.preventDefault){
6535 this.fireEvent('click', this, e);
6538 disable : function()
6540 this.setDisabled(true);
6545 this.setDisabled(false);
6548 setDisabled : function(state)
6550 if(this.disabled == state){
6554 this.disabled = state;
6557 this.el.addClass('disabled');
6561 this.el.removeClass('disabled');
6566 setActive : function(state)
6568 if(this.active == state){
6572 this.active = state;
6575 this.el.addClass('active');
6579 this.el.removeClass('active');
6584 isActive: function ()
6589 setBadge : function(str)
6595 this.badgeEl.dom.innerHTML = str;
6613 * @class Roo.bootstrap.breadcrumb.Nav
6614 * @extends Roo.bootstrap.Component
6615 * Bootstrap Breadcrumb Nav Class
6617 * @children Roo.bootstrap.breadcrumb.Item
6620 * Create a new breadcrumb.Nav
6621 * @param {Object} config The config object
6624 Roo.bootstrap.breadcrumb.Nav = function(config){
6625 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6630 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6632 getAutoCreate : function()
6649 initEvents: function()
6651 this.olEl = this.el.select('ol',true).first();
6653 getChildContainer : function()
6669 * @class Roo.bootstrap.breadcrumb.Nav
6670 * @extends Roo.bootstrap.Component
6671 * Bootstrap Breadcrumb Nav Class
6673 * @children Roo.bootstrap.breadcrumb.Component
6674 * @cfg {String} html the content of the link.
6675 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6679 * Create a new breadcrumb.Nav
6680 * @param {Object} config The config object
6683 Roo.bootstrap.breadcrumb.Item = function(config){
6684 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6689 * The img click event for the img.
6690 * @param {Roo.EventObject} e
6697 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6702 getAutoCreate : function()
6708 if (this.href !== false) {
6715 cfg.html = this.html;
6721 initEvents: function()
6724 this.el.select('a', true).first().onClick(this.onClick, this)
6728 onClick : function(e)
6731 this.fireEvent('click',this, e);
6744 * @class Roo.bootstrap.Row
6745 * @extends Roo.bootstrap.Component
6746 * Bootstrap Row class (contains columns...)
6750 * @param {Object} config The config object
6753 Roo.bootstrap.Row = function(config){
6754 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6757 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6759 getAutoCreate : function(){
6778 * @class Roo.bootstrap.Pagination
6779 * @extends Roo.bootstrap.Component
6780 * Bootstrap Pagination class
6781 * @cfg {String} size xs | sm | md | lg
6782 * @cfg {Boolean} inverse false | true
6785 * Create a new Pagination
6786 * @param {Object} config The config object
6789 Roo.bootstrap.Pagination = function(config){
6790 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6793 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6799 getAutoCreate : function(){
6805 cfg.cls += ' inverse';
6811 cfg.cls += " " + this.cls;
6829 * @class Roo.bootstrap.PaginationItem
6830 * @extends Roo.bootstrap.Component
6831 * Bootstrap PaginationItem class
6832 * @cfg {String} html text
6833 * @cfg {String} href the link
6834 * @cfg {Boolean} preventDefault (true | false) default true
6835 * @cfg {Boolean} active (true | false) default false
6836 * @cfg {Boolean} disabled default false
6840 * Create a new PaginationItem
6841 * @param {Object} config The config object
6845 Roo.bootstrap.PaginationItem = function(config){
6846 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6851 * The raw click event for the entire grid.
6852 * @param {Roo.EventObject} e
6858 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6862 preventDefault: true,
6867 getAutoCreate : function(){
6873 href : this.href ? this.href : '#',
6874 html : this.html ? this.html : ''
6884 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6888 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6894 initEvents: function() {
6896 this.el.on('click', this.onClick, this);
6899 onClick : function(e)
6901 Roo.log('PaginationItem on click ');
6902 if(this.preventDefault){
6910 this.fireEvent('click', this, e);
6926 * @class Roo.bootstrap.Slider
6927 * @extends Roo.bootstrap.Component
6928 * Bootstrap Slider class
6931 * Create a new Slider
6932 * @param {Object} config The config object
6935 Roo.bootstrap.Slider = function(config){
6936 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6939 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6941 getAutoCreate : function(){
6945 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6949 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6961 * Ext JS Library 1.1.1
6962 * Copyright(c) 2006-2007, Ext JS, LLC.
6964 * Originally Released Under LGPL - original licence link has changed is not relivant.
6967 * <script type="text/javascript">
6972 * @class Roo.grid.ColumnModel
6973 * @extends Roo.util.Observable
6974 * This is the default implementation of a ColumnModel used by the Grid. It defines
6975 * the columns in the grid.
6978 var colModel = new Roo.grid.ColumnModel([
6979 {header: "Ticker", width: 60, sortable: true, locked: true},
6980 {header: "Company Name", width: 150, sortable: true},
6981 {header: "Market Cap.", width: 100, sortable: true},
6982 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6983 {header: "Employees", width: 100, sortable: true, resizable: false}
6988 * The config options listed for this class are options which may appear in each
6989 * individual column definition.
6990 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6992 * @param {Object} config An Array of column config objects. See this class's
6993 * config objects for details.
6995 Roo.grid.ColumnModel = function(config){
6997 * The config passed into the constructor
6999 this.config = config;
7002 // if no id, create one
7003 // if the column does not have a dataIndex mapping,
7004 // map it to the order it is in the config
7005 for(var i = 0, len = config.length; i < len; i++){
7007 if(typeof c.dataIndex == "undefined"){
7010 if(typeof c.renderer == "string"){
7011 c.renderer = Roo.util.Format[c.renderer];
7013 if(typeof c.id == "undefined"){
7016 if(c.editor && c.editor.xtype){
7017 c.editor = Roo.factory(c.editor, Roo.grid);
7019 if(c.editor && c.editor.isFormField){
7020 c.editor = new Roo.grid.GridEditor(c.editor);
7022 this.lookup[c.id] = c;
7026 * The width of columns which have no width specified (defaults to 100)
7029 this.defaultWidth = 100;
7032 * Default sortable of columns which have no sortable specified (defaults to false)
7035 this.defaultSortable = false;
7039 * @event widthchange
7040 * Fires when the width of a column changes.
7041 * @param {ColumnModel} this
7042 * @param {Number} columnIndex The column index
7043 * @param {Number} newWidth The new width
7045 "widthchange": true,
7047 * @event headerchange
7048 * Fires when the text of a header changes.
7049 * @param {ColumnModel} this
7050 * @param {Number} columnIndex The column index
7051 * @param {Number} newText The new header text
7053 "headerchange": true,
7055 * @event hiddenchange
7056 * Fires when a column is hidden or "unhidden".
7057 * @param {ColumnModel} this
7058 * @param {Number} columnIndex The column index
7059 * @param {Boolean} hidden true if hidden, false otherwise
7061 "hiddenchange": true,
7063 * @event columnmoved
7064 * Fires when a column is moved.
7065 * @param {ColumnModel} this
7066 * @param {Number} oldIndex
7067 * @param {Number} newIndex
7069 "columnmoved" : true,
7071 * @event columlockchange
7072 * Fires when a column's locked state is changed
7073 * @param {ColumnModel} this
7074 * @param {Number} colIndex
7075 * @param {Boolean} locked true if locked
7077 "columnlockchange" : true
7079 Roo.grid.ColumnModel.superclass.constructor.call(this);
7081 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7083 * @cfg {String} header The header text to display in the Grid view.
7086 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7087 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7088 * specified, the column's index is used as an index into the Record's data Array.
7091 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7092 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7095 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7096 * Defaults to the value of the {@link #defaultSortable} property.
7097 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7100 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7103 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7106 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7109 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7112 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7113 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7114 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7115 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7118 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7121 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7124 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7127 * @cfg {String} cursor (Optional)
7130 * @cfg {String} tooltip (Optional)
7133 * @cfg {Number} xs (Optional)
7136 * @cfg {Number} sm (Optional)
7139 * @cfg {Number} md (Optional)
7142 * @cfg {Number} lg (Optional)
7145 * Returns the id of the column at the specified index.
7146 * @param {Number} index The column index
7147 * @return {String} the id
7149 getColumnId : function(index){
7150 return this.config[index].id;
7154 * Returns the column for a specified id.
7155 * @param {String} id The column id
7156 * @return {Object} the column
7158 getColumnById : function(id){
7159 return this.lookup[id];
7164 * Returns the column for a specified dataIndex.
7165 * @param {String} dataIndex The column dataIndex
7166 * @return {Object|Boolean} the column or false if not found
7168 getColumnByDataIndex: function(dataIndex){
7169 var index = this.findColumnIndex(dataIndex);
7170 return index > -1 ? this.config[index] : false;
7174 * Returns the index for a specified column id.
7175 * @param {String} id The column id
7176 * @return {Number} the index, or -1 if not found
7178 getIndexById : function(id){
7179 for(var i = 0, len = this.config.length; i < len; i++){
7180 if(this.config[i].id == id){
7188 * Returns the index for a specified column dataIndex.
7189 * @param {String} dataIndex The column dataIndex
7190 * @return {Number} the index, or -1 if not found
7193 findColumnIndex : function(dataIndex){
7194 for(var i = 0, len = this.config.length; i < len; i++){
7195 if(this.config[i].dataIndex == dataIndex){
7203 moveColumn : function(oldIndex, newIndex){
7204 var c = this.config[oldIndex];
7205 this.config.splice(oldIndex, 1);
7206 this.config.splice(newIndex, 0, c);
7207 this.dataMap = null;
7208 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7211 isLocked : function(colIndex){
7212 return this.config[colIndex].locked === true;
7215 setLocked : function(colIndex, value, suppressEvent){
7216 if(this.isLocked(colIndex) == value){
7219 this.config[colIndex].locked = value;
7221 this.fireEvent("columnlockchange", this, colIndex, value);
7225 getTotalLockedWidth : function(){
7227 for(var i = 0; i < this.config.length; i++){
7228 if(this.isLocked(i) && !this.isHidden(i)){
7229 this.totalWidth += this.getColumnWidth(i);
7235 getLockedCount : function(){
7236 for(var i = 0, len = this.config.length; i < len; i++){
7237 if(!this.isLocked(i)){
7242 return this.config.length;
7246 * Returns the number of columns.
7249 getColumnCount : function(visibleOnly){
7250 if(visibleOnly === true){
7252 for(var i = 0, len = this.config.length; i < len; i++){
7253 if(!this.isHidden(i)){
7259 return this.config.length;
7263 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7264 * @param {Function} fn
7265 * @param {Object} scope (optional)
7266 * @return {Array} result
7268 getColumnsBy : function(fn, scope){
7270 for(var i = 0, len = this.config.length; i < len; i++){
7271 var c = this.config[i];
7272 if(fn.call(scope||this, c, i) === true){
7280 * Returns true if the specified column is sortable.
7281 * @param {Number} col The column index
7284 isSortable : function(col){
7285 if(typeof this.config[col].sortable == "undefined"){
7286 return this.defaultSortable;
7288 return this.config[col].sortable;
7292 * Returns the rendering (formatting) function defined for the column.
7293 * @param {Number} col The column index.
7294 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7296 getRenderer : function(col){
7297 if(!this.config[col].renderer){
7298 return Roo.grid.ColumnModel.defaultRenderer;
7300 return this.config[col].renderer;
7304 * Sets the rendering (formatting) function for a column.
7305 * @param {Number} col The column index
7306 * @param {Function} fn The function to use to process the cell's raw data
7307 * to return HTML markup for the grid view. The render function is called with
7308 * the following parameters:<ul>
7309 * <li>Data value.</li>
7310 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7311 * <li>css A CSS style string to apply to the table cell.</li>
7312 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7313 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7314 * <li>Row index</li>
7315 * <li>Column index</li>
7316 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7318 setRenderer : function(col, fn){
7319 this.config[col].renderer = fn;
7323 * Returns the width for the specified column.
7324 * @param {Number} col The column index
7327 getColumnWidth : function(col){
7328 return this.config[col].width * 1 || this.defaultWidth;
7332 * Sets the width for a column.
7333 * @param {Number} col The column index
7334 * @param {Number} width The new width
7336 setColumnWidth : function(col, width, suppressEvent){
7337 this.config[col].width = width;
7338 this.totalWidth = null;
7340 this.fireEvent("widthchange", this, col, width);
7345 * Returns the total width of all columns.
7346 * @param {Boolean} includeHidden True to include hidden column widths
7349 getTotalWidth : function(includeHidden){
7350 if(!this.totalWidth){
7351 this.totalWidth = 0;
7352 for(var i = 0, len = this.config.length; i < len; i++){
7353 if(includeHidden || !this.isHidden(i)){
7354 this.totalWidth += this.getColumnWidth(i);
7358 return this.totalWidth;
7362 * Returns the header for the specified column.
7363 * @param {Number} col The column index
7366 getColumnHeader : function(col){
7367 return this.config[col].header;
7371 * Sets the header for a column.
7372 * @param {Number} col The column index
7373 * @param {String} header The new header
7375 setColumnHeader : function(col, header){
7376 this.config[col].header = header;
7377 this.fireEvent("headerchange", this, col, header);
7381 * Returns the tooltip for the specified column.
7382 * @param {Number} col The column index
7385 getColumnTooltip : function(col){
7386 return this.config[col].tooltip;
7389 * Sets the tooltip for a column.
7390 * @param {Number} col The column index
7391 * @param {String} tooltip The new tooltip
7393 setColumnTooltip : function(col, tooltip){
7394 this.config[col].tooltip = tooltip;
7398 * Returns the dataIndex for the specified column.
7399 * @param {Number} col The column index
7402 getDataIndex : function(col){
7403 return this.config[col].dataIndex;
7407 * Sets the dataIndex for a column.
7408 * @param {Number} col The column index
7409 * @param {Number} dataIndex The new dataIndex
7411 setDataIndex : function(col, dataIndex){
7412 this.config[col].dataIndex = dataIndex;
7418 * Returns true if the cell is editable.
7419 * @param {Number} colIndex The column index
7420 * @param {Number} rowIndex The row index - this is nto actually used..?
7423 isCellEditable : function(colIndex, rowIndex){
7424 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7428 * Returns the editor defined for the cell/column.
7429 * return false or null to disable editing.
7430 * @param {Number} colIndex The column index
7431 * @param {Number} rowIndex The row index
7434 getCellEditor : function(colIndex, rowIndex){
7435 return this.config[colIndex].editor;
7439 * Sets if a column is editable.
7440 * @param {Number} col The column index
7441 * @param {Boolean} editable True if the column is editable
7443 setEditable : function(col, editable){
7444 this.config[col].editable = editable;
7449 * Returns true if the column is hidden.
7450 * @param {Number} colIndex The column index
7453 isHidden : function(colIndex){
7454 return this.config[colIndex].hidden;
7459 * Returns true if the column width cannot be changed
7461 isFixed : function(colIndex){
7462 return this.config[colIndex].fixed;
7466 * Returns true if the column can be resized
7469 isResizable : function(colIndex){
7470 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7473 * Sets if a column is hidden.
7474 * @param {Number} colIndex The column index
7475 * @param {Boolean} hidden True if the column is hidden
7477 setHidden : function(colIndex, hidden){
7478 this.config[colIndex].hidden = hidden;
7479 this.totalWidth = null;
7480 this.fireEvent("hiddenchange", this, colIndex, hidden);
7484 * Sets the editor for a column.
7485 * @param {Number} col The column index
7486 * @param {Object} editor The editor object
7488 setEditor : function(col, editor){
7489 this.config[col].editor = editor;
7493 Roo.grid.ColumnModel.defaultRenderer = function(value)
7495 if(typeof value == "object") {
7498 if(typeof value == "string" && value.length < 1){
7502 return String.format("{0}", value);
7505 // Alias for backwards compatibility
7506 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7509 * Ext JS Library 1.1.1
7510 * Copyright(c) 2006-2007, Ext JS, LLC.
7512 * Originally Released Under LGPL - original licence link has changed is not relivant.
7515 * <script type="text/javascript">
7519 * @class Roo.LoadMask
7520 * A simple utility class for generically masking elements while loading data. If the element being masked has
7521 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7522 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7523 * element's UpdateManager load indicator and will be destroyed after the initial load.
7525 * Create a new LoadMask
7526 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7527 * @param {Object} config The config object
7529 Roo.LoadMask = function(el, config){
7530 this.el = Roo.get(el);
7531 Roo.apply(this, config);
7533 this.store.on('beforeload', this.onBeforeLoad, this);
7534 this.store.on('load', this.onLoad, this);
7535 this.store.on('loadexception', this.onLoadException, this);
7536 this.removeMask = false;
7538 var um = this.el.getUpdateManager();
7539 um.showLoadIndicator = false; // disable the default indicator
7540 um.on('beforeupdate', this.onBeforeLoad, this);
7541 um.on('update', this.onLoad, this);
7542 um.on('failure', this.onLoad, this);
7543 this.removeMask = true;
7547 Roo.LoadMask.prototype = {
7549 * @cfg {Boolean} removeMask
7550 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7551 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7555 * The text to display in a centered loading message box (defaults to 'Loading...')
7559 * @cfg {String} msgCls
7560 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7562 msgCls : 'x-mask-loading',
7565 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7571 * Disables the mask to prevent it from being displayed
7573 disable : function(){
7574 this.disabled = true;
7578 * Enables the mask so that it can be displayed
7580 enable : function(){
7581 this.disabled = false;
7584 onLoadException : function()
7588 if (typeof(arguments[3]) != 'undefined') {
7589 Roo.MessageBox.alert("Error loading",arguments[3]);
7593 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7594 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7601 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7606 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7610 onBeforeLoad : function(){
7612 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7617 destroy : function(){
7619 this.store.un('beforeload', this.onBeforeLoad, this);
7620 this.store.un('load', this.onLoad, this);
7621 this.store.un('loadexception', this.onLoadException, this);
7623 var um = this.el.getUpdateManager();
7624 um.un('beforeupdate', this.onBeforeLoad, this);
7625 um.un('update', this.onLoad, this);
7626 um.un('failure', this.onLoad, this);
7637 * @class Roo.bootstrap.Table
7638 * @extends Roo.bootstrap.Component
7639 * Bootstrap Table class
7640 * @cfg {String} cls table class
7641 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7642 * @cfg {String} bgcolor Specifies the background color for a table
7643 * @cfg {Number} border Specifies whether the table cells should have borders or not
7644 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7645 * @cfg {Number} cellspacing Specifies the space between cells
7646 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7647 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7648 * @cfg {String} sortable Specifies that the table should be sortable
7649 * @cfg {String} summary Specifies a summary of the content of a table
7650 * @cfg {Number} width Specifies the width of a table
7651 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7653 * @cfg {boolean} striped Should the rows be alternative striped
7654 * @cfg {boolean} bordered Add borders to the table
7655 * @cfg {boolean} hover Add hover highlighting
7656 * @cfg {boolean} condensed Format condensed
7657 * @cfg {boolean} responsive Format condensed
7658 * @cfg {Boolean} loadMask (true|false) default false
7659 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7660 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7661 * @cfg {Boolean} rowSelection (true|false) default false
7662 * @cfg {Boolean} cellSelection (true|false) default false
7663 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7664 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7665 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7666 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7670 * Create a new Table
7671 * @param {Object} config The config object
7674 Roo.bootstrap.Table = function(config){
7675 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7680 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7681 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7682 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7683 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7685 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7687 this.sm.grid = this;
7688 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7689 this.sm = this.selModel;
7690 this.sm.xmodule = this.xmodule || false;
7693 if (this.cm && typeof(this.cm.config) == 'undefined') {
7694 this.colModel = new Roo.grid.ColumnModel(this.cm);
7695 this.cm = this.colModel;
7696 this.cm.xmodule = this.xmodule || false;
7699 this.store= Roo.factory(this.store, Roo.data);
7700 this.ds = this.store;
7701 this.ds.xmodule = this.xmodule || false;
7704 if (this.footer && this.store) {
7705 this.footer.dataSource = this.ds;
7706 this.footer = Roo.factory(this.footer);
7713 * Fires when a cell is clicked
7714 * @param {Roo.bootstrap.Table} this
7715 * @param {Roo.Element} el
7716 * @param {Number} rowIndex
7717 * @param {Number} columnIndex
7718 * @param {Roo.EventObject} e
7722 * @event celldblclick
7723 * Fires when a cell is double clicked
7724 * @param {Roo.bootstrap.Table} this
7725 * @param {Roo.Element} el
7726 * @param {Number} rowIndex
7727 * @param {Number} columnIndex
7728 * @param {Roo.EventObject} e
7730 "celldblclick" : true,
7733 * Fires when a row is clicked
7734 * @param {Roo.bootstrap.Table} this
7735 * @param {Roo.Element} el
7736 * @param {Number} rowIndex
7737 * @param {Roo.EventObject} e
7741 * @event rowdblclick
7742 * Fires when a row is double clicked
7743 * @param {Roo.bootstrap.Table} this
7744 * @param {Roo.Element} el
7745 * @param {Number} rowIndex
7746 * @param {Roo.EventObject} e
7748 "rowdblclick" : true,
7751 * Fires when a mouseover occur
7752 * @param {Roo.bootstrap.Table} this
7753 * @param {Roo.Element} el
7754 * @param {Number} rowIndex
7755 * @param {Number} columnIndex
7756 * @param {Roo.EventObject} e
7761 * Fires when a mouseout occur
7762 * @param {Roo.bootstrap.Table} this
7763 * @param {Roo.Element} el
7764 * @param {Number} rowIndex
7765 * @param {Number} columnIndex
7766 * @param {Roo.EventObject} e
7771 * Fires when a row is rendered, so you can change add a style to it.
7772 * @param {Roo.bootstrap.Table} this
7773 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7777 * @event rowsrendered
7778 * Fires when all the rows have been rendered
7779 * @param {Roo.bootstrap.Table} this
7781 'rowsrendered' : true,
7783 * @event contextmenu
7784 * The raw contextmenu event for the entire grid.
7785 * @param {Roo.EventObject} e
7787 "contextmenu" : true,
7789 * @event rowcontextmenu
7790 * Fires when a row is right clicked
7791 * @param {Roo.bootstrap.Table} this
7792 * @param {Number} rowIndex
7793 * @param {Roo.EventObject} e
7795 "rowcontextmenu" : true,
7797 * @event cellcontextmenu
7798 * Fires when a cell is right clicked
7799 * @param {Roo.bootstrap.Table} this
7800 * @param {Number} rowIndex
7801 * @param {Number} cellIndex
7802 * @param {Roo.EventObject} e
7804 "cellcontextmenu" : true,
7806 * @event headercontextmenu
7807 * Fires when a header is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} columnIndex
7810 * @param {Roo.EventObject} e
7812 "headercontextmenu" : true
7816 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7842 rowSelection : false,
7843 cellSelection : false,
7846 // Roo.Element - the tbody
7848 // Roo.Element - thead element
7851 container: false, // used by gridpanel...
7857 auto_hide_footer : false,
7859 getAutoCreate : function()
7861 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7868 if (this.scrollBody) {
7869 cfg.cls += ' table-body-fixed';
7872 cfg.cls += ' table-striped';
7876 cfg.cls += ' table-hover';
7878 if (this.bordered) {
7879 cfg.cls += ' table-bordered';
7881 if (this.condensed) {
7882 cfg.cls += ' table-condensed';
7884 if (this.responsive) {
7885 cfg.cls += ' table-responsive';
7889 cfg.cls+= ' ' +this.cls;
7892 // this lot should be simplifed...
7905 ].forEach(function(k) {
7913 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7916 if(this.store || this.cm){
7917 if(this.headerShow){
7918 cfg.cn.push(this.renderHeader());
7921 cfg.cn.push(this.renderBody());
7923 if(this.footerShow){
7924 cfg.cn.push(this.renderFooter());
7926 // where does this come from?
7927 //cfg.cls+= ' TableGrid';
7930 return { cn : [ cfg ] };
7933 initEvents : function()
7935 if(!this.store || !this.cm){
7938 if (this.selModel) {
7939 this.selModel.initEvents();
7943 //Roo.log('initEvents with ds!!!!');
7945 this.mainBody = this.el.select('tbody', true).first();
7946 this.mainHead = this.el.select('thead', true).first();
7947 this.mainFoot = this.el.select('tfoot', true).first();
7953 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7954 e.on('click', _this.sort, _this);
7957 this.mainBody.on("click", this.onClick, this);
7958 this.mainBody.on("dblclick", this.onDblClick, this);
7960 // why is this done????? = it breaks dialogs??
7961 //this.parent().el.setStyle('position', 'relative');
7965 this.footer.parentId = this.id;
7966 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7969 this.el.select('tfoot tr td').first().addClass('hide');
7974 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7977 this.store.on('load', this.onLoad, this);
7978 this.store.on('beforeload', this.onBeforeLoad, this);
7979 this.store.on('update', this.onUpdate, this);
7980 this.store.on('add', this.onAdd, this);
7981 this.store.on("clear", this.clear, this);
7983 this.el.on("contextmenu", this.onContextMenu, this);
7985 this.mainBody.on('scroll', this.onBodyScroll, this);
7987 this.cm.on("headerchange", this.onHeaderChange, this);
7989 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7993 onContextMenu : function(e, t)
7995 this.processEvent("contextmenu", e);
7998 processEvent : function(name, e)
8000 if (name != 'touchstart' ) {
8001 this.fireEvent(name, e);
8004 var t = e.getTarget();
8006 var cell = Roo.get(t);
8012 if(cell.findParent('tfoot', false, true)){
8016 if(cell.findParent('thead', false, true)){
8018 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8019 cell = Roo.get(t).findParent('th', false, true);
8021 Roo.log("failed to find th in thead?");
8022 Roo.log(e.getTarget());
8027 var cellIndex = cell.dom.cellIndex;
8029 var ename = name == 'touchstart' ? 'click' : name;
8030 this.fireEvent("header" + ename, this, cellIndex, e);
8035 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8036 cell = Roo.get(t).findParent('td', false, true);
8038 Roo.log("failed to find th in tbody?");
8039 Roo.log(e.getTarget());
8044 var row = cell.findParent('tr', false, true);
8045 var cellIndex = cell.dom.cellIndex;
8046 var rowIndex = row.dom.rowIndex - 1;
8050 this.fireEvent("row" + name, this, rowIndex, e);
8054 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8060 onMouseover : function(e, el)
8062 var cell = Roo.get(el);
8068 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8069 cell = cell.findParent('td', false, true);
8072 var row = cell.findParent('tr', false, true);
8073 var cellIndex = cell.dom.cellIndex;
8074 var rowIndex = row.dom.rowIndex - 1; // start from 0
8076 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8080 onMouseout : function(e, el)
8082 var cell = Roo.get(el);
8088 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8089 cell = cell.findParent('td', false, true);
8092 var row = cell.findParent('tr', false, true);
8093 var cellIndex = cell.dom.cellIndex;
8094 var rowIndex = row.dom.rowIndex - 1; // start from 0
8096 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8100 onClick : function(e, el)
8102 var cell = Roo.get(el);
8104 if(!cell || (!this.cellSelection && !this.rowSelection)){
8108 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8109 cell = cell.findParent('td', false, true);
8112 if(!cell || typeof(cell) == 'undefined'){
8116 var row = cell.findParent('tr', false, true);
8118 if(!row || typeof(row) == 'undefined'){
8122 var cellIndex = cell.dom.cellIndex;
8123 var rowIndex = this.getRowIndex(row);
8125 // why??? - should these not be based on SelectionModel?
8126 if(this.cellSelection){
8127 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8130 if(this.rowSelection){
8131 this.fireEvent('rowclick', this, row, rowIndex, e);
8137 onDblClick : function(e,el)
8139 var cell = Roo.get(el);
8141 if(!cell || (!this.cellSelection && !this.rowSelection)){
8145 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8146 cell = cell.findParent('td', false, true);
8149 if(!cell || typeof(cell) == 'undefined'){
8153 var row = cell.findParent('tr', false, true);
8155 if(!row || typeof(row) == 'undefined'){
8159 var cellIndex = cell.dom.cellIndex;
8160 var rowIndex = this.getRowIndex(row);
8162 if(this.cellSelection){
8163 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8166 if(this.rowSelection){
8167 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8171 sort : function(e,el)
8173 var col = Roo.get(el);
8175 if(!col.hasClass('sortable')){
8179 var sort = col.attr('sort');
8182 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8186 this.store.sortInfo = {field : sort, direction : dir};
8189 Roo.log("calling footer first");
8190 this.footer.onClick('first');
8193 this.store.load({ params : { start : 0 } });
8197 renderHeader : function()
8205 this.totalWidth = 0;
8207 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8209 var config = cm.config[i];
8213 cls : 'x-hcol-' + i,
8215 html: cm.getColumnHeader(i)
8220 if(typeof(config.sortable) != 'undefined' && config.sortable){
8222 c.html = '<i class="glyphicon"></i>' + c.html;
8225 // could use BS4 hidden-..-down
8227 if(typeof(config.lgHeader) != 'undefined'){
8228 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8231 if(typeof(config.mdHeader) != 'undefined'){
8232 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8235 if(typeof(config.smHeader) != 'undefined'){
8236 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8239 if(typeof(config.xsHeader) != 'undefined'){
8240 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8247 if(typeof(config.tooltip) != 'undefined'){
8248 c.tooltip = config.tooltip;
8251 if(typeof(config.colspan) != 'undefined'){
8252 c.colspan = config.colspan;
8255 if(typeof(config.hidden) != 'undefined' && config.hidden){
8256 c.style += ' display:none;';
8259 if(typeof(config.dataIndex) != 'undefined'){
8260 c.sort = config.dataIndex;
8265 if(typeof(config.align) != 'undefined' && config.align.length){
8266 c.style += ' text-align:' + config.align + ';';
8269 if(typeof(config.width) != 'undefined'){
8270 c.style += ' width:' + config.width + 'px;';
8271 this.totalWidth += config.width;
8273 this.totalWidth += 100; // assume minimum of 100 per column?
8276 if(typeof(config.cls) != 'undefined'){
8277 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8280 ['xs','sm','md','lg'].map(function(size){
8282 if(typeof(config[size]) == 'undefined'){
8286 if (!config[size]) { // 0 = hidden
8287 // BS 4 '0' is treated as hide that column and below.
8288 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8292 c.cls += ' col-' + size + '-' + config[size] + (
8293 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8305 renderBody : function()
8315 colspan : this.cm.getColumnCount()
8325 renderFooter : function()
8335 colspan : this.cm.getColumnCount()
8349 // Roo.log('ds onload');
8354 var ds = this.store;
8356 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8357 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8358 if (_this.store.sortInfo) {
8360 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8361 e.select('i', true).addClass(['glyphicon-arrow-up']);
8364 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8365 e.select('i', true).addClass(['glyphicon-arrow-down']);
8370 var tbody = this.mainBody;
8372 if(ds.getCount() > 0){
8373 ds.data.each(function(d,rowIndex){
8374 var row = this.renderRow(cm, ds, rowIndex);
8376 tbody.createChild(row);
8380 if(row.cellObjects.length){
8381 Roo.each(row.cellObjects, function(r){
8382 _this.renderCellObject(r);
8389 var tfoot = this.el.select('tfoot', true).first();
8391 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8393 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8395 var total = this.ds.getTotalCount();
8397 if(this.footer.pageSize < total){
8398 this.mainFoot.show();
8402 Roo.each(this.el.select('tbody td', true).elements, function(e){
8403 e.on('mouseover', _this.onMouseover, _this);
8406 Roo.each(this.el.select('tbody td', true).elements, function(e){
8407 e.on('mouseout', _this.onMouseout, _this);
8409 this.fireEvent('rowsrendered', this);
8415 onUpdate : function(ds,record)
8417 this.refreshRow(record);
8421 onRemove : function(ds, record, index, isUpdate){
8422 if(isUpdate !== true){
8423 this.fireEvent("beforerowremoved", this, index, record);
8425 var bt = this.mainBody.dom;
8427 var rows = this.el.select('tbody > tr', true).elements;
8429 if(typeof(rows[index]) != 'undefined'){
8430 bt.removeChild(rows[index].dom);
8433 // if(bt.rows[index]){
8434 // bt.removeChild(bt.rows[index]);
8437 if(isUpdate !== true){
8438 //this.stripeRows(index);
8439 //this.syncRowHeights(index, index);
8441 this.fireEvent("rowremoved", this, index, record);
8445 onAdd : function(ds, records, rowIndex)
8447 //Roo.log('on Add called');
8448 // - note this does not handle multiple adding very well..
8449 var bt = this.mainBody.dom;
8450 for (var i =0 ; i < records.length;i++) {
8451 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8452 //Roo.log(records[i]);
8453 //Roo.log(this.store.getAt(rowIndex+i));
8454 this.insertRow(this.store, rowIndex + i, false);
8461 refreshRow : function(record){
8462 var ds = this.store, index;
8463 if(typeof record == 'number'){
8465 record = ds.getAt(index);
8467 index = ds.indexOf(record);
8469 return; // should not happen - but seems to
8472 this.insertRow(ds, index, true);
8474 this.onRemove(ds, record, index+1, true);
8476 //this.syncRowHeights(index, index);
8478 this.fireEvent("rowupdated", this, index, record);
8481 insertRow : function(dm, rowIndex, isUpdate){
8484 this.fireEvent("beforerowsinserted", this, rowIndex);
8486 //var s = this.getScrollState();
8487 var row = this.renderRow(this.cm, this.store, rowIndex);
8488 // insert before rowIndex..
8489 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8493 if(row.cellObjects.length){
8494 Roo.each(row.cellObjects, function(r){
8495 _this.renderCellObject(r);
8500 this.fireEvent("rowsinserted", this, rowIndex);
8501 //this.syncRowHeights(firstRow, lastRow);
8502 //this.stripeRows(firstRow);
8509 getRowDom : function(rowIndex)
8511 var rows = this.el.select('tbody > tr', true).elements;
8513 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8516 // returns the object tree for a tr..
8519 renderRow : function(cm, ds, rowIndex)
8521 var d = ds.getAt(rowIndex);
8525 cls : 'x-row-' + rowIndex,
8529 var cellObjects = [];
8531 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8532 var config = cm.config[i];
8534 var renderer = cm.getRenderer(i);
8538 if(typeof(renderer) !== 'undefined'){
8539 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8541 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8542 // and are rendered into the cells after the row is rendered - using the id for the element.
8544 if(typeof(value) === 'object'){
8554 rowIndex : rowIndex,
8559 this.fireEvent('rowclass', this, rowcfg);
8563 cls : rowcfg.rowClass + ' x-col-' + i,
8565 html: (typeof(value) === 'object') ? '' : value
8572 if(typeof(config.colspan) != 'undefined'){
8573 td.colspan = config.colspan;
8576 if(typeof(config.hidden) != 'undefined' && config.hidden){
8577 td.style += ' display:none;';
8580 if(typeof(config.align) != 'undefined' && config.align.length){
8581 td.style += ' text-align:' + config.align + ';';
8583 if(typeof(config.valign) != 'undefined' && config.valign.length){
8584 td.style += ' vertical-align:' + config.valign + ';';
8587 if(typeof(config.width) != 'undefined'){
8588 td.style += ' width:' + config.width + 'px;';
8591 if(typeof(config.cursor) != 'undefined'){
8592 td.style += ' cursor:' + config.cursor + ';';
8595 if(typeof(config.cls) != 'undefined'){
8596 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8599 ['xs','sm','md','lg'].map(function(size){
8601 if(typeof(config[size]) == 'undefined'){
8607 if (!config[size]) { // 0 = hidden
8608 // BS 4 '0' is treated as hide that column and below.
8609 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8613 td.cls += ' col-' + size + '-' + config[size] + (
8614 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8624 row.cellObjects = cellObjects;
8632 onBeforeLoad : function()
8641 this.el.select('tbody', true).first().dom.innerHTML = '';
8644 * Show or hide a row.
8645 * @param {Number} rowIndex to show or hide
8646 * @param {Boolean} state hide
8648 setRowVisibility : function(rowIndex, state)
8650 var bt = this.mainBody.dom;
8652 var rows = this.el.select('tbody > tr', true).elements;
8654 if(typeof(rows[rowIndex]) == 'undefined'){
8657 rows[rowIndex].dom.style.display = state ? '' : 'none';
8661 getSelectionModel : function(){
8663 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8665 return this.selModel;
8668 * Render the Roo.bootstrap object from renderder
8670 renderCellObject : function(r)
8674 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8676 var t = r.cfg.render(r.container);
8679 Roo.each(r.cfg.cn, function(c){
8681 container: t.getChildContainer(),
8684 _this.renderCellObject(child);
8689 getRowIndex : function(row)
8693 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8704 * Returns the grid's underlying element = used by panel.Grid
8705 * @return {Element} The element
8707 getGridEl : function(){
8711 * Forces a resize - used by panel.Grid
8712 * @return {Element} The element
8714 autoSize : function()
8716 //var ctr = Roo.get(this.container.dom.parentElement);
8717 var ctr = Roo.get(this.el.dom);
8719 var thd = this.getGridEl().select('thead',true).first();
8720 var tbd = this.getGridEl().select('tbody', true).first();
8721 var tfd = this.getGridEl().select('tfoot', true).first();
8723 var cw = ctr.getWidth();
8724 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8728 tbd.setWidth(ctr.getWidth());
8729 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8730 // this needs fixing for various usage - currently only hydra job advers I think..
8732 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8734 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8737 cw = Math.max(cw, this.totalWidth);
8738 this.getGridEl().select('tbody tr',true).setWidth(cw);
8740 // resize 'expandable coloumn?
8742 return; // we doe not have a view in this design..
8745 onBodyScroll: function()
8747 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8749 this.mainHead.setStyle({
8750 'position' : 'relative',
8751 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8757 var scrollHeight = this.mainBody.dom.scrollHeight;
8759 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8761 var height = this.mainBody.getHeight();
8763 if(scrollHeight - height == scrollTop) {
8765 var total = this.ds.getTotalCount();
8767 if(this.footer.cursor + this.footer.pageSize < total){
8769 this.footer.ds.load({
8771 start : this.footer.cursor + this.footer.pageSize,
8772 limit : this.footer.pageSize
8782 onHeaderChange : function()
8784 var header = this.renderHeader();
8785 var table = this.el.select('table', true).first();
8787 this.mainHead.remove();
8788 this.mainHead = table.createChild(header, this.mainBody, false);
8791 onHiddenChange : function(colModel, colIndex, hidden)
8793 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8794 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8796 this.CSS.updateRule(thSelector, "display", "");
8797 this.CSS.updateRule(tdSelector, "display", "");
8800 this.CSS.updateRule(thSelector, "display", "none");
8801 this.CSS.updateRule(tdSelector, "display", "none");
8804 this.onHeaderChange();
8808 setColumnWidth: function(col_index, width)
8810 // width = "md-2 xs-2..."
8811 if(!this.colModel.config[col_index]) {
8815 var w = width.split(" ");
8817 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8819 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8822 for(var j = 0; j < w.length; j++) {
8828 var size_cls = w[j].split("-");
8830 if(!Number.isInteger(size_cls[1] * 1)) {
8834 if(!this.colModel.config[col_index][size_cls[0]]) {
8838 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8842 h_row[0].classList.replace(
8843 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8844 "col-"+size_cls[0]+"-"+size_cls[1]
8847 for(var i = 0; i < rows.length; i++) {
8849 var size_cls = w[j].split("-");
8851 if(!Number.isInteger(size_cls[1] * 1)) {
8855 if(!this.colModel.config[col_index][size_cls[0]]) {
8859 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8863 rows[i].classList.replace(
8864 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8865 "col-"+size_cls[0]+"-"+size_cls[1]
8869 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884 * @class Roo.bootstrap.TableCell
8885 * @extends Roo.bootstrap.Component
8886 * Bootstrap TableCell class
8887 * @cfg {String} html cell contain text
8888 * @cfg {String} cls cell class
8889 * @cfg {String} tag cell tag (td|th) default td
8890 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8891 * @cfg {String} align Aligns the content in a cell
8892 * @cfg {String} axis Categorizes cells
8893 * @cfg {String} bgcolor Specifies the background color of a cell
8894 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8895 * @cfg {Number} colspan Specifies the number of columns a cell should span
8896 * @cfg {String} headers Specifies one or more header cells a cell is related to
8897 * @cfg {Number} height Sets the height of a cell
8898 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8899 * @cfg {Number} rowspan Sets the number of rows a cell should span
8900 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8901 * @cfg {String} valign Vertical aligns the content in a cell
8902 * @cfg {Number} width Specifies the width of a cell
8905 * Create a new TableCell
8906 * @param {Object} config The config object
8909 Roo.bootstrap.TableCell = function(config){
8910 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8913 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8933 getAutoCreate : function(){
8934 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8954 cfg.align=this.align
8960 cfg.bgcolor=this.bgcolor
8963 cfg.charoff=this.charoff
8966 cfg.colspan=this.colspan
8969 cfg.headers=this.headers
8972 cfg.height=this.height
8975 cfg.nowrap=this.nowrap
8978 cfg.rowspan=this.rowspan
8981 cfg.scope=this.scope
8984 cfg.valign=this.valign
8987 cfg.width=this.width
9006 * @class Roo.bootstrap.TableRow
9007 * @extends Roo.bootstrap.Component
9008 * Bootstrap TableRow class
9009 * @cfg {String} cls row class
9010 * @cfg {String} align Aligns the content in a table row
9011 * @cfg {String} bgcolor Specifies a background color for a table row
9012 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9013 * @cfg {String} valign Vertical aligns the content in a table row
9016 * Create a new TableRow
9017 * @param {Object} config The config object
9020 Roo.bootstrap.TableRow = function(config){
9021 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9024 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9032 getAutoCreate : function(){
9033 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043 cfg.align = this.align;
9046 cfg.bgcolor = this.bgcolor;
9049 cfg.charoff = this.charoff;
9052 cfg.valign = this.valign;
9070 * @class Roo.bootstrap.TableBody
9071 * @extends Roo.bootstrap.Component
9072 * Bootstrap TableBody class
9073 * @cfg {String} cls element class
9074 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9075 * @cfg {String} align Aligns the content inside the element
9076 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9077 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9080 * Create a new TableBody
9081 * @param {Object} config The config object
9084 Roo.bootstrap.TableBody = function(config){
9085 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9088 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9096 getAutoCreate : function(){
9097 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9111 cfg.align = this.align;
9114 cfg.charoff = this.charoff;
9117 cfg.valign = this.valign;
9124 // initEvents : function()
9131 // this.store = Roo.factory(this.store, Roo.data);
9132 // this.store.on('load', this.onLoad, this);
9134 // this.store.load();
9138 // onLoad: function ()
9140 // this.fireEvent('load', this);
9150 * Ext JS Library 1.1.1
9151 * Copyright(c) 2006-2007, Ext JS, LLC.
9153 * Originally Released Under LGPL - original licence link has changed is not relivant.
9156 * <script type="text/javascript">
9159 // as we use this in bootstrap.
9160 Roo.namespace('Roo.form');
9162 * @class Roo.form.Action
9163 * Internal Class used to handle form actions
9165 * @param {Roo.form.BasicForm} el The form element or its id
9166 * @param {Object} config Configuration options
9171 // define the action interface
9172 Roo.form.Action = function(form, options){
9174 this.options = options || {};
9177 * Client Validation Failed
9180 Roo.form.Action.CLIENT_INVALID = 'client';
9182 * Server Validation Failed
9185 Roo.form.Action.SERVER_INVALID = 'server';
9187 * Connect to Server Failed
9190 Roo.form.Action.CONNECT_FAILURE = 'connect';
9192 * Reading Data from Server Failed
9195 Roo.form.Action.LOAD_FAILURE = 'load';
9197 Roo.form.Action.prototype = {
9199 failureType : undefined,
9200 response : undefined,
9204 run : function(options){
9209 success : function(response){
9214 handleResponse : function(response){
9218 // default connection failure
9219 failure : function(response){
9221 this.response = response;
9222 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9223 this.form.afterAction(this, false);
9226 processResponse : function(response){
9227 this.response = response;
9228 if(!response.responseText){
9231 this.result = this.handleResponse(response);
9235 // utility functions used internally
9236 getUrl : function(appendParams){
9237 var url = this.options.url || this.form.url || this.form.el.dom.action;
9239 var p = this.getParams();
9241 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9247 getMethod : function(){
9248 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9251 getParams : function(){
9252 var bp = this.form.baseParams;
9253 var p = this.options.params;
9255 if(typeof p == "object"){
9256 p = Roo.urlEncode(Roo.applyIf(p, bp));
9257 }else if(typeof p == 'string' && bp){
9258 p += '&' + Roo.urlEncode(bp);
9261 p = Roo.urlEncode(bp);
9266 createCallback : function(){
9268 success: this.success,
9269 failure: this.failure,
9271 timeout: (this.form.timeout*1000),
9272 upload: this.form.fileUpload ? this.success : undefined
9277 Roo.form.Action.Submit = function(form, options){
9278 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9281 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9284 haveProgress : false,
9285 uploadComplete : false,
9287 // uploadProgress indicator.
9288 uploadProgress : function()
9290 if (!this.form.progressUrl) {
9294 if (!this.haveProgress) {
9295 Roo.MessageBox.progress("Uploading", "Uploading");
9297 if (this.uploadComplete) {
9298 Roo.MessageBox.hide();
9302 this.haveProgress = true;
9304 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9306 var c = new Roo.data.Connection();
9308 url : this.form.progressUrl,
9313 success : function(req){
9314 //console.log(data);
9318 rdata = Roo.decode(req.responseText)
9320 Roo.log("Invalid data from server..");
9324 if (!rdata || !rdata.success) {
9326 Roo.MessageBox.alert(Roo.encode(rdata));
9329 var data = rdata.data;
9331 if (this.uploadComplete) {
9332 Roo.MessageBox.hide();
9337 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9338 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9341 this.uploadProgress.defer(2000,this);
9344 failure: function(data) {
9345 Roo.log('progress url failed ');
9356 // run get Values on the form, so it syncs any secondary forms.
9357 this.form.getValues();
9359 var o = this.options;
9360 var method = this.getMethod();
9361 var isPost = method == 'POST';
9362 if(o.clientValidation === false || this.form.isValid()){
9364 if (this.form.progressUrl) {
9365 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9366 (new Date() * 1) + '' + Math.random());
9371 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9372 form:this.form.el.dom,
9373 url:this.getUrl(!isPost),
9375 params:isPost ? this.getParams() : null,
9376 isUpload: this.form.fileUpload,
9377 formData : this.form.formData
9380 this.uploadProgress();
9382 }else if (o.clientValidation !== false){ // client validation failed
9383 this.failureType = Roo.form.Action.CLIENT_INVALID;
9384 this.form.afterAction(this, false);
9388 success : function(response)
9390 this.uploadComplete= true;
9391 if (this.haveProgress) {
9392 Roo.MessageBox.hide();
9396 var result = this.processResponse(response);
9397 if(result === true || result.success){
9398 this.form.afterAction(this, true);
9402 this.form.markInvalid(result.errors);
9403 this.failureType = Roo.form.Action.SERVER_INVALID;
9405 this.form.afterAction(this, false);
9407 failure : function(response)
9409 this.uploadComplete= true;
9410 if (this.haveProgress) {
9411 Roo.MessageBox.hide();
9414 this.response = response;
9415 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9416 this.form.afterAction(this, false);
9419 handleResponse : function(response){
9420 if(this.form.errorReader){
9421 var rs = this.form.errorReader.read(response);
9424 for(var i = 0, len = rs.records.length; i < len; i++) {
9425 var r = rs.records[i];
9429 if(errors.length < 1){
9433 success : rs.success,
9439 ret = Roo.decode(response.responseText);
9443 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453 Roo.form.Action.Load = function(form, options){
9454 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9455 this.reader = this.form.reader;
9458 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9463 Roo.Ajax.request(Roo.apply(
9464 this.createCallback(), {
9465 method:this.getMethod(),
9466 url:this.getUrl(false),
9467 params:this.getParams()
9471 success : function(response){
9473 var result = this.processResponse(response);
9474 if(result === true || !result.success || !result.data){
9475 this.failureType = Roo.form.Action.LOAD_FAILURE;
9476 this.form.afterAction(this, false);
9479 this.form.clearInvalid();
9480 this.form.setValues(result.data);
9481 this.form.afterAction(this, true);
9484 handleResponse : function(response){
9485 if(this.form.reader){
9486 var rs = this.form.reader.read(response);
9487 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9489 success : rs.success,
9493 return Roo.decode(response.responseText);
9497 Roo.form.Action.ACTION_TYPES = {
9498 'load' : Roo.form.Action.Load,
9499 'submit' : Roo.form.Action.Submit
9508 * @class Roo.bootstrap.Form
9509 * @extends Roo.bootstrap.Component
9510 * Bootstrap Form class
9511 * @cfg {String} method GET | POST (default POST)
9512 * @cfg {String} labelAlign top | left (default top)
9513 * @cfg {String} align left | right - for navbars
9514 * @cfg {Boolean} loadMask load mask when submit (default true)
9519 * @param {Object} config The config object
9523 Roo.bootstrap.Form = function(config){
9525 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9527 Roo.bootstrap.Form.popover.apply();
9531 * @event clientvalidation
9532 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9533 * @param {Form} this
9534 * @param {Boolean} valid true if the form has passed client-side validation
9536 clientvalidation: true,
9538 * @event beforeaction
9539 * Fires before any action is performed. Return false to cancel the action.
9540 * @param {Form} this
9541 * @param {Action} action The action to be performed
9545 * @event actionfailed
9546 * Fires when an action fails.
9547 * @param {Form} this
9548 * @param {Action} action The action that failed
9550 actionfailed : true,
9552 * @event actioncomplete
9553 * Fires when an action is completed.
9554 * @param {Form} this
9555 * @param {Action} action The action that completed
9557 actioncomplete : true
9561 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9564 * @cfg {String} method
9565 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9570 * The URL to use for form actions if one isn't supplied in the action options.
9573 * @cfg {Boolean} fileUpload
9574 * Set to true if this form is a file upload.
9578 * @cfg {Object} baseParams
9579 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9583 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9587 * @cfg {Sting} align (left|right) for navbar forms
9592 activeAction : null,
9595 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9596 * element by passing it or its id or mask the form itself by passing in true.
9599 waitMsgTarget : false,
9604 * @cfg {Boolean} errorMask (true|false) default false
9609 * @cfg {Number} maskOffset Default 100
9614 * @cfg {Boolean} maskBody
9618 getAutoCreate : function(){
9622 method : this.method || 'POST',
9623 id : this.id || Roo.id(),
9626 if (this.parent().xtype.match(/^Nav/)) {
9627 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9631 if (this.labelAlign == 'left' ) {
9632 cfg.cls += ' form-horizontal';
9638 initEvents : function()
9640 this.el.on('submit', this.onSubmit, this);
9641 // this was added as random key presses on the form where triggering form submit.
9642 this.el.on('keypress', function(e) {
9643 if (e.getCharCode() != 13) {
9646 // we might need to allow it for textareas.. and some other items.
9647 // check e.getTarget().
9649 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9653 Roo.log("keypress blocked");
9661 onSubmit : function(e){
9666 * Returns true if client-side validation on the form is successful.
9669 isValid : function(){
9670 var items = this.getItems();
9674 items.each(function(f){
9680 Roo.log('invalid field: ' + f.name);
9684 if(!target && f.el.isVisible(true)){
9690 if(this.errorMask && !valid){
9691 Roo.bootstrap.Form.popover.mask(this, target);
9698 * Returns true if any fields in this form have changed since their original load.
9701 isDirty : function(){
9703 var items = this.getItems();
9704 items.each(function(f){
9714 * Performs a predefined action (submit or load) or custom actions you define on this form.
9715 * @param {String} actionName The name of the action type
9716 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9717 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9718 * accept other config options):
9720 Property Type Description
9721 ---------------- --------------- ----------------------------------------------------------------------------------
9722 url String The url for the action (defaults to the form's url)
9723 method String The form method to use (defaults to the form's method, or POST if not defined)
9724 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9725 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9726 validate the form on the client (defaults to false)
9728 * @return {BasicForm} this
9730 doAction : function(action, options){
9731 if(typeof action == 'string'){
9732 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9734 if(this.fireEvent('beforeaction', this, action) !== false){
9735 this.beforeAction(action);
9736 action.run.defer(100, action);
9742 beforeAction : function(action){
9743 var o = action.options;
9748 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9750 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9753 // not really supported yet.. ??
9755 //if(this.waitMsgTarget === true){
9756 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9757 //}else if(this.waitMsgTarget){
9758 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9759 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9761 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9767 afterAction : function(action, success){
9768 this.activeAction = null;
9769 var o = action.options;
9774 Roo.get(document.body).unmask();
9780 //if(this.waitMsgTarget === true){
9781 // this.el.unmask();
9782 //}else if(this.waitMsgTarget){
9783 // this.waitMsgTarget.unmask();
9785 // Roo.MessageBox.updateProgress(1);
9786 // Roo.MessageBox.hide();
9793 Roo.callback(o.success, o.scope, [this, action]);
9794 this.fireEvent('actioncomplete', this, action);
9798 // failure condition..
9799 // we have a scenario where updates need confirming.
9800 // eg. if a locking scenario exists..
9801 // we look for { errors : { needs_confirm : true }} in the response.
9803 (typeof(action.result) != 'undefined') &&
9804 (typeof(action.result.errors) != 'undefined') &&
9805 (typeof(action.result.errors.needs_confirm) != 'undefined')
9808 Roo.log("not supported yet");
9811 Roo.MessageBox.confirm(
9812 "Change requires confirmation",
9813 action.result.errorMsg,
9818 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9828 Roo.callback(o.failure, o.scope, [this, action]);
9829 // show an error message if no failed handler is set..
9830 if (!this.hasListener('actionfailed')) {
9831 Roo.log("need to add dialog support");
9833 Roo.MessageBox.alert("Error",
9834 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9835 action.result.errorMsg :
9836 "Saving Failed, please check your entries or try again"
9841 this.fireEvent('actionfailed', this, action);
9846 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9847 * @param {String} id The value to search for
9850 findField : function(id){
9851 var items = this.getItems();
9852 var field = items.get(id);
9854 items.each(function(f){
9855 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9862 return field || null;
9865 * Mark fields in this form invalid in bulk.
9866 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9867 * @return {BasicForm} this
9869 markInvalid : function(errors){
9870 if(errors instanceof Array){
9871 for(var i = 0, len = errors.length; i < len; i++){
9872 var fieldError = errors[i];
9873 var f = this.findField(fieldError.id);
9875 f.markInvalid(fieldError.msg);
9881 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9882 field.markInvalid(errors[id]);
9886 //Roo.each(this.childForms || [], function (f) {
9887 // f.markInvalid(errors);
9894 * Set values for fields in this form in bulk.
9895 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9896 * @return {BasicForm} this
9898 setValues : function(values){
9899 if(values instanceof Array){ // array of objects
9900 for(var i = 0, len = values.length; i < len; i++){
9902 var f = this.findField(v.id);
9904 f.setValue(v.value);
9905 if(this.trackResetOnLoad){
9906 f.originalValue = f.getValue();
9910 }else{ // object hash
9913 if(typeof values[id] != 'function' && (field = this.findField(id))){
9915 if (field.setFromData &&
9917 field.displayField &&
9918 // combos' with local stores can
9919 // be queried via setValue()
9920 // to set their value..
9921 (field.store && !field.store.isLocal)
9925 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9926 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9927 field.setFromData(sd);
9929 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9931 field.setFromData(values);
9934 field.setValue(values[id]);
9938 if(this.trackResetOnLoad){
9939 field.originalValue = field.getValue();
9945 //Roo.each(this.childForms || [], function (f) {
9946 // f.setValues(values);
9953 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9954 * they are returned as an array.
9955 * @param {Boolean} asString
9958 getValues : function(asString){
9959 //if (this.childForms) {
9960 // copy values from the child forms
9961 // Roo.each(this.childForms, function (f) {
9962 // this.setValues(f.getValues());
9968 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9969 if(asString === true){
9972 return Roo.urlDecode(fs);
9976 * Returns the fields in this form as an object with key/value pairs.
9977 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9980 getFieldValues : function(with_hidden)
9982 var items = this.getItems();
9984 items.each(function(f){
9990 var v = f.getValue();
9992 if (f.inputType =='radio') {
9993 if (typeof(ret[f.getName()]) == 'undefined') {
9994 ret[f.getName()] = ''; // empty..
9997 if (!f.el.dom.checked) {
10001 v = f.el.dom.value;
10005 if(f.xtype == 'MoneyField'){
10006 ret[f.currencyName] = f.getCurrency();
10009 // not sure if this supported any more..
10010 if ((typeof(v) == 'object') && f.getRawValue) {
10011 v = f.getRawValue() ; // dates..
10013 // combo boxes where name != hiddenName...
10014 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10015 ret[f.name] = f.getRawValue();
10017 ret[f.getName()] = v;
10024 * Clears all invalid messages in this form.
10025 * @return {BasicForm} this
10027 clearInvalid : function(){
10028 var items = this.getItems();
10030 items.each(function(f){
10038 * Resets this form.
10039 * @return {BasicForm} this
10041 reset : function(){
10042 var items = this.getItems();
10043 items.each(function(f){
10047 Roo.each(this.childForms || [], function (f) {
10055 getItems : function()
10057 var r=new Roo.util.MixedCollection(false, function(o){
10058 return o.id || (o.id = Roo.id());
10060 var iter = function(el) {
10067 Roo.each(el.items,function(e) {
10076 hideFields : function(items)
10078 Roo.each(items, function(i){
10080 var f = this.findField(i);
10091 showFields : function(items)
10093 Roo.each(items, function(i){
10095 var f = this.findField(i);
10108 Roo.apply(Roo.bootstrap.Form, {
10124 intervalID : false,
10130 if(this.isApplied){
10135 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10136 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10137 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10138 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10141 this.maskEl.top.enableDisplayMode("block");
10142 this.maskEl.left.enableDisplayMode("block");
10143 this.maskEl.bottom.enableDisplayMode("block");
10144 this.maskEl.right.enableDisplayMode("block");
10146 this.toolTip = new Roo.bootstrap.Tooltip({
10147 cls : 'roo-form-error-popover',
10149 'left' : ['r-l', [-2,0], 'right'],
10150 'right' : ['l-r', [2,0], 'left'],
10151 'bottom' : ['tl-bl', [0,2], 'top'],
10152 'top' : [ 'bl-tl', [0,-2], 'bottom']
10156 this.toolTip.render(Roo.get(document.body));
10158 this.toolTip.el.enableDisplayMode("block");
10160 Roo.get(document.body).on('click', function(){
10164 Roo.get(document.body).on('touchstart', function(){
10168 this.isApplied = true
10171 mask : function(form, target)
10175 this.target = target;
10177 if(!this.form.errorMask || !target.el){
10181 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10183 Roo.log(scrollable);
10185 var ot = this.target.el.calcOffsetsTo(scrollable);
10187 var scrollTo = ot[1] - this.form.maskOffset;
10189 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10191 scrollable.scrollTo('top', scrollTo);
10193 var box = this.target.el.getBox();
10195 var zIndex = Roo.bootstrap.Modal.zIndex++;
10198 this.maskEl.top.setStyle('position', 'absolute');
10199 this.maskEl.top.setStyle('z-index', zIndex);
10200 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10201 this.maskEl.top.setLeft(0);
10202 this.maskEl.top.setTop(0);
10203 this.maskEl.top.show();
10205 this.maskEl.left.setStyle('position', 'absolute');
10206 this.maskEl.left.setStyle('z-index', zIndex);
10207 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10208 this.maskEl.left.setLeft(0);
10209 this.maskEl.left.setTop(box.y - this.padding);
10210 this.maskEl.left.show();
10212 this.maskEl.bottom.setStyle('position', 'absolute');
10213 this.maskEl.bottom.setStyle('z-index', zIndex);
10214 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10215 this.maskEl.bottom.setLeft(0);
10216 this.maskEl.bottom.setTop(box.bottom + this.padding);
10217 this.maskEl.bottom.show();
10219 this.maskEl.right.setStyle('position', 'absolute');
10220 this.maskEl.right.setStyle('z-index', zIndex);
10221 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10222 this.maskEl.right.setLeft(box.right + this.padding);
10223 this.maskEl.right.setTop(box.y - this.padding);
10224 this.maskEl.right.show();
10226 this.toolTip.bindEl = this.target.el;
10228 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10230 var tip = this.target.blankText;
10232 if(this.target.getValue() !== '' ) {
10234 if (this.target.invalidText.length) {
10235 tip = this.target.invalidText;
10236 } else if (this.target.regexText.length){
10237 tip = this.target.regexText;
10241 this.toolTip.show(tip);
10243 this.intervalID = window.setInterval(function() {
10244 Roo.bootstrap.Form.popover.unmask();
10247 window.onwheel = function(){ return false;};
10249 (function(){ this.isMasked = true; }).defer(500, this);
10253 unmask : function()
10255 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10259 this.maskEl.top.setStyle('position', 'absolute');
10260 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10261 this.maskEl.top.hide();
10263 this.maskEl.left.setStyle('position', 'absolute');
10264 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10265 this.maskEl.left.hide();
10267 this.maskEl.bottom.setStyle('position', 'absolute');
10268 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10269 this.maskEl.bottom.hide();
10271 this.maskEl.right.setStyle('position', 'absolute');
10272 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10273 this.maskEl.right.hide();
10275 this.toolTip.hide();
10277 this.toolTip.el.hide();
10279 window.onwheel = function(){ return true;};
10281 if(this.intervalID){
10282 window.clearInterval(this.intervalID);
10283 this.intervalID = false;
10286 this.isMasked = false;
10296 * Ext JS Library 1.1.1
10297 * Copyright(c) 2006-2007, Ext JS, LLC.
10299 * Originally Released Under LGPL - original licence link has changed is not relivant.
10302 * <script type="text/javascript">
10305 * @class Roo.form.VTypes
10306 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10309 Roo.form.VTypes = function(){
10310 // closure these in so they are only created once.
10311 var alpha = /^[a-zA-Z_]+$/;
10312 var alphanum = /^[a-zA-Z0-9_]+$/;
10313 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10314 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10316 // All these messages and functions are configurable
10319 * The function used to validate email addresses
10320 * @param {String} value The email address
10322 'email' : function(v){
10323 return email.test(v);
10326 * The error text to display when the email validation function returns false
10329 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10331 * The keystroke filter mask to be applied on email input
10334 'emailMask' : /[a-z0-9_\.\-@]/i,
10337 * The function used to validate URLs
10338 * @param {String} value The URL
10340 'url' : function(v){
10341 return url.test(v);
10344 * The error text to display when the url validation function returns false
10347 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10350 * The function used to validate alpha values
10351 * @param {String} value The value
10353 'alpha' : function(v){
10354 return alpha.test(v);
10357 * The error text to display when the alpha validation function returns false
10360 'alphaText' : 'This field should only contain letters and _',
10362 * The keystroke filter mask to be applied on alpha input
10365 'alphaMask' : /[a-z_]/i,
10368 * The function used to validate alphanumeric values
10369 * @param {String} value The value
10371 'alphanum' : function(v){
10372 return alphanum.test(v);
10375 * The error text to display when the alphanumeric validation function returns false
10378 'alphanumText' : 'This field should only contain letters, numbers and _',
10380 * The keystroke filter mask to be applied on alphanumeric input
10383 'alphanumMask' : /[a-z0-9_]/i
10393 * @class Roo.bootstrap.Input
10394 * @extends Roo.bootstrap.Component
10395 * Bootstrap Input class
10396 * @cfg {Boolean} disabled is it disabled
10397 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10398 * @cfg {String} name name of the input
10399 * @cfg {string} fieldLabel - the label associated
10400 * @cfg {string} placeholder - placeholder to put in text.
10401 * @cfg {string} before - input group add on before
10402 * @cfg {string} after - input group add on after
10403 * @cfg {string} size - (lg|sm) or leave empty..
10404 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10405 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10406 * @cfg {Number} md colspan out of 12 for computer-sized screens
10407 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10408 * @cfg {string} value default value of the input
10409 * @cfg {Number} labelWidth set the width of label
10410 * @cfg {Number} labellg set the width of label (1-12)
10411 * @cfg {Number} labelmd set the width of label (1-12)
10412 * @cfg {Number} labelsm set the width of label (1-12)
10413 * @cfg {Number} labelxs set the width of label (1-12)
10414 * @cfg {String} labelAlign (top|left)
10415 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10416 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10417 * @cfg {String} indicatorpos (left|right) default left
10418 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10419 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10420 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10422 * @cfg {String} align (left|center|right) Default left
10423 * @cfg {Boolean} forceFeedback (true|false) Default false
10426 * Create a new Input
10427 * @param {Object} config The config object
10430 Roo.bootstrap.Input = function(config){
10432 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10437 * Fires when this field receives input focus.
10438 * @param {Roo.form.Field} this
10443 * Fires when this field loses input focus.
10444 * @param {Roo.form.Field} this
10448 * @event specialkey
10449 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10450 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10451 * @param {Roo.form.Field} this
10452 * @param {Roo.EventObject} e The event object
10457 * Fires just before the field blurs if the field value has changed.
10458 * @param {Roo.form.Field} this
10459 * @param {Mixed} newValue The new value
10460 * @param {Mixed} oldValue The original value
10465 * Fires after the field has been marked as invalid.
10466 * @param {Roo.form.Field} this
10467 * @param {String} msg The validation message
10472 * Fires after the field has been validated with no errors.
10473 * @param {Roo.form.Field} this
10478 * Fires after the key up
10479 * @param {Roo.form.Field} this
10480 * @param {Roo.EventObject} e The event Object
10486 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10488 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10489 automatic validation (defaults to "keyup").
10491 validationEvent : "keyup",
10493 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10495 validateOnBlur : true,
10497 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10499 validationDelay : 250,
10501 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10503 focusClass : "x-form-focus", // not needed???
10507 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10509 invalidClass : "has-warning",
10512 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10514 validClass : "has-success",
10517 * @cfg {Boolean} hasFeedback (true|false) default true
10519 hasFeedback : true,
10522 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10524 invalidFeedbackClass : "glyphicon-warning-sign",
10527 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10529 validFeedbackClass : "glyphicon-ok",
10532 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10534 selectOnFocus : false,
10537 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10541 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10546 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10548 disableKeyFilter : false,
10551 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10555 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10559 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10561 blankText : "Please complete this mandatory field",
10564 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10568 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10570 maxLength : Number.MAX_VALUE,
10572 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10574 minLengthText : "The minimum length for this field is {0}",
10576 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10578 maxLengthText : "The maximum length for this field is {0}",
10582 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10583 * If available, this function will be called only after the basic validators all return true, and will be passed the
10584 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10588 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10589 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10590 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10594 * @cfg {String} regexText -- Depricated - use Invalid Text
10599 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10605 autocomplete: false,
10609 inputType : 'text',
10612 placeholder: false,
10617 preventMark: false,
10618 isFormField : true,
10621 labelAlign : false,
10624 formatedValue : false,
10625 forceFeedback : false,
10627 indicatorpos : 'left',
10637 parentLabelAlign : function()
10640 while (parent.parent()) {
10641 parent = parent.parent();
10642 if (typeof(parent.labelAlign) !='undefined') {
10643 return parent.labelAlign;
10650 getAutoCreate : function()
10652 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10658 if(this.inputType != 'hidden'){
10659 cfg.cls = 'form-group' //input-group
10665 type : this.inputType,
10666 value : this.value,
10667 cls : 'form-control',
10668 placeholder : this.placeholder || '',
10669 autocomplete : this.autocomplete || 'new-password'
10671 if (this.inputType == 'file') {
10672 input.style = 'overflow:hidden'; // why not in CSS?
10675 if(this.capture.length){
10676 input.capture = this.capture;
10679 if(this.accept.length){
10680 input.accept = this.accept + "/*";
10684 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10687 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10688 input.maxLength = this.maxLength;
10691 if (this.disabled) {
10692 input.disabled=true;
10695 if (this.readOnly) {
10696 input.readonly=true;
10700 input.name = this.name;
10704 input.cls += ' input-' + this.size;
10708 ['xs','sm','md','lg'].map(function(size){
10709 if (settings[size]) {
10710 cfg.cls += ' col-' + size + '-' + settings[size];
10714 var inputblock = input;
10718 cls: 'glyphicon form-control-feedback'
10721 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10724 cls : 'has-feedback',
10732 if (this.before || this.after) {
10735 cls : 'input-group',
10739 if (this.before && typeof(this.before) == 'string') {
10741 inputblock.cn.push({
10743 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10747 if (this.before && typeof(this.before) == 'object') {
10748 this.before = Roo.factory(this.before);
10750 inputblock.cn.push({
10752 cls : 'roo-input-before input-group-prepend input-group-' +
10753 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10757 inputblock.cn.push(input);
10759 if (this.after && typeof(this.after) == 'string') {
10760 inputblock.cn.push({
10762 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10766 if (this.after && typeof(this.after) == 'object') {
10767 this.after = Roo.factory(this.after);
10769 inputblock.cn.push({
10771 cls : 'roo-input-after input-group-append input-group-' +
10772 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10776 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10777 inputblock.cls += ' has-feedback';
10778 inputblock.cn.push(feedback);
10783 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10784 tooltip : 'This field is required'
10786 if (this.allowBlank ) {
10787 indicator.style = this.allowBlank ? ' display:none' : '';
10789 if (align ==='left' && this.fieldLabel.length) {
10791 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10798 cls : 'control-label col-form-label',
10799 html : this.fieldLabel
10810 var labelCfg = cfg.cn[1];
10811 var contentCfg = cfg.cn[2];
10813 if(this.indicatorpos == 'right'){
10818 cls : 'control-label col-form-label',
10822 html : this.fieldLabel
10836 labelCfg = cfg.cn[0];
10837 contentCfg = cfg.cn[1];
10841 if(this.labelWidth > 12){
10842 labelCfg.style = "width: " + this.labelWidth + 'px';
10845 if(this.labelWidth < 13 && this.labelmd == 0){
10846 this.labelmd = this.labelWidth;
10849 if(this.labellg > 0){
10850 labelCfg.cls += ' col-lg-' + this.labellg;
10851 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10854 if(this.labelmd > 0){
10855 labelCfg.cls += ' col-md-' + this.labelmd;
10856 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10859 if(this.labelsm > 0){
10860 labelCfg.cls += ' col-sm-' + this.labelsm;
10861 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10864 if(this.labelxs > 0){
10865 labelCfg.cls += ' col-xs-' + this.labelxs;
10866 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10870 } else if ( this.fieldLabel.length) {
10877 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10878 tooltip : 'This field is required',
10879 style : this.allowBlank ? ' display:none' : ''
10883 //cls : 'input-group-addon',
10884 html : this.fieldLabel
10892 if(this.indicatorpos == 'right'){
10897 //cls : 'input-group-addon',
10898 html : this.fieldLabel
10903 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10904 tooltip : 'This field is required',
10905 style : this.allowBlank ? ' display:none' : ''
10925 if (this.parentType === 'Navbar' && this.parent().bar) {
10926 cfg.cls += ' navbar-form';
10929 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10930 // on BS4 we do this only if not form
10931 cfg.cls += ' navbar-form';
10939 * return the real input element.
10941 inputEl: function ()
10943 return this.el.select('input.form-control',true).first();
10946 tooltipEl : function()
10948 return this.inputEl();
10951 indicatorEl : function()
10953 if (Roo.bootstrap.version == 4) {
10954 return false; // not enabled in v4 yet.
10957 var indicator = this.el.select('i.roo-required-indicator',true).first();
10967 setDisabled : function(v)
10969 var i = this.inputEl().dom;
10971 i.removeAttribute('disabled');
10975 i.setAttribute('disabled','true');
10977 initEvents : function()
10980 this.inputEl().on("keydown" , this.fireKey, this);
10981 this.inputEl().on("focus", this.onFocus, this);
10982 this.inputEl().on("blur", this.onBlur, this);
10984 this.inputEl().relayEvent('keyup', this);
10986 this.indicator = this.indicatorEl();
10988 if(this.indicator){
10989 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10992 // reference to original value for reset
10993 this.originalValue = this.getValue();
10994 //Roo.form.TextField.superclass.initEvents.call(this);
10995 if(this.validationEvent == 'keyup'){
10996 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10997 this.inputEl().on('keyup', this.filterValidation, this);
10999 else if(this.validationEvent !== false){
11000 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11003 if(this.selectOnFocus){
11004 this.on("focus", this.preFocus, this);
11007 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11008 this.inputEl().on("keypress", this.filterKeys, this);
11010 this.inputEl().relayEvent('keypress', this);
11013 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11014 this.el.on("click", this.autoSize, this);
11017 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11018 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11021 if (typeof(this.before) == 'object') {
11022 this.before.render(this.el.select('.roo-input-before',true).first());
11024 if (typeof(this.after) == 'object') {
11025 this.after.render(this.el.select('.roo-input-after',true).first());
11028 this.inputEl().on('change', this.onChange, this);
11031 filterValidation : function(e){
11032 if(!e.isNavKeyPress()){
11033 this.validationTask.delay(this.validationDelay);
11037 * Validates the field value
11038 * @return {Boolean} True if the value is valid, else false
11040 validate : function(){
11041 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11042 if(this.disabled || this.validateValue(this.getRawValue())){
11047 this.markInvalid();
11053 * Validates a value according to the field's validation rules and marks the field as invalid
11054 * if the validation fails
11055 * @param {Mixed} value The value to validate
11056 * @return {Boolean} True if the value is valid, else false
11058 validateValue : function(value)
11060 if(this.getVisibilityEl().hasClass('hidden')){
11064 if(value.length < 1) { // if it's blank
11065 if(this.allowBlank){
11071 if(value.length < this.minLength){
11074 if(value.length > this.maxLength){
11078 var vt = Roo.form.VTypes;
11079 if(!vt[this.vtype](value, this)){
11083 if(typeof this.validator == "function"){
11084 var msg = this.validator(value);
11088 if (typeof(msg) == 'string') {
11089 this.invalidText = msg;
11093 if(this.regex && !this.regex.test(value)){
11101 fireKey : function(e){
11102 //Roo.log('field ' + e.getKey());
11103 if(e.isNavKeyPress()){
11104 this.fireEvent("specialkey", this, e);
11107 focus : function (selectText){
11109 this.inputEl().focus();
11110 if(selectText === true){
11111 this.inputEl().dom.select();
11117 onFocus : function(){
11118 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11119 // this.el.addClass(this.focusClass);
11121 if(!this.hasFocus){
11122 this.hasFocus = true;
11123 this.startValue = this.getValue();
11124 this.fireEvent("focus", this);
11128 beforeBlur : Roo.emptyFn,
11132 onBlur : function(){
11134 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11135 //this.el.removeClass(this.focusClass);
11137 this.hasFocus = false;
11138 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11141 var v = this.getValue();
11142 if(String(v) !== String(this.startValue)){
11143 this.fireEvent('change', this, v, this.startValue);
11145 this.fireEvent("blur", this);
11148 onChange : function(e)
11150 var v = this.getValue();
11151 if(String(v) !== String(this.startValue)){
11152 this.fireEvent('change', this, v, this.startValue);
11158 * Resets the current field value to the originally loaded value and clears any validation messages
11160 reset : function(){
11161 this.setValue(this.originalValue);
11165 * Returns the name of the field
11166 * @return {Mixed} name The name field
11168 getName: function(){
11172 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11173 * @return {Mixed} value The field value
11175 getValue : function(){
11177 var v = this.inputEl().getValue();
11182 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11183 * @return {Mixed} value The field value
11185 getRawValue : function(){
11186 var v = this.inputEl().getValue();
11192 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11193 * @param {Mixed} value The value to set
11195 setRawValue : function(v){
11196 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11199 selectText : function(start, end){
11200 var v = this.getRawValue();
11202 start = start === undefined ? 0 : start;
11203 end = end === undefined ? v.length : end;
11204 var d = this.inputEl().dom;
11205 if(d.setSelectionRange){
11206 d.setSelectionRange(start, end);
11207 }else if(d.createTextRange){
11208 var range = d.createTextRange();
11209 range.moveStart("character", start);
11210 range.moveEnd("character", v.length-end);
11217 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11218 * @param {Mixed} value The value to set
11220 setValue : function(v){
11223 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11229 processValue : function(value){
11230 if(this.stripCharsRe){
11231 var newValue = value.replace(this.stripCharsRe, '');
11232 if(newValue !== value){
11233 this.setRawValue(newValue);
11240 preFocus : function(){
11242 if(this.selectOnFocus){
11243 this.inputEl().dom.select();
11246 filterKeys : function(e){
11247 var k = e.getKey();
11248 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11251 var c = e.getCharCode(), cc = String.fromCharCode(c);
11252 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11255 if(!this.maskRe.test(cc)){
11260 * Clear any invalid styles/messages for this field
11262 clearInvalid : function(){
11264 if(!this.el || this.preventMark){ // not rendered
11269 this.el.removeClass([this.invalidClass, 'is-invalid']);
11271 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11273 var feedback = this.el.select('.form-control-feedback', true).first();
11276 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11281 if(this.indicator){
11282 this.indicator.removeClass('visible');
11283 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11286 this.fireEvent('valid', this);
11290 * Mark this field as valid
11292 markValid : function()
11294 if(!this.el || this.preventMark){ // not rendered...
11298 this.el.removeClass([this.invalidClass, this.validClass]);
11299 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11301 var feedback = this.el.select('.form-control-feedback', true).first();
11304 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11307 if(this.indicator){
11308 this.indicator.removeClass('visible');
11309 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11317 if(this.allowBlank && !this.getRawValue().length){
11320 if (Roo.bootstrap.version == 3) {
11321 this.el.addClass(this.validClass);
11323 this.inputEl().addClass('is-valid');
11326 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11328 var feedback = this.el.select('.form-control-feedback', true).first();
11331 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11332 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11337 this.fireEvent('valid', this);
11341 * Mark this field as invalid
11342 * @param {String} msg The validation message
11344 markInvalid : function(msg)
11346 if(!this.el || this.preventMark){ // not rendered
11350 this.el.removeClass([this.invalidClass, this.validClass]);
11351 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11353 var feedback = this.el.select('.form-control-feedback', true).first();
11356 this.el.select('.form-control-feedback', true).first().removeClass(
11357 [this.invalidFeedbackClass, this.validFeedbackClass]);
11364 if(this.allowBlank && !this.getRawValue().length){
11368 if(this.indicator){
11369 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11370 this.indicator.addClass('visible');
11372 if (Roo.bootstrap.version == 3) {
11373 this.el.addClass(this.invalidClass);
11375 this.inputEl().addClass('is-invalid');
11380 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11382 var feedback = this.el.select('.form-control-feedback', true).first();
11385 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11387 if(this.getValue().length || this.forceFeedback){
11388 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11395 this.fireEvent('invalid', this, msg);
11398 SafariOnKeyDown : function(event)
11400 // this is a workaround for a password hang bug on chrome/ webkit.
11401 if (this.inputEl().dom.type != 'password') {
11405 var isSelectAll = false;
11407 if(this.inputEl().dom.selectionEnd > 0){
11408 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11410 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11411 event.preventDefault();
11416 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11418 event.preventDefault();
11419 // this is very hacky as keydown always get's upper case.
11421 var cc = String.fromCharCode(event.getCharCode());
11422 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11426 adjustWidth : function(tag, w){
11427 tag = tag.toLowerCase();
11428 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11429 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11430 if(tag == 'input'){
11433 if(tag == 'textarea'){
11436 }else if(Roo.isOpera){
11437 if(tag == 'input'){
11440 if(tag == 'textarea'){
11448 setFieldLabel : function(v)
11450 if(!this.rendered){
11454 if(this.indicatorEl()){
11455 var ar = this.el.select('label > span',true);
11457 if (ar.elements.length) {
11458 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11459 this.fieldLabel = v;
11463 var br = this.el.select('label',true);
11465 if(br.elements.length) {
11466 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11467 this.fieldLabel = v;
11471 Roo.log('Cannot Found any of label > span || label in input');
11475 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476 this.fieldLabel = v;
11491 * @class Roo.bootstrap.TextArea
11492 * @extends Roo.bootstrap.Input
11493 * Bootstrap TextArea class
11494 * @cfg {Number} cols Specifies the visible width of a text area
11495 * @cfg {Number} rows Specifies the visible number of lines in a text area
11496 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11497 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11498 * @cfg {string} html text
11501 * Create a new TextArea
11502 * @param {Object} config The config object
11505 Roo.bootstrap.TextArea = function(config){
11506 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11510 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11520 getAutoCreate : function(){
11522 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11528 if(this.inputType != 'hidden'){
11529 cfg.cls = 'form-group' //input-group
11537 value : this.value || '',
11538 html: this.html || '',
11539 cls : 'form-control',
11540 placeholder : this.placeholder || ''
11544 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11545 input.maxLength = this.maxLength;
11549 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11553 input.cols = this.cols;
11556 if (this.readOnly) {
11557 input.readonly = true;
11561 input.name = this.name;
11565 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11569 ['xs','sm','md','lg'].map(function(size){
11570 if (settings[size]) {
11571 cfg.cls += ' col-' + size + '-' + settings[size];
11575 var inputblock = input;
11577 if(this.hasFeedback && !this.allowBlank){
11581 cls: 'glyphicon form-control-feedback'
11585 cls : 'has-feedback',
11594 if (this.before || this.after) {
11597 cls : 'input-group',
11601 inputblock.cn.push({
11603 cls : 'input-group-addon',
11608 inputblock.cn.push(input);
11610 if(this.hasFeedback && !this.allowBlank){
11611 inputblock.cls += ' has-feedback';
11612 inputblock.cn.push(feedback);
11616 inputblock.cn.push({
11618 cls : 'input-group-addon',
11625 if (align ==='left' && this.fieldLabel.length) {
11630 cls : 'control-label',
11631 html : this.fieldLabel
11642 if(this.labelWidth > 12){
11643 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11646 if(this.labelWidth < 13 && this.labelmd == 0){
11647 this.labelmd = this.labelWidth;
11650 if(this.labellg > 0){
11651 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11652 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11655 if(this.labelmd > 0){
11656 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11657 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11660 if(this.labelsm > 0){
11661 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11662 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11665 if(this.labelxs > 0){
11666 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11667 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11670 } else if ( this.fieldLabel.length) {
11675 //cls : 'input-group-addon',
11676 html : this.fieldLabel
11694 if (this.disabled) {
11695 input.disabled=true;
11702 * return the real textarea element.
11704 inputEl: function ()
11706 return this.el.select('textarea.form-control',true).first();
11710 * Clear any invalid styles/messages for this field
11712 clearInvalid : function()
11715 if(!this.el || this.preventMark){ // not rendered
11719 var label = this.el.select('label', true).first();
11720 var icon = this.el.select('i.fa-star', true).first();
11725 this.el.removeClass( this.validClass);
11726 this.inputEl().removeClass('is-invalid');
11728 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11730 var feedback = this.el.select('.form-control-feedback', true).first();
11733 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11738 this.fireEvent('valid', this);
11742 * Mark this field as valid
11744 markValid : function()
11746 if(!this.el || this.preventMark){ // not rendered
11750 this.el.removeClass([this.invalidClass, this.validClass]);
11751 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11753 var feedback = this.el.select('.form-control-feedback', true).first();
11756 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11759 if(this.disabled || this.allowBlank){
11763 var label = this.el.select('label', true).first();
11764 var icon = this.el.select('i.fa-star', true).first();
11769 if (Roo.bootstrap.version == 3) {
11770 this.el.addClass(this.validClass);
11772 this.inputEl().addClass('is-valid');
11776 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11778 var feedback = this.el.select('.form-control-feedback', true).first();
11781 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11782 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11787 this.fireEvent('valid', this);
11791 * Mark this field as invalid
11792 * @param {String} msg The validation message
11794 markInvalid : function(msg)
11796 if(!this.el || this.preventMark){ // not rendered
11800 this.el.removeClass([this.invalidClass, this.validClass]);
11801 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11803 var feedback = this.el.select('.form-control-feedback', true).first();
11806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11809 if(this.disabled || this.allowBlank){
11813 var label = this.el.select('label', true).first();
11814 var icon = this.el.select('i.fa-star', true).first();
11816 if(!this.getValue().length && label && !icon){
11817 this.el.createChild({
11819 cls : 'text-danger fa fa-lg fa-star',
11820 tooltip : 'This field is required',
11821 style : 'margin-right:5px;'
11825 if (Roo.bootstrap.version == 3) {
11826 this.el.addClass(this.invalidClass);
11828 this.inputEl().addClass('is-invalid');
11831 // fixme ... this may be depricated need to test..
11832 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11834 var feedback = this.el.select('.form-control-feedback', true).first();
11837 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11839 if(this.getValue().length || this.forceFeedback){
11840 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11847 this.fireEvent('invalid', this, msg);
11855 * trigger field - base class for combo..
11860 * @class Roo.bootstrap.TriggerField
11861 * @extends Roo.bootstrap.Input
11862 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11863 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11864 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11865 * for which you can provide a custom implementation. For example:
11867 var trigger = new Roo.bootstrap.TriggerField();
11868 trigger.onTriggerClick = myTriggerFn;
11869 trigger.applyTo('my-field');
11872 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11873 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11874 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11875 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11876 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11879 * Create a new TriggerField.
11880 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11881 * to the base TextField)
11883 Roo.bootstrap.TriggerField = function(config){
11884 this.mimicing = false;
11885 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11888 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11890 * @cfg {String} triggerClass A CSS class to apply to the trigger
11893 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11898 * @cfg {Boolean} removable (true|false) special filter default false
11902 /** @cfg {Boolean} grow @hide */
11903 /** @cfg {Number} growMin @hide */
11904 /** @cfg {Number} growMax @hide */
11910 autoSize: Roo.emptyFn,
11914 deferHeight : true,
11917 actionMode : 'wrap',
11922 getAutoCreate : function(){
11924 var align = this.labelAlign || this.parentLabelAlign();
11929 cls: 'form-group' //input-group
11936 type : this.inputType,
11937 cls : 'form-control',
11938 autocomplete: 'new-password',
11939 placeholder : this.placeholder || ''
11943 input.name = this.name;
11946 input.cls += ' input-' + this.size;
11949 if (this.disabled) {
11950 input.disabled=true;
11953 var inputblock = input;
11955 if(this.hasFeedback && !this.allowBlank){
11959 cls: 'glyphicon form-control-feedback'
11962 if(this.removable && !this.editable ){
11964 cls : 'has-feedback',
11970 cls : 'roo-combo-removable-btn close'
11977 cls : 'has-feedback',
11986 if(this.removable && !this.editable ){
11988 cls : 'roo-removable',
11994 cls : 'roo-combo-removable-btn close'
12001 if (this.before || this.after) {
12004 cls : 'input-group',
12008 inputblock.cn.push({
12010 cls : 'input-group-addon input-group-prepend input-group-text',
12015 inputblock.cn.push(input);
12017 if(this.hasFeedback && !this.allowBlank){
12018 inputblock.cls += ' has-feedback';
12019 inputblock.cn.push(feedback);
12023 inputblock.cn.push({
12025 cls : 'input-group-addon input-group-append input-group-text',
12034 var ibwrap = inputblock;
12039 cls: 'roo-select2-choices',
12043 cls: 'roo-select2-search-field',
12055 cls: 'roo-select2-container input-group',
12060 cls: 'form-hidden-field'
12066 if(!this.multiple && this.showToggleBtn){
12072 if (this.caret != false) {
12075 cls: 'fa fa-' + this.caret
12082 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12084 Roo.bootstrap.version == 3 ? caret : '',
12087 cls: 'combobox-clear',
12101 combobox.cls += ' roo-select2-container-multi';
12105 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12106 tooltip : 'This field is required'
12108 if (Roo.bootstrap.version == 4) {
12111 style : 'display:none'
12116 if (align ==='left' && this.fieldLabel.length) {
12118 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12125 cls : 'control-label',
12126 html : this.fieldLabel
12138 var labelCfg = cfg.cn[1];
12139 var contentCfg = cfg.cn[2];
12141 if(this.indicatorpos == 'right'){
12146 cls : 'control-label',
12150 html : this.fieldLabel
12164 labelCfg = cfg.cn[0];
12165 contentCfg = cfg.cn[1];
12168 if(this.labelWidth > 12){
12169 labelCfg.style = "width: " + this.labelWidth + 'px';
12172 if(this.labelWidth < 13 && this.labelmd == 0){
12173 this.labelmd = this.labelWidth;
12176 if(this.labellg > 0){
12177 labelCfg.cls += ' col-lg-' + this.labellg;
12178 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12181 if(this.labelmd > 0){
12182 labelCfg.cls += ' col-md-' + this.labelmd;
12183 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12186 if(this.labelsm > 0){
12187 labelCfg.cls += ' col-sm-' + this.labelsm;
12188 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12191 if(this.labelxs > 0){
12192 labelCfg.cls += ' col-xs-' + this.labelxs;
12193 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12196 } else if ( this.fieldLabel.length) {
12197 // Roo.log(" label");
12202 //cls : 'input-group-addon',
12203 html : this.fieldLabel
12211 if(this.indicatorpos == 'right'){
12219 html : this.fieldLabel
12233 // Roo.log(" no label && no align");
12240 ['xs','sm','md','lg'].map(function(size){
12241 if (settings[size]) {
12242 cfg.cls += ' col-' + size + '-' + settings[size];
12253 onResize : function(w, h){
12254 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12255 // if(typeof w == 'number'){
12256 // var x = w - this.trigger.getWidth();
12257 // this.inputEl().setWidth(this.adjustWidth('input', x));
12258 // this.trigger.setStyle('left', x+'px');
12263 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12266 getResizeEl : function(){
12267 return this.inputEl();
12271 getPositionEl : function(){
12272 return this.inputEl();
12276 alignErrorIcon : function(){
12277 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12281 initEvents : function(){
12285 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12286 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12287 if(!this.multiple && this.showToggleBtn){
12288 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12289 if(this.hideTrigger){
12290 this.trigger.setDisplayed(false);
12292 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12296 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12299 if(this.removable && !this.editable && !this.tickable){
12300 var close = this.closeTriggerEl();
12303 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12304 close.on('click', this.removeBtnClick, this, close);
12308 //this.trigger.addClassOnOver('x-form-trigger-over');
12309 //this.trigger.addClassOnClick('x-form-trigger-click');
12312 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12316 closeTriggerEl : function()
12318 var close = this.el.select('.roo-combo-removable-btn', true).first();
12319 return close ? close : false;
12322 removeBtnClick : function(e, h, el)
12324 e.preventDefault();
12326 if(this.fireEvent("remove", this) !== false){
12328 this.fireEvent("afterremove", this)
12332 createList : function()
12334 this.list = Roo.get(document.body).createChild({
12335 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12336 cls: 'typeahead typeahead-long dropdown-menu',
12337 style: 'display:none'
12340 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12345 initTrigger : function(){
12350 onDestroy : function(){
12352 this.trigger.removeAllListeners();
12353 // this.trigger.remove();
12356 // this.wrap.remove();
12358 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12362 onFocus : function(){
12363 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12365 if(!this.mimicing){
12366 this.wrap.addClass('x-trigger-wrap-focus');
12367 this.mimicing = true;
12368 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12369 if(this.monitorTab){
12370 this.el.on("keydown", this.checkTab, this);
12377 checkTab : function(e){
12378 if(e.getKey() == e.TAB){
12379 this.triggerBlur();
12384 onBlur : function(){
12389 mimicBlur : function(e, t){
12391 if(!this.wrap.contains(t) && this.validateBlur()){
12392 this.triggerBlur();
12398 triggerBlur : function(){
12399 this.mimicing = false;
12400 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12401 if(this.monitorTab){
12402 this.el.un("keydown", this.checkTab, this);
12404 //this.wrap.removeClass('x-trigger-wrap-focus');
12405 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12409 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12410 validateBlur : function(e, t){
12415 onDisable : function(){
12416 this.inputEl().dom.disabled = true;
12417 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12419 // this.wrap.addClass('x-item-disabled');
12424 onEnable : function(){
12425 this.inputEl().dom.disabled = false;
12426 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12428 // this.el.removeClass('x-item-disabled');
12433 onShow : function(){
12434 var ae = this.getActionEl();
12437 ae.dom.style.display = '';
12438 ae.dom.style.visibility = 'visible';
12444 onHide : function(){
12445 var ae = this.getActionEl();
12446 ae.dom.style.display = 'none';
12450 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12451 * by an implementing function.
12453 * @param {EventObject} e
12455 onTriggerClick : Roo.emptyFn
12463 * @class Roo.bootstrap.CardUploader
12464 * @extends Roo.bootstrap.Button
12465 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12466 * @cfg {Number} errorTimeout default 3000
12467 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12468 * @cfg {Array} html The button text.
12472 * Create a new CardUploader
12473 * @param {Object} config The config object
12476 Roo.bootstrap.CardUploader = function(config){
12480 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12483 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12490 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12493 errorTimeout : 3000,
12497 fileCollection : false,
12500 getAutoCreate : function()
12504 cls :'form-group' ,
12509 //cls : 'input-group-addon',
12510 html : this.fieldLabel
12517 value : this.value,
12518 cls : 'd-none form-control'
12523 multiple : 'multiple',
12525 cls : 'd-none roo-card-upload-selector'
12529 cls : 'roo-card-uploader-button-container w-100 mb-2'
12532 cls : 'card-columns roo-card-uploader-container'
12542 getChildContainer : function() /// what children are added to.
12544 return this.containerEl;
12547 getButtonContainer : function() /// what children are added to.
12549 return this.el.select(".roo-card-uploader-button-container").first();
12552 initEvents : function()
12555 Roo.bootstrap.Input.prototype.initEvents.call(this);
12559 xns: Roo.bootstrap,
12562 container_method : 'getButtonContainer' ,
12563 html : this.html, // fix changable?
12566 'click' : function(btn, e) {
12575 this.urlAPI = (window.createObjectURL && window) ||
12576 (window.URL && URL.revokeObjectURL && URL) ||
12577 (window.webkitURL && webkitURL);
12582 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12584 this.selectorEl.on('change', this.onFileSelected, this);
12587 this.images.forEach(function(img) {
12590 this.images = false;
12592 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12598 onClick : function(e)
12600 e.preventDefault();
12602 this.selectorEl.dom.click();
12606 onFileSelected : function(e)
12608 e.preventDefault();
12610 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12614 Roo.each(this.selectorEl.dom.files, function(file){
12615 this.addFile(file);
12624 addFile : function(file)
12627 if(typeof(file) === 'string'){
12628 throw "Add file by name?"; // should not happen
12632 if(!file || !this.urlAPI){
12642 var url = _this.urlAPI.createObjectURL( file);
12645 id : Roo.bootstrap.CardUploader.ID--,
12646 is_uploaded : false,
12649 mimetype : file.type,
12656 addCard : function (data)
12658 // hidden input element?
12659 // if the file is not an image...
12660 //then we need to use something other that and header_image
12665 xns : Roo.bootstrap,
12666 xtype : 'CardFooter',
12669 xns : Roo.bootstrap,
12675 xns : Roo.bootstrap,
12677 html : String.format("<small>{0}</small>", data.title),
12678 cls : 'col-11 text-left',
12683 click : function() {
12684 this.downloadCard(data.id)
12690 xns : Roo.bootstrap,
12698 click : function() {
12699 t.removeCard(data.id)
12711 var cn = this.addxtype(
12714 xns : Roo.bootstrap,
12717 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12718 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12719 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12724 initEvents : function() {
12725 Roo.bootstrap.Card.prototype.initEvents.call(this);
12726 this.imgEl = this.el.select('.card-img-top').first();
12728 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12729 this.imgEl.set({ 'pointer' : 'cursor' });
12738 // dont' really need ot update items.
12739 // this.items.push(cn);
12740 this.fileCollection.add(cn);
12741 this.updateInput();
12744 removeCard : function(id)
12747 var card = this.fileCollection.get(id);
12748 card.data.is_deleted = 1;
12749 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12750 this.fileCollection.remove(card);
12751 //this.items = this.items.filter(function(e) { return e != card });
12752 // dont' really need ot update items.
12753 card.el.dom.parentNode.removeChild(card.el.dom);
12758 this.fileCollection.each(function(card) {
12759 card.el.dom.parentNode.removeChild(card.el.dom);
12761 this.fileCollection.clear();
12762 this.updateInput();
12765 updateInput : function()
12768 this.fileCollection.each(function(e) {
12772 this.inputEl().dom.value = JSON.stringify(data);
12779 Roo.bootstrap.CardUploader.ID = -1;/*
12781 * Ext JS Library 1.1.1
12782 * Copyright(c) 2006-2007, Ext JS, LLC.
12784 * Originally Released Under LGPL - original licence link has changed is not relivant.
12787 * <script type="text/javascript">
12792 * @class Roo.data.SortTypes
12794 * Defines the default sorting (casting?) comparison functions used when sorting data.
12796 Roo.data.SortTypes = {
12798 * Default sort that does nothing
12799 * @param {Mixed} s The value being converted
12800 * @return {Mixed} The comparison value
12802 none : function(s){
12807 * The regular expression used to strip tags
12811 stripTagsRE : /<\/?[^>]+>/gi,
12814 * Strips all HTML tags to sort on text only
12815 * @param {Mixed} s The value being converted
12816 * @return {String} The comparison value
12818 asText : function(s){
12819 return String(s).replace(this.stripTagsRE, "");
12823 * Strips all HTML tags to sort on text only - Case insensitive
12824 * @param {Mixed} s The value being converted
12825 * @return {String} The comparison value
12827 asUCText : function(s){
12828 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12832 * Case insensitive string
12833 * @param {Mixed} s The value being converted
12834 * @return {String} The comparison value
12836 asUCString : function(s) {
12837 return String(s).toUpperCase();
12842 * @param {Mixed} s The value being converted
12843 * @return {Number} The comparison value
12845 asDate : function(s) {
12849 if(s instanceof Date){
12850 return s.getTime();
12852 return Date.parse(String(s));
12857 * @param {Mixed} s The value being converted
12858 * @return {Float} The comparison value
12860 asFloat : function(s) {
12861 var val = parseFloat(String(s).replace(/,/g, ""));
12870 * @param {Mixed} s The value being converted
12871 * @return {Number} The comparison value
12873 asInt : function(s) {
12874 var val = parseInt(String(s).replace(/,/g, ""));
12882 * Ext JS Library 1.1.1
12883 * Copyright(c) 2006-2007, Ext JS, LLC.
12885 * Originally Released Under LGPL - original licence link has changed is not relivant.
12888 * <script type="text/javascript">
12892 * @class Roo.data.Record
12893 * Instances of this class encapsulate both record <em>definition</em> information, and record
12894 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12895 * to access Records cached in an {@link Roo.data.Store} object.<br>
12897 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12898 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12901 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12903 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12904 * {@link #create}. The parameters are the same.
12905 * @param {Array} data An associative Array of data values keyed by the field name.
12906 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12907 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12908 * not specified an integer id is generated.
12910 Roo.data.Record = function(data, id){
12911 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12916 * Generate a constructor for a specific record layout.
12917 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12918 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12919 * Each field definition object may contain the following properties: <ul>
12920 * <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,
12921 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12922 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12923 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12924 * is being used, then this is a string containing the javascript expression to reference the data relative to
12925 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12926 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12927 * this may be omitted.</p></li>
12928 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12929 * <ul><li>auto (Default, implies no conversion)</li>
12934 * <li>date</li></ul></p></li>
12935 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12936 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12937 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12938 * by the Reader into an object that will be stored in the Record. It is passed the
12939 * following parameters:<ul>
12940 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12942 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12944 * <br>usage:<br><pre><code>
12945 var TopicRecord = Roo.data.Record.create(
12946 {name: 'title', mapping: 'topic_title'},
12947 {name: 'author', mapping: 'username'},
12948 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12949 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12950 {name: 'lastPoster', mapping: 'user2'},
12951 {name: 'excerpt', mapping: 'post_text'}
12954 var myNewRecord = new TopicRecord({
12955 title: 'Do my job please',
12958 lastPost: new Date(),
12959 lastPoster: 'Animal',
12960 excerpt: 'No way dude!'
12962 myStore.add(myNewRecord);
12967 Roo.data.Record.create = function(o){
12968 var f = function(){
12969 f.superclass.constructor.apply(this, arguments);
12971 Roo.extend(f, Roo.data.Record);
12972 var p = f.prototype;
12973 p.fields = new Roo.util.MixedCollection(false, function(field){
12976 for(var i = 0, len = o.length; i < len; i++){
12977 p.fields.add(new Roo.data.Field(o[i]));
12979 f.getField = function(name){
12980 return p.fields.get(name);
12985 Roo.data.Record.AUTO_ID = 1000;
12986 Roo.data.Record.EDIT = 'edit';
12987 Roo.data.Record.REJECT = 'reject';
12988 Roo.data.Record.COMMIT = 'commit';
12990 Roo.data.Record.prototype = {
12992 * Readonly flag - true if this record has been modified.
13001 join : function(store){
13002 this.store = store;
13006 * Set the named field to the specified value.
13007 * @param {String} name The name of the field to set.
13008 * @param {Object} value The value to set the field to.
13010 set : function(name, value){
13011 if(this.data[name] == value){
13015 if(!this.modified){
13016 this.modified = {};
13018 if(typeof this.modified[name] == 'undefined'){
13019 this.modified[name] = this.data[name];
13021 this.data[name] = value;
13022 if(!this.editing && this.store){
13023 this.store.afterEdit(this);
13028 * Get the value of the named field.
13029 * @param {String} name The name of the field to get the value of.
13030 * @return {Object} The value of the field.
13032 get : function(name){
13033 return this.data[name];
13037 beginEdit : function(){
13038 this.editing = true;
13039 this.modified = {};
13043 cancelEdit : function(){
13044 this.editing = false;
13045 delete this.modified;
13049 endEdit : function(){
13050 this.editing = false;
13051 if(this.dirty && this.store){
13052 this.store.afterEdit(this);
13057 * Usually called by the {@link Roo.data.Store} which owns the Record.
13058 * Rejects all changes made to the Record since either creation, or the last commit operation.
13059 * Modified fields are reverted to their original values.
13061 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13062 * of reject operations.
13064 reject : function(){
13065 var m = this.modified;
13067 if(typeof m[n] != "function"){
13068 this.data[n] = m[n];
13071 this.dirty = false;
13072 delete this.modified;
13073 this.editing = false;
13075 this.store.afterReject(this);
13080 * Usually called by the {@link Roo.data.Store} which owns the Record.
13081 * Commits all changes made to the Record since either creation, or the last commit operation.
13083 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13084 * of commit operations.
13086 commit : function(){
13087 this.dirty = false;
13088 delete this.modified;
13089 this.editing = false;
13091 this.store.afterCommit(this);
13096 hasError : function(){
13097 return this.error != null;
13101 clearError : function(){
13106 * Creates a copy of this record.
13107 * @param {String} id (optional) A new record id if you don't want to use this record's id
13110 copy : function(newId) {
13111 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13115 * Ext JS Library 1.1.1
13116 * Copyright(c) 2006-2007, Ext JS, LLC.
13118 * Originally Released Under LGPL - original licence link has changed is not relivant.
13121 * <script type="text/javascript">
13127 * @class Roo.data.Store
13128 * @extends Roo.util.Observable
13129 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13130 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13132 * 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
13133 * has no knowledge of the format of the data returned by the Proxy.<br>
13135 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13136 * instances from the data object. These records are cached and made available through accessor functions.
13138 * Creates a new Store.
13139 * @param {Object} config A config object containing the objects needed for the Store to access data,
13140 * and read the data into Records.
13142 Roo.data.Store = function(config){
13143 this.data = new Roo.util.MixedCollection(false);
13144 this.data.getKey = function(o){
13147 this.baseParams = {};
13149 this.paramNames = {
13154 "multisort" : "_multisort"
13157 if(config && config.data){
13158 this.inlineData = config.data;
13159 delete config.data;
13162 Roo.apply(this, config);
13164 if(this.reader){ // reader passed
13165 this.reader = Roo.factory(this.reader, Roo.data);
13166 this.reader.xmodule = this.xmodule || false;
13167 if(!this.recordType){
13168 this.recordType = this.reader.recordType;
13170 if(this.reader.onMetaChange){
13171 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13175 if(this.recordType){
13176 this.fields = this.recordType.prototype.fields;
13178 this.modified = [];
13182 * @event datachanged
13183 * Fires when the data cache has changed, and a widget which is using this Store
13184 * as a Record cache should refresh its view.
13185 * @param {Store} this
13187 datachanged : true,
13189 * @event metachange
13190 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13191 * @param {Store} this
13192 * @param {Object} meta The JSON metadata
13197 * Fires when Records have been added to the Store
13198 * @param {Store} this
13199 * @param {Roo.data.Record[]} records The array of Records added
13200 * @param {Number} index The index at which the record(s) were added
13205 * Fires when a Record has been removed from the Store
13206 * @param {Store} this
13207 * @param {Roo.data.Record} record The Record that was removed
13208 * @param {Number} index The index at which the record was removed
13213 * Fires when a Record has been updated
13214 * @param {Store} this
13215 * @param {Roo.data.Record} record The Record that was updated
13216 * @param {String} operation The update operation being performed. Value may be one of:
13218 Roo.data.Record.EDIT
13219 Roo.data.Record.REJECT
13220 Roo.data.Record.COMMIT
13226 * Fires when the data cache has been cleared.
13227 * @param {Store} this
13231 * @event beforeload
13232 * Fires before a request is made for a new data object. If the beforeload handler returns false
13233 * the load action will be canceled.
13234 * @param {Store} this
13235 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13239 * @event beforeloadadd
13240 * Fires after a new set of Records has been loaded.
13241 * @param {Store} this
13242 * @param {Roo.data.Record[]} records The Records that were loaded
13243 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245 beforeloadadd : true,
13248 * Fires after a new set of Records has been loaded, before they are added to the store.
13249 * @param {Store} this
13250 * @param {Roo.data.Record[]} records The Records that were loaded
13251 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13252 * @params {Object} return from reader
13256 * @event loadexception
13257 * Fires if an exception occurs in the Proxy during loading.
13258 * Called with the signature of the Proxy's "loadexception" event.
13259 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13262 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13263 * @param {Object} load options
13264 * @param {Object} jsonData from your request (normally this contains the Exception)
13266 loadexception : true
13270 this.proxy = Roo.factory(this.proxy, Roo.data);
13271 this.proxy.xmodule = this.xmodule || false;
13272 this.relayEvents(this.proxy, ["loadexception"]);
13274 this.sortToggle = {};
13275 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13277 Roo.data.Store.superclass.constructor.call(this);
13279 if(this.inlineData){
13280 this.loadData(this.inlineData);
13281 delete this.inlineData;
13285 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13287 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13288 * without a remote query - used by combo/forms at present.
13292 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13295 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13298 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13299 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13302 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13303 * on any HTTP request
13306 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13309 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13313 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13314 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13316 remoteSort : false,
13319 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13320 * loaded or when a record is removed. (defaults to false).
13322 pruneModifiedRecords : false,
13325 lastOptions : null,
13328 * Add Records to the Store and fires the add event.
13329 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13331 add : function(records){
13332 records = [].concat(records);
13333 for(var i = 0, len = records.length; i < len; i++){
13334 records[i].join(this);
13336 var index = this.data.length;
13337 this.data.addAll(records);
13338 this.fireEvent("add", this, records, index);
13342 * Remove a Record from the Store and fires the remove event.
13343 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13345 remove : function(record){
13346 var index = this.data.indexOf(record);
13347 this.data.removeAt(index);
13349 if(this.pruneModifiedRecords){
13350 this.modified.remove(record);
13352 this.fireEvent("remove", this, record, index);
13356 * Remove all Records from the Store and fires the clear event.
13358 removeAll : function(){
13360 if(this.pruneModifiedRecords){
13361 this.modified = [];
13363 this.fireEvent("clear", this);
13367 * Inserts Records to the Store at the given index and fires the add event.
13368 * @param {Number} index The start index at which to insert the passed Records.
13369 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13371 insert : function(index, records){
13372 records = [].concat(records);
13373 for(var i = 0, len = records.length; i < len; i++){
13374 this.data.insert(index, records[i]);
13375 records[i].join(this);
13377 this.fireEvent("add", this, records, index);
13381 * Get the index within the cache of the passed Record.
13382 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13383 * @return {Number} The index of the passed Record. Returns -1 if not found.
13385 indexOf : function(record){
13386 return this.data.indexOf(record);
13390 * Get the index within the cache of the Record with the passed id.
13391 * @param {String} id The id of the Record to find.
13392 * @return {Number} The index of the Record. Returns -1 if not found.
13394 indexOfId : function(id){
13395 return this.data.indexOfKey(id);
13399 * Get the Record with the specified id.
13400 * @param {String} id The id of the Record to find.
13401 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13403 getById : function(id){
13404 return this.data.key(id);
13408 * Get the Record at the specified index.
13409 * @param {Number} index The index of the Record to find.
13410 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13412 getAt : function(index){
13413 return this.data.itemAt(index);
13417 * Returns a range of Records between specified indices.
13418 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13419 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13420 * @return {Roo.data.Record[]} An array of Records
13422 getRange : function(start, end){
13423 return this.data.getRange(start, end);
13427 storeOptions : function(o){
13428 o = Roo.apply({}, o);
13431 this.lastOptions = o;
13435 * Loads the Record cache from the configured Proxy using the configured Reader.
13437 * If using remote paging, then the first load call must specify the <em>start</em>
13438 * and <em>limit</em> properties in the options.params property to establish the initial
13439 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13441 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13442 * and this call will return before the new data has been loaded. Perform any post-processing
13443 * in a callback function, or in a "load" event handler.</strong>
13445 * @param {Object} options An object containing properties which control loading options:<ul>
13446 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13447 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13448 * passed the following arguments:<ul>
13449 * <li>r : Roo.data.Record[]</li>
13450 * <li>options: Options object from the load call</li>
13451 * <li>success: Boolean success indicator</li></ul></li>
13452 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13453 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13456 load : function(options){
13457 options = options || {};
13458 if(this.fireEvent("beforeload", this, options) !== false){
13459 this.storeOptions(options);
13460 var p = Roo.apply(options.params || {}, this.baseParams);
13461 // if meta was not loaded from remote source.. try requesting it.
13462 if (!this.reader.metaFromRemote) {
13463 p._requestMeta = 1;
13465 if(this.sortInfo && this.remoteSort){
13466 var pn = this.paramNames;
13467 p[pn["sort"]] = this.sortInfo.field;
13468 p[pn["dir"]] = this.sortInfo.direction;
13470 if (this.multiSort) {
13471 var pn = this.paramNames;
13472 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13475 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13480 * Reloads the Record cache from the configured Proxy using the configured Reader and
13481 * the options from the last load operation performed.
13482 * @param {Object} options (optional) An object containing properties which may override the options
13483 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13484 * the most recently used options are reused).
13486 reload : function(options){
13487 this.load(Roo.applyIf(options||{}, this.lastOptions));
13491 // Called as a callback by the Reader during a load operation.
13492 loadRecords : function(o, options, success){
13493 if(!o || success === false){
13494 if(success !== false){
13495 this.fireEvent("load", this, [], options, o);
13497 if(options.callback){
13498 options.callback.call(options.scope || this, [], options, false);
13502 // if data returned failure - throw an exception.
13503 if (o.success === false) {
13504 // show a message if no listener is registered.
13505 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13506 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13508 // loadmask wil be hooked into this..
13509 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13512 var r = o.records, t = o.totalRecords || r.length;
13514 this.fireEvent("beforeloadadd", this, r, options, o);
13516 if(!options || options.add !== true){
13517 if(this.pruneModifiedRecords){
13518 this.modified = [];
13520 for(var i = 0, len = r.length; i < len; i++){
13524 this.data = this.snapshot;
13525 delete this.snapshot;
13528 this.data.addAll(r);
13529 this.totalLength = t;
13531 this.fireEvent("datachanged", this);
13533 this.totalLength = Math.max(t, this.data.length+r.length);
13537 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13539 var e = new Roo.data.Record({});
13541 e.set(this.parent.displayField, this.parent.emptyTitle);
13542 e.set(this.parent.valueField, '');
13547 this.fireEvent("load", this, r, options, o);
13548 if(options.callback){
13549 options.callback.call(options.scope || this, r, options, true);
13555 * Loads data from a passed data block. A Reader which understands the format of the data
13556 * must have been configured in the constructor.
13557 * @param {Object} data The data block from which to read the Records. The format of the data expected
13558 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13559 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13561 loadData : function(o, append){
13562 var r = this.reader.readRecords(o);
13563 this.loadRecords(r, {add: append}, true);
13567 * using 'cn' the nested child reader read the child array into it's child stores.
13568 * @param {Object} rec The record with a 'children array
13570 loadDataFromChildren : function(rec)
13572 this.loadData(this.reader.toLoadData(rec));
13577 * Gets the number of cached records.
13579 * <em>If using paging, this may not be the total size of the dataset. If the data object
13580 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13581 * the data set size</em>
13583 getCount : function(){
13584 return this.data.length || 0;
13588 * Gets the total number of records in the dataset as returned by the server.
13590 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13591 * the dataset size</em>
13593 getTotalCount : function(){
13594 return this.totalLength || 0;
13598 * Returns the sort state of the Store as an object with two properties:
13600 field {String} The name of the field by which the Records are sorted
13601 direction {String} The sort order, "ASC" or "DESC"
13604 getSortState : function(){
13605 return this.sortInfo;
13609 applySort : function(){
13610 if(this.sortInfo && !this.remoteSort){
13611 var s = this.sortInfo, f = s.field;
13612 var st = this.fields.get(f).sortType;
13613 var fn = function(r1, r2){
13614 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13615 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13617 this.data.sort(s.direction, fn);
13618 if(this.snapshot && this.snapshot != this.data){
13619 this.snapshot.sort(s.direction, fn);
13625 * Sets the default sort column and order to be used by the next load operation.
13626 * @param {String} fieldName The name of the field to sort by.
13627 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13629 setDefaultSort : function(field, dir){
13630 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13634 * Sort the Records.
13635 * If remote sorting is used, the sort is performed on the server, and the cache is
13636 * reloaded. If local sorting is used, the cache is sorted internally.
13637 * @param {String} fieldName The name of the field to sort by.
13638 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13640 sort : function(fieldName, dir){
13641 var f = this.fields.get(fieldName);
13643 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13645 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13646 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13651 this.sortToggle[f.name] = dir;
13652 this.sortInfo = {field: f.name, direction: dir};
13653 if(!this.remoteSort){
13655 this.fireEvent("datachanged", this);
13657 this.load(this.lastOptions);
13662 * Calls the specified function for each of the Records in the cache.
13663 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13664 * Returning <em>false</em> aborts and exits the iteration.
13665 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13667 each : function(fn, scope){
13668 this.data.each(fn, scope);
13672 * Gets all records modified since the last commit. Modified records are persisted across load operations
13673 * (e.g., during paging).
13674 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13676 getModifiedRecords : function(){
13677 return this.modified;
13681 createFilterFn : function(property, value, anyMatch){
13682 if(!value.exec){ // not a regex
13683 value = String(value);
13684 if(value.length == 0){
13687 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13689 return function(r){
13690 return value.test(r.data[property]);
13695 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13696 * @param {String} property A field on your records
13697 * @param {Number} start The record index to start at (defaults to 0)
13698 * @param {Number} end The last record index to include (defaults to length - 1)
13699 * @return {Number} The sum
13701 sum : function(property, start, end){
13702 var rs = this.data.items, v = 0;
13703 start = start || 0;
13704 end = (end || end === 0) ? end : rs.length-1;
13706 for(var i = start; i <= end; i++){
13707 v += (rs[i].data[property] || 0);
13713 * Filter the records by a specified property.
13714 * @param {String} field A field on your records
13715 * @param {String/RegExp} value Either a string that the field
13716 * should start with or a RegExp to test against the field
13717 * @param {Boolean} anyMatch True to match any part not just the beginning
13719 filter : function(property, value, anyMatch){
13720 var fn = this.createFilterFn(property, value, anyMatch);
13721 return fn ? this.filterBy(fn) : this.clearFilter();
13725 * Filter by a function. The specified function will be called with each
13726 * record in this data source. If the function returns true the record is included,
13727 * otherwise it is filtered.
13728 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13729 * @param {Object} scope (optional) The scope of the function (defaults to this)
13731 filterBy : function(fn, scope){
13732 this.snapshot = this.snapshot || this.data;
13733 this.data = this.queryBy(fn, scope||this);
13734 this.fireEvent("datachanged", this);
13738 * Query the records by a specified property.
13739 * @param {String} field A field on your records
13740 * @param {String/RegExp} value Either a string that the field
13741 * should start with or a RegExp to test against the field
13742 * @param {Boolean} anyMatch True to match any part not just the beginning
13743 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13745 query : function(property, value, anyMatch){
13746 var fn = this.createFilterFn(property, value, anyMatch);
13747 return fn ? this.queryBy(fn) : this.data.clone();
13751 * Query by a function. The specified function will be called with each
13752 * record in this data source. If the function returns true the record is included
13754 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13755 * @param {Object} scope (optional) The scope of the function (defaults to this)
13756 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758 queryBy : function(fn, scope){
13759 var data = this.snapshot || this.data;
13760 return data.filterBy(fn, scope||this);
13764 * Collects unique values for a particular dataIndex from this store.
13765 * @param {String} dataIndex The property to collect
13766 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13767 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13768 * @return {Array} An array of the unique values
13770 collect : function(dataIndex, allowNull, bypassFilter){
13771 var d = (bypassFilter === true && this.snapshot) ?
13772 this.snapshot.items : this.data.items;
13773 var v, sv, r = [], l = {};
13774 for(var i = 0, len = d.length; i < len; i++){
13775 v = d[i].data[dataIndex];
13777 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13786 * Revert to a view of the Record cache with no filtering applied.
13787 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13789 clearFilter : function(suppressEvent){
13790 if(this.snapshot && this.snapshot != this.data){
13791 this.data = this.snapshot;
13792 delete this.snapshot;
13793 if(suppressEvent !== true){
13794 this.fireEvent("datachanged", this);
13800 afterEdit : function(record){
13801 if(this.modified.indexOf(record) == -1){
13802 this.modified.push(record);
13804 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13808 afterReject : function(record){
13809 this.modified.remove(record);
13810 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13814 afterCommit : function(record){
13815 this.modified.remove(record);
13816 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13820 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13821 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13823 commitChanges : function(){
13824 var m = this.modified.slice(0);
13825 this.modified = [];
13826 for(var i = 0, len = m.length; i < len; i++){
13832 * Cancel outstanding changes on all changed records.
13834 rejectChanges : function(){
13835 var m = this.modified.slice(0);
13836 this.modified = [];
13837 for(var i = 0, len = m.length; i < len; i++){
13842 onMetaChange : function(meta, rtype, o){
13843 this.recordType = rtype;
13844 this.fields = rtype.prototype.fields;
13845 delete this.snapshot;
13846 this.sortInfo = meta.sortInfo || this.sortInfo;
13847 this.modified = [];
13848 this.fireEvent('metachange', this, this.reader.meta);
13851 moveIndex : function(data, type)
13853 var index = this.indexOf(data);
13855 var newIndex = index + type;
13859 this.insert(newIndex, data);
13864 * Ext JS Library 1.1.1
13865 * Copyright(c) 2006-2007, Ext JS, LLC.
13867 * Originally Released Under LGPL - original licence link has changed is not relivant.
13870 * <script type="text/javascript">
13874 * @class Roo.data.SimpleStore
13875 * @extends Roo.data.Store
13876 * Small helper class to make creating Stores from Array data easier.
13877 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13878 * @cfg {Array} fields An array of field definition objects, or field name strings.
13879 * @cfg {Object} an existing reader (eg. copied from another store)
13880 * @cfg {Array} data The multi-dimensional array of data
13882 * @param {Object} config
13884 Roo.data.SimpleStore = function(config)
13886 Roo.data.SimpleStore.superclass.constructor.call(this, {
13888 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13891 Roo.data.Record.create(config.fields)
13893 proxy : new Roo.data.MemoryProxy(config.data)
13897 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13899 * Ext JS Library 1.1.1
13900 * Copyright(c) 2006-2007, Ext JS, LLC.
13902 * Originally Released Under LGPL - original licence link has changed is not relivant.
13905 * <script type="text/javascript">
13910 * @extends Roo.data.Store
13911 * @class Roo.data.JsonStore
13912 * Small helper class to make creating Stores for JSON data easier. <br/>
13914 var store = new Roo.data.JsonStore({
13915 url: 'get-images.php',
13917 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13920 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13921 * JsonReader and HttpProxy (unless inline data is provided).</b>
13922 * @cfg {Array} fields An array of field definition objects, or field name strings.
13924 * @param {Object} config
13926 Roo.data.JsonStore = function(c){
13927 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13928 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13929 reader: new Roo.data.JsonReader(c, c.fields)
13932 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13934 * Ext JS Library 1.1.1
13935 * Copyright(c) 2006-2007, Ext JS, LLC.
13937 * Originally Released Under LGPL - original licence link has changed is not relivant.
13940 * <script type="text/javascript">
13944 Roo.data.Field = function(config){
13945 if(typeof config == "string"){
13946 config = {name: config};
13948 Roo.apply(this, config);
13951 this.type = "auto";
13954 var st = Roo.data.SortTypes;
13955 // named sortTypes are supported, here we look them up
13956 if(typeof this.sortType == "string"){
13957 this.sortType = st[this.sortType];
13960 // set default sortType for strings and dates
13961 if(!this.sortType){
13964 this.sortType = st.asUCString;
13967 this.sortType = st.asDate;
13970 this.sortType = st.none;
13975 var stripRe = /[\$,%]/g;
13977 // prebuilt conversion function for this field, instead of
13978 // switching every time we're reading a value
13980 var cv, dateFormat = this.dateFormat;
13985 cv = function(v){ return v; };
13988 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13992 return v !== undefined && v !== null && v !== '' ?
13993 parseInt(String(v).replace(stripRe, ""), 10) : '';
13998 return v !== undefined && v !== null && v !== '' ?
13999 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14004 cv = function(v){ return v === true || v === "true" || v == 1; };
14011 if(v instanceof Date){
14015 if(dateFormat == "timestamp"){
14016 return new Date(v*1000);
14018 return Date.parseDate(v, dateFormat);
14020 var parsed = Date.parse(v);
14021 return parsed ? new Date(parsed) : null;
14030 Roo.data.Field.prototype = {
14038 * Ext JS Library 1.1.1
14039 * Copyright(c) 2006-2007, Ext JS, LLC.
14041 * Originally Released Under LGPL - original licence link has changed is not relivant.
14044 * <script type="text/javascript">
14047 // Base class for reading structured data from a data source. This class is intended to be
14048 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14051 * @class Roo.data.DataReader
14052 * Base class for reading structured data from a data source. This class is intended to be
14053 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14056 Roo.data.DataReader = function(meta, recordType){
14060 this.recordType = recordType instanceof Array ?
14061 Roo.data.Record.create(recordType) : recordType;
14064 Roo.data.DataReader.prototype = {
14067 readerType : 'Data',
14069 * Create an empty record
14070 * @param {Object} data (optional) - overlay some values
14071 * @return {Roo.data.Record} record created.
14073 newRow : function(d) {
14075 this.recordType.prototype.fields.each(function(c) {
14077 case 'int' : da[c.name] = 0; break;
14078 case 'date' : da[c.name] = new Date(); break;
14079 case 'float' : da[c.name] = 0.0; break;
14080 case 'boolean' : da[c.name] = false; break;
14081 default : da[c.name] = ""; break;
14085 return new this.recordType(Roo.apply(da, d));
14091 * Ext JS Library 1.1.1
14092 * Copyright(c) 2006-2007, Ext JS, LLC.
14094 * Originally Released Under LGPL - original licence link has changed is not relivant.
14097 * <script type="text/javascript">
14101 * @class Roo.data.DataProxy
14102 * @extends Roo.data.Observable
14103 * This class is an abstract base class for implementations which provide retrieval of
14104 * unformatted data objects.<br>
14106 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14107 * (of the appropriate type which knows how to parse the data object) to provide a block of
14108 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14110 * Custom implementations must implement the load method as described in
14111 * {@link Roo.data.HttpProxy#load}.
14113 Roo.data.DataProxy = function(){
14116 * @event beforeload
14117 * Fires before a network request is made to retrieve a data object.
14118 * @param {Object} This DataProxy object.
14119 * @param {Object} params The params parameter to the load function.
14124 * Fires before the load method's callback is called.
14125 * @param {Object} This DataProxy object.
14126 * @param {Object} o The data object.
14127 * @param {Object} arg The callback argument object passed to the load function.
14131 * @event loadexception
14132 * Fires if an Exception occurs during data retrieval.
14133 * @param {Object} This DataProxy object.
14134 * @param {Object} o The data object.
14135 * @param {Object} arg The callback argument object passed to the load function.
14136 * @param {Object} e The Exception.
14138 loadexception : true
14140 Roo.data.DataProxy.superclass.constructor.call(this);
14143 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14146 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14150 * Ext JS Library 1.1.1
14151 * Copyright(c) 2006-2007, Ext JS, LLC.
14153 * Originally Released Under LGPL - original licence link has changed is not relivant.
14156 * <script type="text/javascript">
14159 * @class Roo.data.MemoryProxy
14160 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14161 * to the Reader when its load method is called.
14163 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14165 Roo.data.MemoryProxy = function(data){
14169 Roo.data.MemoryProxy.superclass.constructor.call(this);
14173 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14176 * Load data from the requested source (in this case an in-memory
14177 * data object passed to the constructor), read the data object into
14178 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14179 * process that block using the passed callback.
14180 * @param {Object} params This parameter is not used by the MemoryProxy class.
14181 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14182 * object into a block of Roo.data.Records.
14183 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14184 * The function must be passed <ul>
14185 * <li>The Record block object</li>
14186 * <li>The "arg" argument from the load function</li>
14187 * <li>A boolean success indicator</li>
14189 * @param {Object} scope The scope in which to call the callback
14190 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14192 load : function(params, reader, callback, scope, arg){
14193 params = params || {};
14196 result = reader.readRecords(params.data ? params.data :this.data);
14198 this.fireEvent("loadexception", this, arg, null, e);
14199 callback.call(scope, null, arg, false);
14202 callback.call(scope, result, arg, true);
14206 update : function(params, records){
14211 * Ext JS Library 1.1.1
14212 * Copyright(c) 2006-2007, Ext JS, LLC.
14214 * Originally Released Under LGPL - original licence link has changed is not relivant.
14217 * <script type="text/javascript">
14220 * @class Roo.data.HttpProxy
14221 * @extends Roo.data.DataProxy
14222 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14223 * configured to reference a certain URL.<br><br>
14225 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14226 * from which the running page was served.<br><br>
14228 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14230 * Be aware that to enable the browser to parse an XML document, the server must set
14231 * the Content-Type header in the HTTP response to "text/xml".
14233 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14234 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14235 * will be used to make the request.
14237 Roo.data.HttpProxy = function(conn){
14238 Roo.data.HttpProxy.superclass.constructor.call(this);
14239 // is conn a conn config or a real conn?
14241 this.useAjax = !conn || !conn.events;
14245 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14246 // thse are take from connection...
14249 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14252 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14253 * extra parameters to each request made by this object. (defaults to undefined)
14256 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14257 * to each request made by this object. (defaults to undefined)
14260 * @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)
14263 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14266 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14272 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14276 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14277 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14278 * a finer-grained basis than the DataProxy events.
14280 getConnection : function(){
14281 return this.useAjax ? Roo.Ajax : this.conn;
14285 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14286 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14287 * process that block using the passed callback.
14288 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14289 * for the request to the remote server.
14290 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14291 * object into a block of Roo.data.Records.
14292 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14293 * The function must be passed <ul>
14294 * <li>The Record block object</li>
14295 * <li>The "arg" argument from the load function</li>
14296 * <li>A boolean success indicator</li>
14298 * @param {Object} scope The scope in which to call the callback
14299 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14301 load : function(params, reader, callback, scope, arg){
14302 if(this.fireEvent("beforeload", this, params) !== false){
14304 params : params || {},
14306 callback : callback,
14311 callback : this.loadResponse,
14315 Roo.applyIf(o, this.conn);
14316 if(this.activeRequest){
14317 Roo.Ajax.abort(this.activeRequest);
14319 this.activeRequest = Roo.Ajax.request(o);
14321 this.conn.request(o);
14324 callback.call(scope||this, null, arg, false);
14329 loadResponse : function(o, success, response){
14330 delete this.activeRequest;
14332 this.fireEvent("loadexception", this, o, response);
14333 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14338 result = o.reader.read(response);
14340 this.fireEvent("loadexception", this, o, response, e);
14341 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14345 this.fireEvent("load", this, o, o.request.arg);
14346 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14350 update : function(dataSet){
14355 updateResponse : function(dataSet){
14360 * Ext JS Library 1.1.1
14361 * Copyright(c) 2006-2007, Ext JS, LLC.
14363 * Originally Released Under LGPL - original licence link has changed is not relivant.
14366 * <script type="text/javascript">
14370 * @class Roo.data.ScriptTagProxy
14371 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14372 * other than the originating domain of the running page.<br><br>
14374 * <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
14375 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14377 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14378 * source code that is used as the source inside a <script> tag.<br><br>
14380 * In order for the browser to process the returned data, the server must wrap the data object
14381 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14382 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14383 * depending on whether the callback name was passed:
14386 boolean scriptTag = false;
14387 String cb = request.getParameter("callback");
14390 response.setContentType("text/javascript");
14392 response.setContentType("application/x-json");
14394 Writer out = response.getWriter();
14396 out.write(cb + "(");
14398 out.print(dataBlock.toJsonString());
14405 * @param {Object} config A configuration object.
14407 Roo.data.ScriptTagProxy = function(config){
14408 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14409 Roo.apply(this, config);
14410 this.head = document.getElementsByTagName("head")[0];
14413 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14415 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14417 * @cfg {String} url The URL from which to request the data object.
14420 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14424 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14425 * the server the name of the callback function set up by the load call to process the returned data object.
14426 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14427 * javascript output which calls this named function passing the data object as its only parameter.
14429 callbackParam : "callback",
14431 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14432 * name to the request.
14437 * Load data from the configured URL, read the data object into
14438 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14439 * process that block using the passed callback.
14440 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14441 * for the request to the remote server.
14442 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14443 * object into a block of Roo.data.Records.
14444 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14445 * The function must be passed <ul>
14446 * <li>The Record block object</li>
14447 * <li>The "arg" argument from the load function</li>
14448 * <li>A boolean success indicator</li>
14450 * @param {Object} scope The scope in which to call the callback
14451 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14453 load : function(params, reader, callback, scope, arg){
14454 if(this.fireEvent("beforeload", this, params) !== false){
14456 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14458 var url = this.url;
14459 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14461 url += "&_dc=" + (new Date().getTime());
14463 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14466 cb : "stcCallback"+transId,
14467 scriptId : "stcScript"+transId,
14471 callback : callback,
14477 window[trans.cb] = function(o){
14478 conn.handleResponse(o, trans);
14481 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14483 if(this.autoAbort !== false){
14487 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14489 var script = document.createElement("script");
14490 script.setAttribute("src", url);
14491 script.setAttribute("type", "text/javascript");
14492 script.setAttribute("id", trans.scriptId);
14493 this.head.appendChild(script);
14495 this.trans = trans;
14497 callback.call(scope||this, null, arg, false);
14502 isLoading : function(){
14503 return this.trans ? true : false;
14507 * Abort the current server request.
14509 abort : function(){
14510 if(this.isLoading()){
14511 this.destroyTrans(this.trans);
14516 destroyTrans : function(trans, isLoaded){
14517 this.head.removeChild(document.getElementById(trans.scriptId));
14518 clearTimeout(trans.timeoutId);
14520 window[trans.cb] = undefined;
14522 delete window[trans.cb];
14525 // if hasn't been loaded, wait for load to remove it to prevent script error
14526 window[trans.cb] = function(){
14527 window[trans.cb] = undefined;
14529 delete window[trans.cb];
14536 handleResponse : function(o, trans){
14537 this.trans = false;
14538 this.destroyTrans(trans, true);
14541 result = trans.reader.readRecords(o);
14543 this.fireEvent("loadexception", this, o, trans.arg, e);
14544 trans.callback.call(trans.scope||window, null, trans.arg, false);
14547 this.fireEvent("load", this, o, trans.arg);
14548 trans.callback.call(trans.scope||window, result, trans.arg, true);
14552 handleFailure : function(trans){
14553 this.trans = false;
14554 this.destroyTrans(trans, false);
14555 this.fireEvent("loadexception", this, null, trans.arg);
14556 trans.callback.call(trans.scope||window, null, trans.arg, false);
14560 * Ext JS Library 1.1.1
14561 * Copyright(c) 2006-2007, Ext JS, LLC.
14563 * Originally Released Under LGPL - original licence link has changed is not relivant.
14566 * <script type="text/javascript">
14570 * @class Roo.data.JsonReader
14571 * @extends Roo.data.DataReader
14572 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14573 * based on mappings in a provided Roo.data.Record constructor.
14575 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14576 * in the reply previously.
14581 var RecordDef = Roo.data.Record.create([
14582 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14583 {name: 'occupation'} // This field will use "occupation" as the mapping.
14585 var myReader = new Roo.data.JsonReader({
14586 totalProperty: "results", // The property which contains the total dataset size (optional)
14587 root: "rows", // The property which contains an Array of row objects
14588 id: "id" // The property within each row object that provides an ID for the record (optional)
14592 * This would consume a JSON file like this:
14594 { 'results': 2, 'rows': [
14595 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14596 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14599 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14600 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14601 * paged from the remote server.
14602 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14603 * @cfg {String} root name of the property which contains the Array of row objects.
14604 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14605 * @cfg {Array} fields Array of field definition objects
14607 * Create a new JsonReader
14608 * @param {Object} meta Metadata configuration options
14609 * @param {Object} recordType Either an Array of field definition objects,
14610 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14612 Roo.data.JsonReader = function(meta, recordType){
14615 // set some defaults:
14616 Roo.applyIf(meta, {
14617 totalProperty: 'total',
14618 successProperty : 'success',
14623 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14625 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14627 readerType : 'Json',
14630 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14631 * Used by Store query builder to append _requestMeta to params.
14634 metaFromRemote : false,
14636 * This method is only used by a DataProxy which has retrieved data from a remote server.
14637 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14638 * @return {Object} data A data block which is used by an Roo.data.Store object as
14639 * a cache of Roo.data.Records.
14641 read : function(response){
14642 var json = response.responseText;
14644 var o = /* eval:var:o */ eval("("+json+")");
14646 throw {message: "JsonReader.read: Json object not found"};
14652 this.metaFromRemote = true;
14653 this.meta = o.metaData;
14654 this.recordType = Roo.data.Record.create(o.metaData.fields);
14655 this.onMetaChange(this.meta, this.recordType, o);
14657 return this.readRecords(o);
14660 // private function a store will implement
14661 onMetaChange : function(meta, recordType, o){
14668 simpleAccess: function(obj, subsc) {
14675 getJsonAccessor: function(){
14677 return function(expr) {
14679 return(re.test(expr))
14680 ? new Function("obj", "return obj." + expr)
14685 return Roo.emptyFn;
14690 * Create a data block containing Roo.data.Records from an XML document.
14691 * @param {Object} o An object which contains an Array of row objects in the property specified
14692 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14693 * which contains the total size of the dataset.
14694 * @return {Object} data A data block which is used by an Roo.data.Store object as
14695 * a cache of Roo.data.Records.
14697 readRecords : function(o){
14699 * After any data loads, the raw JSON data is available for further custom processing.
14703 var s = this.meta, Record = this.recordType,
14704 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14706 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14708 if(s.totalProperty) {
14709 this.getTotal = this.getJsonAccessor(s.totalProperty);
14711 if(s.successProperty) {
14712 this.getSuccess = this.getJsonAccessor(s.successProperty);
14714 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14716 var g = this.getJsonAccessor(s.id);
14717 this.getId = function(rec) {
14719 return (r === undefined || r === "") ? null : r;
14722 this.getId = function(){return null;};
14725 for(var jj = 0; jj < fl; jj++){
14727 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14728 this.ef[jj] = this.getJsonAccessor(map);
14732 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14733 if(s.totalProperty){
14734 var vt = parseInt(this.getTotal(o), 10);
14739 if(s.successProperty){
14740 var vs = this.getSuccess(o);
14741 if(vs === false || vs === 'false'){
14746 for(var i = 0; i < c; i++){
14749 var id = this.getId(n);
14750 for(var j = 0; j < fl; j++){
14752 var v = this.ef[j](n);
14754 Roo.log('missing convert for ' + f.name);
14758 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14760 var record = new Record(values, id);
14762 records[i] = record;
14768 totalRecords : totalRecords
14771 // used when loading children.. @see loadDataFromChildren
14772 toLoadData: function(rec)
14774 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14775 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14776 return { data : data, total : data.length };
14781 * Ext JS Library 1.1.1
14782 * Copyright(c) 2006-2007, Ext JS, LLC.
14784 * Originally Released Under LGPL - original licence link has changed is not relivant.
14787 * <script type="text/javascript">
14791 * @class Roo.data.ArrayReader
14792 * @extends Roo.data.DataReader
14793 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14794 * Each element of that Array represents a row of data fields. The
14795 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14796 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14800 var RecordDef = Roo.data.Record.create([
14801 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14802 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14804 var myReader = new Roo.data.ArrayReader({
14805 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14809 * This would consume an Array like this:
14811 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14815 * Create a new JsonReader
14816 * @param {Object} meta Metadata configuration options.
14817 * @param {Object|Array} recordType Either an Array of field definition objects
14819 * @cfg {Array} fields Array of field definition objects
14820 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14821 * as specified to {@link Roo.data.Record#create},
14822 * or an {@link Roo.data.Record} object
14825 * created using {@link Roo.data.Record#create}.
14827 Roo.data.ArrayReader = function(meta, recordType)
14829 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14832 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14835 * Create a data block containing Roo.data.Records from an XML document.
14836 * @param {Object} o An Array of row objects which represents the dataset.
14837 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14838 * a cache of Roo.data.Records.
14840 readRecords : function(o)
14842 var sid = this.meta ? this.meta.id : null;
14843 var recordType = this.recordType, fields = recordType.prototype.fields;
14846 for(var i = 0; i < root.length; i++){
14849 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14850 for(var j = 0, jlen = fields.length; j < jlen; j++){
14851 var f = fields.items[j];
14852 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14853 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14855 values[f.name] = v;
14857 var record = new recordType(values, id);
14859 records[records.length] = record;
14863 totalRecords : records.length
14866 // used when loading children.. @see loadDataFromChildren
14867 toLoadData: function(rec)
14869 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14870 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14881 * @class Roo.bootstrap.ComboBox
14882 * @extends Roo.bootstrap.TriggerField
14883 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14884 * @cfg {Boolean} append (true|false) default false
14885 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14886 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14887 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14888 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14889 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14890 * @cfg {Boolean} animate default true
14891 * @cfg {Boolean} emptyResultText only for touch device
14892 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14893 * @cfg {String} emptyTitle default ''
14894 * @cfg {Number} width fixed with? experimental
14896 * Create a new ComboBox.
14897 * @param {Object} config Configuration options
14899 Roo.bootstrap.ComboBox = function(config){
14900 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14904 * Fires when the dropdown list is expanded
14905 * @param {Roo.bootstrap.ComboBox} combo This combo box
14910 * Fires when the dropdown list is collapsed
14911 * @param {Roo.bootstrap.ComboBox} combo This combo box
14915 * @event beforeselect
14916 * Fires before a list item is selected. Return false to cancel the selection.
14917 * @param {Roo.bootstrap.ComboBox} combo This combo box
14918 * @param {Roo.data.Record} record The data record returned from the underlying store
14919 * @param {Number} index The index of the selected item in the dropdown list
14921 'beforeselect' : true,
14924 * Fires when a list item is selected
14925 * @param {Roo.bootstrap.ComboBox} combo This combo box
14926 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14927 * @param {Number} index The index of the selected item in the dropdown list
14931 * @event beforequery
14932 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14933 * The event object passed has these properties:
14934 * @param {Roo.bootstrap.ComboBox} combo This combo box
14935 * @param {String} query The query
14936 * @param {Boolean} forceAll true to force "all" query
14937 * @param {Boolean} cancel true to cancel the query
14938 * @param {Object} e The query event object
14940 'beforequery': true,
14943 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14944 * @param {Roo.bootstrap.ComboBox} combo This combo box
14949 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14950 * @param {Roo.bootstrap.ComboBox} combo This combo box
14951 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14956 * Fires when the remove value from the combobox array
14957 * @param {Roo.bootstrap.ComboBox} combo This combo box
14961 * @event afterremove
14962 * Fires when the remove value from the combobox array
14963 * @param {Roo.bootstrap.ComboBox} combo This combo box
14965 'afterremove' : true,
14967 * @event specialfilter
14968 * Fires when specialfilter
14969 * @param {Roo.bootstrap.ComboBox} combo This combo box
14971 'specialfilter' : true,
14974 * Fires when tick the element
14975 * @param {Roo.bootstrap.ComboBox} combo This combo box
14979 * @event touchviewdisplay
14980 * Fires when touch view require special display (default is using displayField)
14981 * @param {Roo.bootstrap.ComboBox} combo This combo box
14982 * @param {Object} cfg set html .
14984 'touchviewdisplay' : true
14989 this.tickItems = [];
14991 this.selectedIndex = -1;
14992 if(this.mode == 'local'){
14993 if(config.queryDelay === undefined){
14994 this.queryDelay = 10;
14996 if(config.minChars === undefined){
15002 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15005 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15006 * rendering into an Roo.Editor, defaults to false)
15009 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15010 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15013 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15016 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15017 * the dropdown list (defaults to undefined, with no header element)
15021 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15025 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15027 listWidth: undefined,
15029 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15030 * mode = 'remote' or 'text' if mode = 'local')
15032 displayField: undefined,
15035 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15036 * mode = 'remote' or 'value' if mode = 'local').
15037 * Note: use of a valueField requires the user make a selection
15038 * in order for a value to be mapped.
15040 valueField: undefined,
15042 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15047 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15048 * field's data value (defaults to the underlying DOM element's name)
15050 hiddenName: undefined,
15052 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15056 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15058 selectedClass: 'active',
15061 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15065 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15066 * anchor positions (defaults to 'tl-bl')
15068 listAlign: 'tl-bl?',
15070 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15074 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15075 * query specified by the allQuery config option (defaults to 'query')
15077 triggerAction: 'query',
15079 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15080 * (defaults to 4, does not apply if editable = false)
15084 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15085 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15089 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15090 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15094 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15095 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15099 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15100 * when editable = true (defaults to false)
15102 selectOnFocus:false,
15104 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15106 queryParam: 'query',
15108 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15109 * when mode = 'remote' (defaults to 'Loading...')
15111 loadingText: 'Loading...',
15113 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15117 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15121 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15122 * traditional select (defaults to true)
15126 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15130 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15134 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15135 * listWidth has a higher value)
15139 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15140 * allow the user to set arbitrary text into the field (defaults to false)
15142 forceSelection:false,
15144 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15145 * if typeAhead = true (defaults to 250)
15147 typeAheadDelay : 250,
15149 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15150 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15152 valueNotFoundText : undefined,
15154 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15156 blockFocus : false,
15159 * @cfg {Boolean} disableClear Disable showing of clear button.
15161 disableClear : false,
15163 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15165 alwaysQuery : false,
15168 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15173 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15175 invalidClass : "has-warning",
15178 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15180 validClass : "has-success",
15183 * @cfg {Boolean} specialFilter (true|false) special filter default false
15185 specialFilter : false,
15188 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15190 mobileTouchView : true,
15193 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15195 useNativeIOS : false,
15198 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15200 mobile_restrict_height : false,
15202 ios_options : false,
15214 btnPosition : 'right',
15215 triggerList : true,
15216 showToggleBtn : true,
15218 emptyResultText: 'Empty',
15219 triggerText : 'Select',
15223 // element that contains real text value.. (when hidden is used..)
15225 getAutoCreate : function()
15230 * Render classic select for iso
15233 if(Roo.isIOS && this.useNativeIOS){
15234 cfg = this.getAutoCreateNativeIOS();
15242 if(Roo.isTouch && this.mobileTouchView){
15243 cfg = this.getAutoCreateTouchView();
15250 if(!this.tickable){
15251 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15256 * ComboBox with tickable selections
15259 var align = this.labelAlign || this.parentLabelAlign();
15262 cls : 'form-group roo-combobox-tickable' //input-group
15265 var btn_text_select = '';
15266 var btn_text_done = '';
15267 var btn_text_cancel = '';
15269 if (this.btn_text_show) {
15270 btn_text_select = 'Select';
15271 btn_text_done = 'Done';
15272 btn_text_cancel = 'Cancel';
15277 cls : 'tickable-buttons',
15282 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15283 //html : this.triggerText
15284 html: btn_text_select
15290 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15292 html: btn_text_done
15298 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15300 html: btn_text_cancel
15306 buttons.cn.unshift({
15308 cls: 'roo-select2-search-field-input'
15314 Roo.each(buttons.cn, function(c){
15316 c.cls += ' btn-' + _this.size;
15319 if (_this.disabled) {
15326 style : 'display: contents',
15331 cls: 'form-hidden-field'
15335 cls: 'roo-select2-choices',
15339 cls: 'roo-select2-search-field',
15350 cls: 'roo-select2-container input-group roo-select2-container-multi',
15356 // cls: 'typeahead typeahead-long dropdown-menu',
15357 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15362 if(this.hasFeedback && !this.allowBlank){
15366 cls: 'glyphicon form-control-feedback'
15369 combobox.cn.push(feedback);
15376 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15377 tooltip : 'This field is required'
15379 if (Roo.bootstrap.version == 4) {
15382 style : 'display:none'
15385 if (align ==='left' && this.fieldLabel.length) {
15387 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15394 cls : 'control-label col-form-label',
15395 html : this.fieldLabel
15407 var labelCfg = cfg.cn[1];
15408 var contentCfg = cfg.cn[2];
15411 if(this.indicatorpos == 'right'){
15417 cls : 'control-label col-form-label',
15421 html : this.fieldLabel
15437 labelCfg = cfg.cn[0];
15438 contentCfg = cfg.cn[1];
15442 if(this.labelWidth > 12){
15443 labelCfg.style = "width: " + this.labelWidth + 'px';
15445 if(this.width * 1 > 0){
15446 contentCfg.style = "width: " + this.width + 'px';
15448 if(this.labelWidth < 13 && this.labelmd == 0){
15449 this.labelmd = this.labelWidth;
15452 if(this.labellg > 0){
15453 labelCfg.cls += ' col-lg-' + this.labellg;
15454 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15457 if(this.labelmd > 0){
15458 labelCfg.cls += ' col-md-' + this.labelmd;
15459 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15462 if(this.labelsm > 0){
15463 labelCfg.cls += ' col-sm-' + this.labelsm;
15464 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15467 if(this.labelxs > 0){
15468 labelCfg.cls += ' col-xs-' + this.labelxs;
15469 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15473 } else if ( this.fieldLabel.length) {
15474 // Roo.log(" label");
15479 //cls : 'input-group-addon',
15480 html : this.fieldLabel
15485 if(this.indicatorpos == 'right'){
15489 //cls : 'input-group-addon',
15490 html : this.fieldLabel
15500 // Roo.log(" no label && no align");
15507 ['xs','sm','md','lg'].map(function(size){
15508 if (settings[size]) {
15509 cfg.cls += ' col-' + size + '-' + settings[size];
15517 _initEventsCalled : false,
15520 initEvents: function()
15522 if (this._initEventsCalled) { // as we call render... prevent looping...
15525 this._initEventsCalled = true;
15528 throw "can not find store for combo";
15531 this.indicator = this.indicatorEl();
15533 this.store = Roo.factory(this.store, Roo.data);
15534 this.store.parent = this;
15536 // if we are building from html. then this element is so complex, that we can not really
15537 // use the rendered HTML.
15538 // so we have to trash and replace the previous code.
15539 if (Roo.XComponent.build_from_html) {
15540 // remove this element....
15541 var e = this.el.dom, k=0;
15542 while (e ) { e = e.previousSibling; ++k;}
15547 this.rendered = false;
15549 this.render(this.parent().getChildContainer(true), k);
15552 if(Roo.isIOS && this.useNativeIOS){
15553 this.initIOSView();
15561 if(Roo.isTouch && this.mobileTouchView){
15562 this.initTouchView();
15567 this.initTickableEvents();
15571 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15573 if(this.hiddenName){
15575 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15577 this.hiddenField.dom.value =
15578 this.hiddenValue !== undefined ? this.hiddenValue :
15579 this.value !== undefined ? this.value : '';
15581 // prevent input submission
15582 this.el.dom.removeAttribute('name');
15583 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15588 // this.el.dom.setAttribute('autocomplete', 'off');
15591 var cls = 'x-combo-list';
15593 //this.list = new Roo.Layer({
15594 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15600 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15601 _this.list.setWidth(lw);
15604 this.list.on('mouseover', this.onViewOver, this);
15605 this.list.on('mousemove', this.onViewMove, this);
15606 this.list.on('scroll', this.onViewScroll, this);
15609 this.list.swallowEvent('mousewheel');
15610 this.assetHeight = 0;
15613 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15614 this.assetHeight += this.header.getHeight();
15617 this.innerList = this.list.createChild({cls:cls+'-inner'});
15618 this.innerList.on('mouseover', this.onViewOver, this);
15619 this.innerList.on('mousemove', this.onViewMove, this);
15620 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15622 if(this.allowBlank && !this.pageSize && !this.disableClear){
15623 this.footer = this.list.createChild({cls:cls+'-ft'});
15624 this.pageTb = new Roo.Toolbar(this.footer);
15628 this.footer = this.list.createChild({cls:cls+'-ft'});
15629 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15630 {pageSize: this.pageSize});
15634 if (this.pageTb && this.allowBlank && !this.disableClear) {
15636 this.pageTb.add(new Roo.Toolbar.Fill(), {
15637 cls: 'x-btn-icon x-btn-clear',
15639 handler: function()
15642 _this.clearValue();
15643 _this.onSelect(false, -1);
15648 this.assetHeight += this.footer.getHeight();
15653 this.tpl = Roo.bootstrap.version == 4 ?
15654 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15655 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15658 this.view = new Roo.View(this.list, this.tpl, {
15659 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15661 //this.view.wrapEl.setDisplayed(false);
15662 this.view.on('click', this.onViewClick, this);
15665 this.store.on('beforeload', this.onBeforeLoad, this);
15666 this.store.on('load', this.onLoad, this);
15667 this.store.on('loadexception', this.onLoadException, this);
15669 if(this.resizable){
15670 this.resizer = new Roo.Resizable(this.list, {
15671 pinned:true, handles:'se'
15673 this.resizer.on('resize', function(r, w, h){
15674 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15675 this.listWidth = w;
15676 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15677 this.restrictHeight();
15679 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15682 if(!this.editable){
15683 this.editable = true;
15684 this.setEditable(false);
15689 if (typeof(this.events.add.listeners) != 'undefined') {
15691 this.addicon = this.wrap.createChild(
15692 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15694 this.addicon.on('click', function(e) {
15695 this.fireEvent('add', this);
15698 if (typeof(this.events.edit.listeners) != 'undefined') {
15700 this.editicon = this.wrap.createChild(
15701 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15702 if (this.addicon) {
15703 this.editicon.setStyle('margin-left', '40px');
15705 this.editicon.on('click', function(e) {
15707 // we fire even if inothing is selected..
15708 this.fireEvent('edit', this, this.lastData );
15714 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15715 "up" : function(e){
15716 this.inKeyMode = true;
15720 "down" : function(e){
15721 if(!this.isExpanded()){
15722 this.onTriggerClick();
15724 this.inKeyMode = true;
15729 "enter" : function(e){
15730 // this.onViewClick();
15734 if(this.fireEvent("specialkey", this, e)){
15735 this.onViewClick(false);
15741 "esc" : function(e){
15745 "tab" : function(e){
15748 if(this.fireEvent("specialkey", this, e)){
15749 this.onViewClick(false);
15757 doRelay : function(foo, bar, hname){
15758 if(hname == 'down' || this.scope.isExpanded()){
15759 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15768 this.queryDelay = Math.max(this.queryDelay || 10,
15769 this.mode == 'local' ? 10 : 250);
15772 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15774 if(this.typeAhead){
15775 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15777 if(this.editable !== false){
15778 this.inputEl().on("keyup", this.onKeyUp, this);
15780 if(this.forceSelection){
15781 this.inputEl().on('blur', this.doForce, this);
15785 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15786 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15790 initTickableEvents: function()
15794 if(this.hiddenName){
15796 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15798 this.hiddenField.dom.value =
15799 this.hiddenValue !== undefined ? this.hiddenValue :
15800 this.value !== undefined ? this.value : '';
15802 // prevent input submission
15803 this.el.dom.removeAttribute('name');
15804 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15809 // this.list = this.el.select('ul.dropdown-menu',true).first();
15811 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15812 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15813 if(this.triggerList){
15814 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15817 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15818 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15820 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15821 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15823 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15824 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15826 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15827 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15828 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15831 this.cancelBtn.hide();
15836 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15837 _this.list.setWidth(lw);
15840 this.list.on('mouseover', this.onViewOver, this);
15841 this.list.on('mousemove', this.onViewMove, this);
15843 this.list.on('scroll', this.onViewScroll, this);
15846 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15847 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15850 this.view = new Roo.View(this.list, this.tpl, {
15855 selectedClass: this.selectedClass
15858 //this.view.wrapEl.setDisplayed(false);
15859 this.view.on('click', this.onViewClick, this);
15863 this.store.on('beforeload', this.onBeforeLoad, this);
15864 this.store.on('load', this.onLoad, this);
15865 this.store.on('loadexception', this.onLoadException, this);
15868 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15869 "up" : function(e){
15870 this.inKeyMode = true;
15874 "down" : function(e){
15875 this.inKeyMode = true;
15879 "enter" : function(e){
15880 if(this.fireEvent("specialkey", this, e)){
15881 this.onViewClick(false);
15887 "esc" : function(e){
15888 this.onTickableFooterButtonClick(e, false, false);
15891 "tab" : function(e){
15892 this.fireEvent("specialkey", this, e);
15894 this.onTickableFooterButtonClick(e, false, false);
15901 doRelay : function(e, fn, key){
15902 if(this.scope.isExpanded()){
15903 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15912 this.queryDelay = Math.max(this.queryDelay || 10,
15913 this.mode == 'local' ? 10 : 250);
15916 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15918 if(this.typeAhead){
15919 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15922 if(this.editable !== false){
15923 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15926 this.indicator = this.indicatorEl();
15928 if(this.indicator){
15929 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15930 this.indicator.hide();
15935 onDestroy : function(){
15937 this.view.setStore(null);
15938 this.view.el.removeAllListeners();
15939 this.view.el.remove();
15940 this.view.purgeListeners();
15943 this.list.dom.innerHTML = '';
15947 this.store.un('beforeload', this.onBeforeLoad, this);
15948 this.store.un('load', this.onLoad, this);
15949 this.store.un('loadexception', this.onLoadException, this);
15951 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15955 fireKey : function(e){
15956 if(e.isNavKeyPress() && !this.list.isVisible()){
15957 this.fireEvent("specialkey", this, e);
15962 onResize: function(w, h)
15966 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15968 // if(typeof w != 'number'){
15969 // // we do not handle it!?!?
15972 // var tw = this.trigger.getWidth();
15973 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15974 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15976 // this.inputEl().setWidth( this.adjustWidth('input', x));
15978 // //this.trigger.setStyle('left', x+'px');
15980 // if(this.list && this.listWidth === undefined){
15981 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15982 // this.list.setWidth(lw);
15983 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15991 * Allow or prevent the user from directly editing the field text. If false is passed,
15992 * the user will only be able to select from the items defined in the dropdown list. This method
15993 * is the runtime equivalent of setting the 'editable' config option at config time.
15994 * @param {Boolean} value True to allow the user to directly edit the field text
15996 setEditable : function(value){
15997 if(value == this.editable){
16000 this.editable = value;
16002 this.inputEl().dom.setAttribute('readOnly', true);
16003 this.inputEl().on('mousedown', this.onTriggerClick, this);
16004 this.inputEl().addClass('x-combo-noedit');
16006 this.inputEl().dom.setAttribute('readOnly', false);
16007 this.inputEl().un('mousedown', this.onTriggerClick, this);
16008 this.inputEl().removeClass('x-combo-noedit');
16014 onBeforeLoad : function(combo,opts){
16015 if(!this.hasFocus){
16019 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16021 this.restrictHeight();
16022 this.selectedIndex = -1;
16026 onLoad : function(){
16028 this.hasQuery = false;
16030 if(!this.hasFocus){
16034 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16035 this.loading.hide();
16038 if(this.store.getCount() > 0){
16041 this.restrictHeight();
16042 if(this.lastQuery == this.allQuery){
16043 if(this.editable && !this.tickable){
16044 this.inputEl().dom.select();
16048 !this.selectByValue(this.value, true) &&
16051 !this.store.lastOptions ||
16052 typeof(this.store.lastOptions.add) == 'undefined' ||
16053 this.store.lastOptions.add != true
16056 this.select(0, true);
16059 if(this.autoFocus){
16062 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16063 this.taTask.delay(this.typeAheadDelay);
16067 this.onEmptyResults();
16073 onLoadException : function()
16075 this.hasQuery = false;
16077 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16078 this.loading.hide();
16081 if(this.tickable && this.editable){
16086 // only causes errors at present
16087 //Roo.log(this.store.reader.jsonData);
16088 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16090 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16096 onTypeAhead : function(){
16097 if(this.store.getCount() > 0){
16098 var r = this.store.getAt(0);
16099 var newValue = r.data[this.displayField];
16100 var len = newValue.length;
16101 var selStart = this.getRawValue().length;
16103 if(selStart != len){
16104 this.setRawValue(newValue);
16105 this.selectText(selStart, newValue.length);
16111 onSelect : function(record, index){
16113 if(this.fireEvent('beforeselect', this, record, index) !== false){
16115 this.setFromData(index > -1 ? record.data : false);
16118 this.fireEvent('select', this, record, index);
16123 * Returns the currently selected field value or empty string if no value is set.
16124 * @return {String} value The selected value
16126 getValue : function()
16128 if(Roo.isIOS && this.useNativeIOS){
16129 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16133 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16136 if(this.valueField){
16137 return typeof this.value != 'undefined' ? this.value : '';
16139 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16143 getRawValue : function()
16145 if(Roo.isIOS && this.useNativeIOS){
16146 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16149 var v = this.inputEl().getValue();
16155 * Clears any text/value currently set in the field
16157 clearValue : function(){
16159 if(this.hiddenField){
16160 this.hiddenField.dom.value = '';
16163 this.setRawValue('');
16164 this.lastSelectionText = '';
16165 this.lastData = false;
16167 var close = this.closeTriggerEl();
16178 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16179 * will be displayed in the field. If the value does not match the data value of an existing item,
16180 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16181 * Otherwise the field will be blank (although the value will still be set).
16182 * @param {String} value The value to match
16184 setValue : function(v)
16186 if(Roo.isIOS && this.useNativeIOS){
16187 this.setIOSValue(v);
16197 if(this.valueField){
16198 var r = this.findRecord(this.valueField, v);
16200 text = r.data[this.displayField];
16201 }else if(this.valueNotFoundText !== undefined){
16202 text = this.valueNotFoundText;
16205 this.lastSelectionText = text;
16206 if(this.hiddenField){
16207 this.hiddenField.dom.value = v;
16209 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16212 var close = this.closeTriggerEl();
16215 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16221 * @property {Object} the last set data for the element
16226 * Sets the value of the field based on a object which is related to the record format for the store.
16227 * @param {Object} value the value to set as. or false on reset?
16229 setFromData : function(o){
16236 var dv = ''; // display value
16237 var vv = ''; // value value..
16239 if (this.displayField) {
16240 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16242 // this is an error condition!!!
16243 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16246 if(this.valueField){
16247 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16250 var close = this.closeTriggerEl();
16253 if(dv.length || vv * 1 > 0){
16255 this.blockFocus=true;
16261 if(this.hiddenField){
16262 this.hiddenField.dom.value = vv;
16264 this.lastSelectionText = dv;
16265 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16269 // no hidden field.. - we store the value in 'value', but still display
16270 // display field!!!!
16271 this.lastSelectionText = dv;
16272 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16279 reset : function(){
16280 // overridden so that last data is reset..
16287 this.setValue(this.originalValue);
16288 //this.clearInvalid();
16289 this.lastData = false;
16291 this.view.clearSelections();
16297 findRecord : function(prop, value){
16299 if(this.store.getCount() > 0){
16300 this.store.each(function(r){
16301 if(r.data[prop] == value){
16311 getName: function()
16313 // returns hidden if it's set..
16314 if (!this.rendered) {return ''};
16315 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16319 onViewMove : function(e, t){
16320 this.inKeyMode = false;
16324 onViewOver : function(e, t){
16325 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16328 var item = this.view.findItemFromChild(t);
16331 var index = this.view.indexOf(item);
16332 this.select(index, false);
16337 onViewClick : function(view, doFocus, el, e)
16339 var index = this.view.getSelectedIndexes()[0];
16341 var r = this.store.getAt(index);
16345 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16352 Roo.each(this.tickItems, function(v,k){
16354 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16356 _this.tickItems.splice(k, 1);
16358 if(typeof(e) == 'undefined' && view == false){
16359 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16371 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16372 this.tickItems.push(r.data);
16375 if(typeof(e) == 'undefined' && view == false){
16376 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16383 this.onSelect(r, index);
16385 if(doFocus !== false && !this.blockFocus){
16386 this.inputEl().focus();
16391 restrictHeight : function(){
16392 //this.innerList.dom.style.height = '';
16393 //var inner = this.innerList.dom;
16394 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16395 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16396 //this.list.beginUpdate();
16397 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16398 this.list.alignTo(this.inputEl(), this.listAlign);
16399 this.list.alignTo(this.inputEl(), this.listAlign);
16400 //this.list.endUpdate();
16404 onEmptyResults : function(){
16406 if(this.tickable && this.editable){
16407 this.hasFocus = false;
16408 this.restrictHeight();
16416 * Returns true if the dropdown list is expanded, else false.
16418 isExpanded : function(){
16419 return this.list.isVisible();
16423 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16424 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16425 * @param {String} value The data value of the item to select
16426 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16427 * selected item if it is not currently in view (defaults to true)
16428 * @return {Boolean} True if the value matched an item in the list, else false
16430 selectByValue : function(v, scrollIntoView){
16431 if(v !== undefined && v !== null){
16432 var r = this.findRecord(this.valueField || this.displayField, v);
16434 this.select(this.store.indexOf(r), scrollIntoView);
16442 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16443 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16444 * @param {Number} index The zero-based index of the list item to select
16445 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16446 * selected item if it is not currently in view (defaults to true)
16448 select : function(index, scrollIntoView){
16449 this.selectedIndex = index;
16450 this.view.select(index);
16451 if(scrollIntoView !== false){
16452 var el = this.view.getNode(index);
16454 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16457 this.list.scrollChildIntoView(el, false);
16463 selectNext : function(){
16464 var ct = this.store.getCount();
16466 if(this.selectedIndex == -1){
16468 }else if(this.selectedIndex < ct-1){
16469 this.select(this.selectedIndex+1);
16475 selectPrev : function(){
16476 var ct = this.store.getCount();
16478 if(this.selectedIndex == -1){
16480 }else if(this.selectedIndex != 0){
16481 this.select(this.selectedIndex-1);
16487 onKeyUp : function(e){
16488 if(this.editable !== false && !e.isSpecialKey()){
16489 this.lastKey = e.getKey();
16490 this.dqTask.delay(this.queryDelay);
16495 validateBlur : function(){
16496 return !this.list || !this.list.isVisible();
16500 initQuery : function(){
16502 var v = this.getRawValue();
16504 if(this.tickable && this.editable){
16505 v = this.tickableInputEl().getValue();
16512 doForce : function(){
16513 if(this.inputEl().dom.value.length > 0){
16514 this.inputEl().dom.value =
16515 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16521 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16522 * query allowing the query action to be canceled if needed.
16523 * @param {String} query The SQL query to execute
16524 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16525 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16526 * saved in the current store (defaults to false)
16528 doQuery : function(q, forceAll){
16530 if(q === undefined || q === null){
16535 forceAll: forceAll,
16539 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16544 forceAll = qe.forceAll;
16545 if(forceAll === true || (q.length >= this.minChars)){
16547 this.hasQuery = true;
16549 if(this.lastQuery != q || this.alwaysQuery){
16550 this.lastQuery = q;
16551 if(this.mode == 'local'){
16552 this.selectedIndex = -1;
16554 this.store.clearFilter();
16557 if(this.specialFilter){
16558 this.fireEvent('specialfilter', this);
16563 this.store.filter(this.displayField, q);
16566 this.store.fireEvent("datachanged", this.store);
16573 this.store.baseParams[this.queryParam] = q;
16575 var options = {params : this.getParams(q)};
16578 options.add = true;
16579 options.params.start = this.page * this.pageSize;
16582 this.store.load(options);
16585 * this code will make the page width larger, at the beginning, the list not align correctly,
16586 * we should expand the list on onLoad
16587 * so command out it
16592 this.selectedIndex = -1;
16597 this.loadNext = false;
16601 getParams : function(q){
16603 //p[this.queryParam] = q;
16607 p.limit = this.pageSize;
16613 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16615 collapse : function(){
16616 if(!this.isExpanded()){
16622 this.hasFocus = false;
16626 this.cancelBtn.hide();
16627 this.trigger.show();
16630 this.tickableInputEl().dom.value = '';
16631 this.tickableInputEl().blur();
16636 Roo.get(document).un('mousedown', this.collapseIf, this);
16637 Roo.get(document).un('mousewheel', this.collapseIf, this);
16638 if (!this.editable) {
16639 Roo.get(document).un('keydown', this.listKeyPress, this);
16641 this.fireEvent('collapse', this);
16647 collapseIf : function(e){
16648 var in_combo = e.within(this.el);
16649 var in_list = e.within(this.list);
16650 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16652 if (in_combo || in_list || is_list) {
16653 //e.stopPropagation();
16658 this.onTickableFooterButtonClick(e, false, false);
16666 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16668 expand : function(){
16670 if(this.isExpanded() || !this.hasFocus){
16674 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16675 this.list.setWidth(lw);
16681 this.restrictHeight();
16685 this.tickItems = Roo.apply([], this.item);
16688 this.cancelBtn.show();
16689 this.trigger.hide();
16692 this.tickableInputEl().focus();
16697 Roo.get(document).on('mousedown', this.collapseIf, this);
16698 Roo.get(document).on('mousewheel', this.collapseIf, this);
16699 if (!this.editable) {
16700 Roo.get(document).on('keydown', this.listKeyPress, this);
16703 this.fireEvent('expand', this);
16707 // Implements the default empty TriggerField.onTriggerClick function
16708 onTriggerClick : function(e)
16710 Roo.log('trigger click');
16712 if(this.disabled || !this.triggerList){
16717 this.loadNext = false;
16719 if(this.isExpanded()){
16721 if (!this.blockFocus) {
16722 this.inputEl().focus();
16726 this.hasFocus = true;
16727 if(this.triggerAction == 'all') {
16728 this.doQuery(this.allQuery, true);
16730 this.doQuery(this.getRawValue());
16732 if (!this.blockFocus) {
16733 this.inputEl().focus();
16738 onTickableTriggerClick : function(e)
16745 this.loadNext = false;
16746 this.hasFocus = true;
16748 if(this.triggerAction == 'all') {
16749 this.doQuery(this.allQuery, true);
16751 this.doQuery(this.getRawValue());
16755 onSearchFieldClick : function(e)
16757 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16758 this.onTickableFooterButtonClick(e, false, false);
16762 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16767 this.loadNext = false;
16768 this.hasFocus = true;
16770 if(this.triggerAction == 'all') {
16771 this.doQuery(this.allQuery, true);
16773 this.doQuery(this.getRawValue());
16777 listKeyPress : function(e)
16779 //Roo.log('listkeypress');
16780 // scroll to first matching element based on key pres..
16781 if (e.isSpecialKey()) {
16784 var k = String.fromCharCode(e.getKey()).toUpperCase();
16787 var csel = this.view.getSelectedNodes();
16788 var cselitem = false;
16790 var ix = this.view.indexOf(csel[0]);
16791 cselitem = this.store.getAt(ix);
16792 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16798 this.store.each(function(v) {
16800 // start at existing selection.
16801 if (cselitem.id == v.id) {
16807 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16808 match = this.store.indexOf(v);
16814 if (match === false) {
16815 return true; // no more action?
16818 this.view.select(match);
16819 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16820 sn.scrollIntoView(sn.dom.parentNode, false);
16823 onViewScroll : function(e, t){
16825 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){
16829 this.hasQuery = true;
16831 this.loading = this.list.select('.loading', true).first();
16833 if(this.loading === null){
16834 this.list.createChild({
16836 cls: 'loading roo-select2-more-results roo-select2-active',
16837 html: 'Loading more results...'
16840 this.loading = this.list.select('.loading', true).first();
16842 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16844 this.loading.hide();
16847 this.loading.show();
16852 this.loadNext = true;
16854 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16859 addItem : function(o)
16861 var dv = ''; // display value
16863 if (this.displayField) {
16864 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16866 // this is an error condition!!!
16867 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16874 var choice = this.choices.createChild({
16876 cls: 'roo-select2-search-choice',
16885 cls: 'roo-select2-search-choice-close fa fa-times',
16890 }, this.searchField);
16892 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16894 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16902 this.inputEl().dom.value = '';
16907 onRemoveItem : function(e, _self, o)
16909 e.preventDefault();
16911 this.lastItem = Roo.apply([], this.item);
16913 var index = this.item.indexOf(o.data) * 1;
16916 Roo.log('not this item?!');
16920 this.item.splice(index, 1);
16925 this.fireEvent('remove', this, e);
16931 syncValue : function()
16933 if(!this.item.length){
16940 Roo.each(this.item, function(i){
16941 if(_this.valueField){
16942 value.push(i[_this.valueField]);
16949 this.value = value.join(',');
16951 if(this.hiddenField){
16952 this.hiddenField.dom.value = this.value;
16955 this.store.fireEvent("datachanged", this.store);
16960 clearItem : function()
16962 if(!this.multiple){
16968 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16976 if(this.tickable && !Roo.isTouch){
16977 this.view.refresh();
16981 inputEl: function ()
16983 if(Roo.isIOS && this.useNativeIOS){
16984 return this.el.select('select.roo-ios-select', true).first();
16987 if(Roo.isTouch && this.mobileTouchView){
16988 return this.el.select('input.form-control',true).first();
16992 return this.searchField;
16995 return this.el.select('input.form-control',true).first();
16998 onTickableFooterButtonClick : function(e, btn, el)
17000 e.preventDefault();
17002 this.lastItem = Roo.apply([], this.item);
17004 if(btn && btn.name == 'cancel'){
17005 this.tickItems = Roo.apply([], this.item);
17014 Roo.each(this.tickItems, function(o){
17022 validate : function()
17024 if(this.getVisibilityEl().hasClass('hidden')){
17028 var v = this.getRawValue();
17031 v = this.getValue();
17034 if(this.disabled || this.allowBlank || v.length){
17039 this.markInvalid();
17043 tickableInputEl : function()
17045 if(!this.tickable || !this.editable){
17046 return this.inputEl();
17049 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17053 getAutoCreateTouchView : function()
17058 cls: 'form-group' //input-group
17064 type : this.inputType,
17065 cls : 'form-control x-combo-noedit',
17066 autocomplete: 'new-password',
17067 placeholder : this.placeholder || '',
17072 input.name = this.name;
17076 input.cls += ' input-' + this.size;
17079 if (this.disabled) {
17080 input.disabled = true;
17084 cls : 'roo-combobox-wrap',
17091 inputblock.cls += ' input-group';
17093 inputblock.cn.unshift({
17095 cls : 'input-group-addon input-group-prepend input-group-text',
17100 if(this.removable && !this.multiple){
17101 inputblock.cls += ' roo-removable';
17103 inputblock.cn.push({
17106 cls : 'roo-combo-removable-btn close'
17110 if(this.hasFeedback && !this.allowBlank){
17112 inputblock.cls += ' has-feedback';
17114 inputblock.cn.push({
17116 cls: 'glyphicon form-control-feedback'
17123 inputblock.cls += (this.before) ? '' : ' input-group';
17125 inputblock.cn.push({
17127 cls : 'input-group-addon input-group-append input-group-text',
17133 var ibwrap = inputblock;
17138 cls: 'roo-select2-choices',
17142 cls: 'roo-select2-search-field',
17155 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17160 cls: 'form-hidden-field'
17166 if(!this.multiple && this.showToggleBtn){
17172 if (this.caret != false) {
17175 cls: 'fa fa-' + this.caret
17182 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17184 Roo.bootstrap.version == 3 ? caret : '',
17187 cls: 'combobox-clear',
17201 combobox.cls += ' roo-select2-container-multi';
17204 var align = this.labelAlign || this.parentLabelAlign();
17206 if (align ==='left' && this.fieldLabel.length) {
17211 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17212 tooltip : 'This field is required'
17216 cls : 'control-label col-form-label',
17217 html : this.fieldLabel
17221 cls : 'roo-combobox-wrap ',
17228 var labelCfg = cfg.cn[1];
17229 var contentCfg = cfg.cn[2];
17232 if(this.indicatorpos == 'right'){
17237 cls : 'control-label col-form-label',
17241 html : this.fieldLabel
17245 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17246 tooltip : 'This field is required'
17251 cls : "roo-combobox-wrap ",
17259 labelCfg = cfg.cn[0];
17260 contentCfg = cfg.cn[1];
17265 if(this.labelWidth > 12){
17266 labelCfg.style = "width: " + this.labelWidth + 'px';
17269 if(this.labelWidth < 13 && this.labelmd == 0){
17270 this.labelmd = this.labelWidth;
17273 if(this.labellg > 0){
17274 labelCfg.cls += ' col-lg-' + this.labellg;
17275 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17278 if(this.labelmd > 0){
17279 labelCfg.cls += ' col-md-' + this.labelmd;
17280 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17283 if(this.labelsm > 0){
17284 labelCfg.cls += ' col-sm-' + this.labelsm;
17285 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17288 if(this.labelxs > 0){
17289 labelCfg.cls += ' col-xs-' + this.labelxs;
17290 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17294 } else if ( this.fieldLabel.length) {
17298 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17299 tooltip : 'This field is required'
17303 cls : 'control-label',
17304 html : this.fieldLabel
17315 if(this.indicatorpos == 'right'){
17319 cls : 'control-label',
17320 html : this.fieldLabel,
17324 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17325 tooltip : 'This field is required'
17342 var settings = this;
17344 ['xs','sm','md','lg'].map(function(size){
17345 if (settings[size]) {
17346 cfg.cls += ' col-' + size + '-' + settings[size];
17353 initTouchView : function()
17355 this.renderTouchView();
17357 this.touchViewEl.on('scroll', function(){
17358 this.el.dom.scrollTop = 0;
17361 this.originalValue = this.getValue();
17363 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17365 this.inputEl().on("click", this.showTouchView, this);
17366 if (this.triggerEl) {
17367 this.triggerEl.on("click", this.showTouchView, this);
17371 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17372 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17374 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17376 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17377 this.store.on('load', this.onTouchViewLoad, this);
17378 this.store.on('loadexception', this.onTouchViewLoadException, this);
17380 if(this.hiddenName){
17382 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17384 this.hiddenField.dom.value =
17385 this.hiddenValue !== undefined ? this.hiddenValue :
17386 this.value !== undefined ? this.value : '';
17388 this.el.dom.removeAttribute('name');
17389 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17393 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17394 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17397 if(this.removable && !this.multiple){
17398 var close = this.closeTriggerEl();
17400 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17401 close.on('click', this.removeBtnClick, this, close);
17405 * fix the bug in Safari iOS8
17407 this.inputEl().on("focus", function(e){
17408 document.activeElement.blur();
17411 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17418 renderTouchView : function()
17420 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17421 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17423 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17424 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17426 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17427 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17428 this.touchViewBodyEl.setStyle('overflow', 'auto');
17430 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17431 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17433 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17434 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17438 showTouchView : function()
17444 this.touchViewHeaderEl.hide();
17446 if(this.modalTitle.length){
17447 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17448 this.touchViewHeaderEl.show();
17451 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17452 this.touchViewEl.show();
17454 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17456 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17457 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17459 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17461 if(this.modalTitle.length){
17462 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17465 this.touchViewBodyEl.setHeight(bodyHeight);
17469 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17471 this.touchViewEl.addClass(['in','show']);
17474 if(this._touchViewMask){
17475 Roo.get(document.body).addClass("x-body-masked");
17476 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17477 this._touchViewMask.setStyle('z-index', 10000);
17478 this._touchViewMask.addClass('show');
17481 this.doTouchViewQuery();
17485 hideTouchView : function()
17487 this.touchViewEl.removeClass(['in','show']);
17491 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17493 this.touchViewEl.setStyle('display', 'none');
17496 if(this._touchViewMask){
17497 this._touchViewMask.removeClass('show');
17498 Roo.get(document.body).removeClass("x-body-masked");
17502 setTouchViewValue : function()
17509 Roo.each(this.tickItems, function(o){
17514 this.hideTouchView();
17517 doTouchViewQuery : function()
17526 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17530 if(!this.alwaysQuery || this.mode == 'local'){
17531 this.onTouchViewLoad();
17538 onTouchViewBeforeLoad : function(combo,opts)
17544 onTouchViewLoad : function()
17546 if(this.store.getCount() < 1){
17547 this.onTouchViewEmptyResults();
17551 this.clearTouchView();
17553 var rawValue = this.getRawValue();
17555 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17557 this.tickItems = [];
17559 this.store.data.each(function(d, rowIndex){
17560 var row = this.touchViewListGroup.createChild(template);
17562 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17563 row.addClass(d.data.cls);
17566 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17569 html : d.data[this.displayField]
17572 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17573 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17576 row.removeClass('selected');
17577 if(!this.multiple && this.valueField &&
17578 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17581 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17582 row.addClass('selected');
17585 if(this.multiple && this.valueField &&
17586 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17590 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591 this.tickItems.push(d.data);
17594 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17598 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17600 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17602 if(this.modalTitle.length){
17603 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17606 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17608 if(this.mobile_restrict_height && listHeight < bodyHeight){
17609 this.touchViewBodyEl.setHeight(listHeight);
17614 if(firstChecked && listHeight > bodyHeight){
17615 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17620 onTouchViewLoadException : function()
17622 this.hideTouchView();
17625 onTouchViewEmptyResults : function()
17627 this.clearTouchView();
17629 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17631 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17635 clearTouchView : function()
17637 this.touchViewListGroup.dom.innerHTML = '';
17640 onTouchViewClick : function(e, el, o)
17642 e.preventDefault();
17645 var rowIndex = o.rowIndex;
17647 var r = this.store.getAt(rowIndex);
17649 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17651 if(!this.multiple){
17652 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17653 c.dom.removeAttribute('checked');
17656 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17658 this.setFromData(r.data);
17660 var close = this.closeTriggerEl();
17666 this.hideTouchView();
17668 this.fireEvent('select', this, r, rowIndex);
17673 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17674 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17675 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17679 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17680 this.addItem(r.data);
17681 this.tickItems.push(r.data);
17685 getAutoCreateNativeIOS : function()
17688 cls: 'form-group' //input-group,
17693 cls : 'roo-ios-select'
17697 combobox.name = this.name;
17700 if (this.disabled) {
17701 combobox.disabled = true;
17704 var settings = this;
17706 ['xs','sm','md','lg'].map(function(size){
17707 if (settings[size]) {
17708 cfg.cls += ' col-' + size + '-' + settings[size];
17718 initIOSView : function()
17720 this.store.on('load', this.onIOSViewLoad, this);
17725 onIOSViewLoad : function()
17727 if(this.store.getCount() < 1){
17731 this.clearIOSView();
17733 if(this.allowBlank) {
17735 var default_text = '-- SELECT --';
17737 if(this.placeholder.length){
17738 default_text = this.placeholder;
17741 if(this.emptyTitle.length){
17742 default_text += ' - ' + this.emptyTitle + ' -';
17745 var opt = this.inputEl().createChild({
17748 html : default_text
17752 o[this.valueField] = 0;
17753 o[this.displayField] = default_text;
17755 this.ios_options.push({
17762 this.store.data.each(function(d, rowIndex){
17766 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17767 html = d.data[this.displayField];
17772 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17773 value = d.data[this.valueField];
17782 if(this.value == d.data[this.valueField]){
17783 option['selected'] = true;
17786 var opt = this.inputEl().createChild(option);
17788 this.ios_options.push({
17795 this.inputEl().on('change', function(){
17796 this.fireEvent('select', this);
17801 clearIOSView: function()
17803 this.inputEl().dom.innerHTML = '';
17805 this.ios_options = [];
17808 setIOSValue: function(v)
17812 if(!this.ios_options){
17816 Roo.each(this.ios_options, function(opts){
17818 opts.el.dom.removeAttribute('selected');
17820 if(opts.data[this.valueField] != v){
17824 opts.el.dom.setAttribute('selected', true);
17830 * @cfg {Boolean} grow
17834 * @cfg {Number} growMin
17838 * @cfg {Number} growMax
17847 Roo.apply(Roo.bootstrap.ComboBox, {
17851 cls: 'modal-header',
17873 cls: 'list-group-item',
17877 cls: 'roo-combobox-list-group-item-value'
17881 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17895 listItemCheckbox : {
17897 cls: 'list-group-item',
17901 cls: 'roo-combobox-list-group-item-value'
17905 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17921 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17926 cls: 'modal-footer',
17934 cls: 'col-xs-6 text-left',
17937 cls: 'btn btn-danger roo-touch-view-cancel',
17943 cls: 'col-xs-6 text-right',
17946 cls: 'btn btn-success roo-touch-view-ok',
17957 Roo.apply(Roo.bootstrap.ComboBox, {
17959 touchViewTemplate : {
17961 cls: 'modal fade roo-combobox-touch-view',
17965 cls: 'modal-dialog',
17966 style : 'position:fixed', // we have to fix position....
17970 cls: 'modal-content',
17972 Roo.bootstrap.ComboBox.header,
17973 Roo.bootstrap.ComboBox.body,
17974 Roo.bootstrap.ComboBox.footer
17983 * Ext JS Library 1.1.1
17984 * Copyright(c) 2006-2007, Ext JS, LLC.
17986 * Originally Released Under LGPL - original licence link has changed is not relivant.
17989 * <script type="text/javascript">
17994 * @extends Roo.util.Observable
17995 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17996 * This class also supports single and multi selection modes. <br>
17997 * Create a data model bound view:
17999 var store = new Roo.data.Store(...);
18001 var view = new Roo.View({
18003 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18005 singleSelect: true,
18006 selectedClass: "ydataview-selected",
18010 // listen for node click?
18011 view.on("click", function(vw, index, node, e){
18012 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18016 dataModel.load("foobar.xml");
18018 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18020 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18021 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18023 * Note: old style constructor is still suported (container, template, config)
18026 * Create a new View
18027 * @param {Object} config The config object
18030 Roo.View = function(config, depreciated_tpl, depreciated_config){
18032 this.parent = false;
18034 if (typeof(depreciated_tpl) == 'undefined') {
18035 // new way.. - universal constructor.
18036 Roo.apply(this, config);
18037 this.el = Roo.get(this.el);
18040 this.el = Roo.get(config);
18041 this.tpl = depreciated_tpl;
18042 Roo.apply(this, depreciated_config);
18044 this.wrapEl = this.el.wrap().wrap();
18045 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18048 if(typeof(this.tpl) == "string"){
18049 this.tpl = new Roo.Template(this.tpl);
18051 // support xtype ctors..
18052 this.tpl = new Roo.factory(this.tpl, Roo);
18056 this.tpl.compile();
18061 * @event beforeclick
18062 * Fires before a click is processed. Returns false to cancel the default action.
18063 * @param {Roo.View} this
18064 * @param {Number} index The index of the target node
18065 * @param {HTMLElement} node The target node
18066 * @param {Roo.EventObject} e The raw event object
18068 "beforeclick" : true,
18071 * Fires when a template node is clicked.
18072 * @param {Roo.View} this
18073 * @param {Number} index The index of the target node
18074 * @param {HTMLElement} node The target node
18075 * @param {Roo.EventObject} e The raw event object
18080 * Fires when a template node is double clicked.
18081 * @param {Roo.View} this
18082 * @param {Number} index The index of the target node
18083 * @param {HTMLElement} node The target node
18084 * @param {Roo.EventObject} e The raw event object
18088 * @event contextmenu
18089 * Fires when a template node is right clicked.
18090 * @param {Roo.View} this
18091 * @param {Number} index The index of the target node
18092 * @param {HTMLElement} node The target node
18093 * @param {Roo.EventObject} e The raw event object
18095 "contextmenu" : true,
18097 * @event selectionchange
18098 * Fires when the selected nodes change.
18099 * @param {Roo.View} this
18100 * @param {Array} selections Array of the selected nodes
18102 "selectionchange" : true,
18105 * @event beforeselect
18106 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18107 * @param {Roo.View} this
18108 * @param {HTMLElement} node The node to be selected
18109 * @param {Array} selections Array of currently selected nodes
18111 "beforeselect" : true,
18113 * @event preparedata
18114 * Fires on every row to render, to allow you to change the data.
18115 * @param {Roo.View} this
18116 * @param {Object} data to be rendered (change this)
18118 "preparedata" : true
18126 "click": this.onClick,
18127 "dblclick": this.onDblClick,
18128 "contextmenu": this.onContextMenu,
18132 this.selections = [];
18134 this.cmp = new Roo.CompositeElementLite([]);
18136 this.store = Roo.factory(this.store, Roo.data);
18137 this.setStore(this.store, true);
18140 if ( this.footer && this.footer.xtype) {
18142 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18144 this.footer.dataSource = this.store;
18145 this.footer.container = fctr;
18146 this.footer = Roo.factory(this.footer, Roo);
18147 fctr.insertFirst(this.el);
18149 // this is a bit insane - as the paging toolbar seems to detach the el..
18150 // dom.parentNode.parentNode.parentNode
18151 // they get detached?
18155 Roo.View.superclass.constructor.call(this);
18160 Roo.extend(Roo.View, Roo.util.Observable, {
18163 * @cfg {Roo.data.Store} store Data store to load data from.
18168 * @cfg {String|Roo.Element} el The container element.
18173 * @cfg {String|Roo.Template} tpl The template used by this View
18177 * @cfg {String} dataName the named area of the template to use as the data area
18178 * Works with domtemplates roo-name="name"
18182 * @cfg {String} selectedClass The css class to add to selected nodes
18184 selectedClass : "x-view-selected",
18186 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18191 * @cfg {String} text to display on mask (default Loading)
18195 * @cfg {Boolean} multiSelect Allow multiple selection
18197 multiSelect : false,
18199 * @cfg {Boolean} singleSelect Allow single selection
18201 singleSelect: false,
18204 * @cfg {Boolean} toggleSelect - selecting
18206 toggleSelect : false,
18209 * @cfg {Boolean} tickable - selecting
18214 * Returns the element this view is bound to.
18215 * @return {Roo.Element}
18217 getEl : function(){
18218 return this.wrapEl;
18224 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18226 refresh : function(){
18227 //Roo.log('refresh');
18230 // if we are using something like 'domtemplate', then
18231 // the what gets used is:
18232 // t.applySubtemplate(NAME, data, wrapping data..)
18233 // the outer template then get' applied with
18234 // the store 'extra data'
18235 // and the body get's added to the
18236 // roo-name="data" node?
18237 // <span class='roo-tpl-{name}'></span> ?????
18241 this.clearSelections();
18242 this.el.update("");
18244 var records = this.store.getRange();
18245 if(records.length < 1) {
18247 // is this valid?? = should it render a template??
18249 this.el.update(this.emptyText);
18253 if (this.dataName) {
18254 this.el.update(t.apply(this.store.meta)); //????
18255 el = this.el.child('.roo-tpl-' + this.dataName);
18258 for(var i = 0, len = records.length; i < len; i++){
18259 var data = this.prepareData(records[i].data, i, records[i]);
18260 this.fireEvent("preparedata", this, data, i, records[i]);
18262 var d = Roo.apply({}, data);
18265 Roo.apply(d, {'roo-id' : Roo.id()});
18269 Roo.each(this.parent.item, function(item){
18270 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18273 Roo.apply(d, {'roo-data-checked' : 'checked'});
18277 html[html.length] = Roo.util.Format.trim(
18279 t.applySubtemplate(this.dataName, d, this.store.meta) :
18286 el.update(html.join(""));
18287 this.nodes = el.dom.childNodes;
18288 this.updateIndexes(0);
18293 * Function to override to reformat the data that is sent to
18294 * the template for each node.
18295 * DEPRICATED - use the preparedata event handler.
18296 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18297 * a JSON object for an UpdateManager bound view).
18299 prepareData : function(data, index, record)
18301 this.fireEvent("preparedata", this, data, index, record);
18305 onUpdate : function(ds, record){
18306 // Roo.log('on update');
18307 this.clearSelections();
18308 var index = this.store.indexOf(record);
18309 var n = this.nodes[index];
18310 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18311 n.parentNode.removeChild(n);
18312 this.updateIndexes(index, index);
18318 onAdd : function(ds, records, index)
18320 //Roo.log(['on Add', ds, records, index] );
18321 this.clearSelections();
18322 if(this.nodes.length == 0){
18326 var n = this.nodes[index];
18327 for(var i = 0, len = records.length; i < len; i++){
18328 var d = this.prepareData(records[i].data, i, records[i]);
18330 this.tpl.insertBefore(n, d);
18333 this.tpl.append(this.el, d);
18336 this.updateIndexes(index);
18339 onRemove : function(ds, record, index){
18340 // Roo.log('onRemove');
18341 this.clearSelections();
18342 var el = this.dataName ?
18343 this.el.child('.roo-tpl-' + this.dataName) :
18346 el.dom.removeChild(this.nodes[index]);
18347 this.updateIndexes(index);
18351 * Refresh an individual node.
18352 * @param {Number} index
18354 refreshNode : function(index){
18355 this.onUpdate(this.store, this.store.getAt(index));
18358 updateIndexes : function(startIndex, endIndex){
18359 var ns = this.nodes;
18360 startIndex = startIndex || 0;
18361 endIndex = endIndex || ns.length - 1;
18362 for(var i = startIndex; i <= endIndex; i++){
18363 ns[i].nodeIndex = i;
18368 * Changes the data store this view uses and refresh the view.
18369 * @param {Store} store
18371 setStore : function(store, initial){
18372 if(!initial && this.store){
18373 this.store.un("datachanged", this.refresh);
18374 this.store.un("add", this.onAdd);
18375 this.store.un("remove", this.onRemove);
18376 this.store.un("update", this.onUpdate);
18377 this.store.un("clear", this.refresh);
18378 this.store.un("beforeload", this.onBeforeLoad);
18379 this.store.un("load", this.onLoad);
18380 this.store.un("loadexception", this.onLoad);
18384 store.on("datachanged", this.refresh, this);
18385 store.on("add", this.onAdd, this);
18386 store.on("remove", this.onRemove, this);
18387 store.on("update", this.onUpdate, this);
18388 store.on("clear", this.refresh, this);
18389 store.on("beforeload", this.onBeforeLoad, this);
18390 store.on("load", this.onLoad, this);
18391 store.on("loadexception", this.onLoad, this);
18399 * onbeforeLoad - masks the loading area.
18402 onBeforeLoad : function(store,opts)
18404 //Roo.log('onBeforeLoad');
18406 this.el.update("");
18408 this.el.mask(this.mask ? this.mask : "Loading" );
18410 onLoad : function ()
18417 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18418 * @param {HTMLElement} node
18419 * @return {HTMLElement} The template node
18421 findItemFromChild : function(node){
18422 var el = this.dataName ?
18423 this.el.child('.roo-tpl-' + this.dataName,true) :
18426 if(!node || node.parentNode == el){
18429 var p = node.parentNode;
18430 while(p && p != el){
18431 if(p.parentNode == el){
18440 onClick : function(e){
18441 var item = this.findItemFromChild(e.getTarget());
18443 var index = this.indexOf(item);
18444 if(this.onItemClick(item, index, e) !== false){
18445 this.fireEvent("click", this, index, item, e);
18448 this.clearSelections();
18453 onContextMenu : function(e){
18454 var item = this.findItemFromChild(e.getTarget());
18456 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18461 onDblClick : function(e){
18462 var item = this.findItemFromChild(e.getTarget());
18464 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18468 onItemClick : function(item, index, e)
18470 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18473 if (this.toggleSelect) {
18474 var m = this.isSelected(item) ? 'unselect' : 'select';
18477 _t[m](item, true, false);
18480 if(this.multiSelect || this.singleSelect){
18481 if(this.multiSelect && e.shiftKey && this.lastSelection){
18482 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18484 this.select(item, this.multiSelect && e.ctrlKey);
18485 this.lastSelection = item;
18488 if(!this.tickable){
18489 e.preventDefault();
18497 * Get the number of selected nodes.
18500 getSelectionCount : function(){
18501 return this.selections.length;
18505 * Get the currently selected nodes.
18506 * @return {Array} An array of HTMLElements
18508 getSelectedNodes : function(){
18509 return this.selections;
18513 * Get the indexes of the selected nodes.
18516 getSelectedIndexes : function(){
18517 var indexes = [], s = this.selections;
18518 for(var i = 0, len = s.length; i < len; i++){
18519 indexes.push(s[i].nodeIndex);
18525 * Clear all selections
18526 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18528 clearSelections : function(suppressEvent){
18529 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18530 this.cmp.elements = this.selections;
18531 this.cmp.removeClass(this.selectedClass);
18532 this.selections = [];
18533 if(!suppressEvent){
18534 this.fireEvent("selectionchange", this, this.selections);
18540 * Returns true if the passed node is selected
18541 * @param {HTMLElement/Number} node The node or node index
18542 * @return {Boolean}
18544 isSelected : function(node){
18545 var s = this.selections;
18549 node = this.getNode(node);
18550 return s.indexOf(node) !== -1;
18555 * @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
18556 * @param {Boolean} keepExisting (optional) true to keep existing selections
18557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18559 select : function(nodeInfo, keepExisting, suppressEvent){
18560 if(nodeInfo instanceof Array){
18562 this.clearSelections(true);
18564 for(var i = 0, len = nodeInfo.length; i < len; i++){
18565 this.select(nodeInfo[i], true, true);
18569 var node = this.getNode(nodeInfo);
18570 if(!node || this.isSelected(node)){
18571 return; // already selected.
18574 this.clearSelections(true);
18577 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18578 Roo.fly(node).addClass(this.selectedClass);
18579 this.selections.push(node);
18580 if(!suppressEvent){
18581 this.fireEvent("selectionchange", this, this.selections);
18589 * @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
18590 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18591 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18593 unselect : function(nodeInfo, keepExisting, suppressEvent)
18595 if(nodeInfo instanceof Array){
18596 Roo.each(this.selections, function(s) {
18597 this.unselect(s, nodeInfo);
18601 var node = this.getNode(nodeInfo);
18602 if(!node || !this.isSelected(node)){
18603 //Roo.log("not selected");
18604 return; // not selected.
18608 Roo.each(this.selections, function(s) {
18610 Roo.fly(node).removeClass(this.selectedClass);
18617 this.selections= ns;
18618 this.fireEvent("selectionchange", this, this.selections);
18622 * Gets a template node.
18623 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18624 * @return {HTMLElement} The node or null if it wasn't found
18626 getNode : function(nodeInfo){
18627 if(typeof nodeInfo == "string"){
18628 return document.getElementById(nodeInfo);
18629 }else if(typeof nodeInfo == "number"){
18630 return this.nodes[nodeInfo];
18636 * Gets a range template nodes.
18637 * @param {Number} startIndex
18638 * @param {Number} endIndex
18639 * @return {Array} An array of nodes
18641 getNodes : function(start, end){
18642 var ns = this.nodes;
18643 start = start || 0;
18644 end = typeof end == "undefined" ? ns.length - 1 : end;
18647 for(var i = start; i <= end; i++){
18651 for(var i = start; i >= end; i--){
18659 * Finds the index of the passed node
18660 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18661 * @return {Number} The index of the node or -1
18663 indexOf : function(node){
18664 node = this.getNode(node);
18665 if(typeof node.nodeIndex == "number"){
18666 return node.nodeIndex;
18668 var ns = this.nodes;
18669 for(var i = 0, len = ns.length; i < len; i++){
18680 * based on jquery fullcalendar
18684 Roo.bootstrap = Roo.bootstrap || {};
18686 * @class Roo.bootstrap.Calendar
18687 * @extends Roo.bootstrap.Component
18688 * Bootstrap Calendar class
18689 * @cfg {Boolean} loadMask (true|false) default false
18690 * @cfg {Object} header generate the user specific header of the calendar, default false
18693 * Create a new Container
18694 * @param {Object} config The config object
18699 Roo.bootstrap.Calendar = function(config){
18700 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18704 * Fires when a date is selected
18705 * @param {DatePicker} this
18706 * @param {Date} date The selected date
18710 * @event monthchange
18711 * Fires when the displayed month changes
18712 * @param {DatePicker} this
18713 * @param {Date} date The selected month
18715 'monthchange': true,
18717 * @event evententer
18718 * Fires when mouse over an event
18719 * @param {Calendar} this
18720 * @param {event} Event
18722 'evententer': true,
18724 * @event eventleave
18725 * Fires when the mouse leaves an
18726 * @param {Calendar} this
18729 'eventleave': true,
18731 * @event eventclick
18732 * Fires when the mouse click an
18733 * @param {Calendar} this
18742 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18745 * @cfg {Number} startDay
18746 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18754 getAutoCreate : function(){
18757 var fc_button = function(name, corner, style, content ) {
18758 return Roo.apply({},{
18760 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18762 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18765 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18776 style : 'width:100%',
18783 cls : 'fc-header-left',
18785 fc_button('prev', 'left', 'arrow', '‹' ),
18786 fc_button('next', 'right', 'arrow', '›' ),
18787 { tag: 'span', cls: 'fc-header-space' },
18788 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18796 cls : 'fc-header-center',
18800 cls: 'fc-header-title',
18803 html : 'month / year'
18811 cls : 'fc-header-right',
18813 /* fc_button('month', 'left', '', 'month' ),
18814 fc_button('week', '', '', 'week' ),
18815 fc_button('day', 'right', '', 'day' )
18827 header = this.header;
18830 var cal_heads = function() {
18832 // fixme - handle this.
18834 for (var i =0; i < Date.dayNames.length; i++) {
18835 var d = Date.dayNames[i];
18838 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18839 html : d.substring(0,3)
18843 ret[0].cls += ' fc-first';
18844 ret[6].cls += ' fc-last';
18847 var cal_cell = function(n) {
18850 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18855 cls: 'fc-day-number',
18859 cls: 'fc-day-content',
18863 style: 'position: relative;' // height: 17px;
18875 var cal_rows = function() {
18878 for (var r = 0; r < 6; r++) {
18885 for (var i =0; i < Date.dayNames.length; i++) {
18886 var d = Date.dayNames[i];
18887 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18890 row.cn[0].cls+=' fc-first';
18891 row.cn[0].cn[0].style = 'min-height:90px';
18892 row.cn[6].cls+=' fc-last';
18896 ret[0].cls += ' fc-first';
18897 ret[4].cls += ' fc-prev-last';
18898 ret[5].cls += ' fc-last';
18905 cls: 'fc-border-separate',
18906 style : 'width:100%',
18914 cls : 'fc-first fc-last',
18932 cls : 'fc-content',
18933 style : "position: relative;",
18936 cls : 'fc-view fc-view-month fc-grid',
18937 style : 'position: relative',
18938 unselectable : 'on',
18941 cls : 'fc-event-container',
18942 style : 'position:absolute;z-index:8;top:0;left:0;'
18960 initEvents : function()
18963 throw "can not find store for calendar";
18969 style: "text-align:center",
18973 style: "background-color:white;width:50%;margin:250 auto",
18977 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18988 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18990 var size = this.el.select('.fc-content', true).first().getSize();
18991 this.maskEl.setSize(size.width, size.height);
18992 this.maskEl.enableDisplayMode("block");
18993 if(!this.loadMask){
18994 this.maskEl.hide();
18997 this.store = Roo.factory(this.store, Roo.data);
18998 this.store.on('load', this.onLoad, this);
18999 this.store.on('beforeload', this.onBeforeLoad, this);
19003 this.cells = this.el.select('.fc-day',true);
19004 //Roo.log(this.cells);
19005 this.textNodes = this.el.query('.fc-day-number');
19006 this.cells.addClassOnOver('fc-state-hover');
19008 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19009 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19010 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19011 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19013 this.on('monthchange', this.onMonthChange, this);
19015 this.update(new Date().clearTime());
19018 resize : function() {
19019 var sz = this.el.getSize();
19021 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19022 this.el.select('.fc-day-content div',true).setHeight(34);
19027 showPrevMonth : function(e){
19028 this.update(this.activeDate.add("mo", -1));
19030 showToday : function(e){
19031 this.update(new Date().clearTime());
19034 showNextMonth : function(e){
19035 this.update(this.activeDate.add("mo", 1));
19039 showPrevYear : function(){
19040 this.update(this.activeDate.add("y", -1));
19044 showNextYear : function(){
19045 this.update(this.activeDate.add("y", 1));
19050 update : function(date)
19052 var vd = this.activeDate;
19053 this.activeDate = date;
19054 // if(vd && this.el){
19055 // var t = date.getTime();
19056 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19057 // Roo.log('using add remove');
19059 // this.fireEvent('monthchange', this, date);
19061 // this.cells.removeClass("fc-state-highlight");
19062 // this.cells.each(function(c){
19063 // if(c.dateValue == t){
19064 // c.addClass("fc-state-highlight");
19065 // setTimeout(function(){
19066 // try{c.dom.firstChild.focus();}catch(e){}
19076 var days = date.getDaysInMonth();
19078 var firstOfMonth = date.getFirstDateOfMonth();
19079 var startingPos = firstOfMonth.getDay()-this.startDay;
19081 if(startingPos < this.startDay){
19085 var pm = date.add(Date.MONTH, -1);
19086 var prevStart = pm.getDaysInMonth()-startingPos;
19088 this.cells = this.el.select('.fc-day',true);
19089 this.textNodes = this.el.query('.fc-day-number');
19090 this.cells.addClassOnOver('fc-state-hover');
19092 var cells = this.cells.elements;
19093 var textEls = this.textNodes;
19095 Roo.each(cells, function(cell){
19096 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19099 days += startingPos;
19101 // convert everything to numbers so it's fast
19102 var day = 86400000;
19103 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19106 //Roo.log(prevStart);
19108 var today = new Date().clearTime().getTime();
19109 var sel = date.clearTime().getTime();
19110 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19111 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19112 var ddMatch = this.disabledDatesRE;
19113 var ddText = this.disabledDatesText;
19114 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19115 var ddaysText = this.disabledDaysText;
19116 var format = this.format;
19118 var setCellClass = function(cal, cell){
19122 //Roo.log('set Cell Class');
19124 var t = d.getTime();
19128 cell.dateValue = t;
19130 cell.className += " fc-today";
19131 cell.className += " fc-state-highlight";
19132 cell.title = cal.todayText;
19135 // disable highlight in other month..
19136 //cell.className += " fc-state-highlight";
19141 cell.className = " fc-state-disabled";
19142 cell.title = cal.minText;
19146 cell.className = " fc-state-disabled";
19147 cell.title = cal.maxText;
19151 if(ddays.indexOf(d.getDay()) != -1){
19152 cell.title = ddaysText;
19153 cell.className = " fc-state-disabled";
19156 if(ddMatch && format){
19157 var fvalue = d.dateFormat(format);
19158 if(ddMatch.test(fvalue)){
19159 cell.title = ddText.replace("%0", fvalue);
19160 cell.className = " fc-state-disabled";
19164 if (!cell.initialClassName) {
19165 cell.initialClassName = cell.dom.className;
19168 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19173 for(; i < startingPos; i++) {
19174 textEls[i].innerHTML = (++prevStart);
19175 d.setDate(d.getDate()+1);
19177 cells[i].className = "fc-past fc-other-month";
19178 setCellClass(this, cells[i]);
19183 for(; i < days; i++){
19184 intDay = i - startingPos + 1;
19185 textEls[i].innerHTML = (intDay);
19186 d.setDate(d.getDate()+1);
19188 cells[i].className = ''; // "x-date-active";
19189 setCellClass(this, cells[i]);
19193 for(; i < 42; i++) {
19194 textEls[i].innerHTML = (++extraDays);
19195 d.setDate(d.getDate()+1);
19197 cells[i].className = "fc-future fc-other-month";
19198 setCellClass(this, cells[i]);
19201 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19203 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19205 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19206 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19208 if(totalRows != 6){
19209 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19210 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19213 this.fireEvent('monthchange', this, date);
19217 if(!this.internalRender){
19218 var main = this.el.dom.firstChild;
19219 var w = main.offsetWidth;
19220 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19221 Roo.fly(main).setWidth(w);
19222 this.internalRender = true;
19223 // opera does not respect the auto grow header center column
19224 // then, after it gets a width opera refuses to recalculate
19225 // without a second pass
19226 if(Roo.isOpera && !this.secondPass){
19227 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19228 this.secondPass = true;
19229 this.update.defer(10, this, [date]);
19236 findCell : function(dt) {
19237 dt = dt.clearTime().getTime();
19239 this.cells.each(function(c){
19240 //Roo.log("check " +c.dateValue + '?=' + dt);
19241 if(c.dateValue == dt){
19251 findCells : function(ev) {
19252 var s = ev.start.clone().clearTime().getTime();
19254 var e= ev.end.clone().clearTime().getTime();
19257 this.cells.each(function(c){
19258 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19260 if(c.dateValue > e){
19263 if(c.dateValue < s){
19272 // findBestRow: function(cells)
19276 // for (var i =0 ; i < cells.length;i++) {
19277 // ret = Math.max(cells[i].rows || 0,ret);
19284 addItem : function(ev)
19286 // look for vertical location slot in
19287 var cells = this.findCells(ev);
19289 // ev.row = this.findBestRow(cells);
19291 // work out the location.
19295 for(var i =0; i < cells.length; i++) {
19297 cells[i].row = cells[0].row;
19300 cells[i].row = cells[i].row + 1;
19310 if (crow.start.getY() == cells[i].getY()) {
19312 crow.end = cells[i];
19329 cells[0].events.push(ev);
19331 this.calevents.push(ev);
19334 clearEvents: function() {
19336 if(!this.calevents){
19340 Roo.each(this.cells.elements, function(c){
19346 Roo.each(this.calevents, function(e) {
19347 Roo.each(e.els, function(el) {
19348 el.un('mouseenter' ,this.onEventEnter, this);
19349 el.un('mouseleave' ,this.onEventLeave, this);
19354 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19360 renderEvents: function()
19364 this.cells.each(function(c) {
19373 if(c.row != c.events.length){
19374 r = 4 - (4 - (c.row - c.events.length));
19377 c.events = ev.slice(0, r);
19378 c.more = ev.slice(r);
19380 if(c.more.length && c.more.length == 1){
19381 c.events.push(c.more.pop());
19384 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19388 this.cells.each(function(c) {
19390 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19393 for (var e = 0; e < c.events.length; e++){
19394 var ev = c.events[e];
19395 var rows = ev.rows;
19397 for(var i = 0; i < rows.length; i++) {
19399 // how many rows should it span..
19402 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19403 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19405 unselectable : "on",
19408 cls: 'fc-event-inner',
19412 // cls: 'fc-event-time',
19413 // html : cells.length > 1 ? '' : ev.time
19417 cls: 'fc-event-title',
19418 html : String.format('{0}', ev.title)
19425 cls: 'ui-resizable-handle ui-resizable-e',
19426 html : '  '
19433 cfg.cls += ' fc-event-start';
19435 if ((i+1) == rows.length) {
19436 cfg.cls += ' fc-event-end';
19439 var ctr = _this.el.select('.fc-event-container',true).first();
19440 var cg = ctr.createChild(cfg);
19442 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19443 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19445 var r = (c.more.length) ? 1 : 0;
19446 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19447 cg.setWidth(ebox.right - sbox.x -2);
19449 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19450 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19451 cg.on('click', _this.onEventClick, _this, ev);
19462 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19463 style : 'position: absolute',
19464 unselectable : "on",
19467 cls: 'fc-event-inner',
19471 cls: 'fc-event-title',
19479 cls: 'ui-resizable-handle ui-resizable-e',
19480 html : '  '
19486 var ctr = _this.el.select('.fc-event-container',true).first();
19487 var cg = ctr.createChild(cfg);
19489 var sbox = c.select('.fc-day-content',true).first().getBox();
19490 var ebox = c.select('.fc-day-content',true).first().getBox();
19492 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19493 cg.setWidth(ebox.right - sbox.x -2);
19495 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505 onEventEnter: function (e, el,event,d) {
19506 this.fireEvent('evententer', this, el, event);
19509 onEventLeave: function (e, el,event,d) {
19510 this.fireEvent('eventleave', this, el, event);
19513 onEventClick: function (e, el,event,d) {
19514 this.fireEvent('eventclick', this, el, event);
19517 onMonthChange: function () {
19521 onMoreEventClick: function(e, el, more)
19525 this.calpopover.placement = 'right';
19526 this.calpopover.setTitle('More');
19528 this.calpopover.setContent('');
19530 var ctr = this.calpopover.el.select('.popover-content', true).first();
19532 Roo.each(more, function(m){
19534 cls : 'fc-event-hori fc-event-draggable',
19537 var cg = ctr.createChild(cfg);
19539 cg.on('click', _this.onEventClick, _this, m);
19542 this.calpopover.show(el);
19547 onLoad: function ()
19549 this.calevents = [];
19552 if(this.store.getCount() > 0){
19553 this.store.data.each(function(d){
19556 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19557 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19558 time : d.data.start_time,
19559 title : d.data.title,
19560 description : d.data.description,
19561 venue : d.data.venue
19566 this.renderEvents();
19568 if(this.calevents.length && this.loadMask){
19569 this.maskEl.hide();
19573 onBeforeLoad: function()
19575 this.clearEvents();
19577 this.maskEl.show();
19591 * @class Roo.bootstrap.Popover
19592 * @extends Roo.bootstrap.Component
19593 * Bootstrap Popover class
19594 * @cfg {String} html contents of the popover (or false to use children..)
19595 * @cfg {String} title of popover (or false to hide)
19596 * @cfg {String} placement how it is placed
19597 * @cfg {String} trigger click || hover (or false to trigger manually)
19598 * @cfg {String} over what (parent or false to trigger manually.)
19599 * @cfg {Number} delay - delay before showing
19602 * Create a new Popover
19603 * @param {Object} config The config object
19606 Roo.bootstrap.Popover = function(config){
19607 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19613 * After the popover show
19615 * @param {Roo.bootstrap.Popover} this
19620 * After the popover hide
19622 * @param {Roo.bootstrap.Popover} this
19628 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19630 title: 'Fill in a title',
19633 placement : 'right',
19634 trigger : 'hover', // hover
19640 can_build_overlaid : false,
19642 getChildContainer : function()
19644 return this.el.select('.popover-content',true).first();
19647 getAutoCreate : function(){
19650 cls : 'popover roo-dynamic',
19651 style: 'display:block',
19657 cls : 'popover-inner',
19661 cls: 'popover-title popover-header',
19665 cls : 'popover-content popover-body',
19676 setTitle: function(str)
19679 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19681 setContent: function(str)
19684 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19686 // as it get's added to the bottom of the page.
19687 onRender : function(ct, position)
19689 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19691 var cfg = Roo.apply({}, this.getAutoCreate());
19695 cfg.cls += ' ' + this.cls;
19698 cfg.style = this.style;
19700 //Roo.log("adding to ");
19701 this.el = Roo.get(document.body).createChild(cfg, position);
19702 // Roo.log(this.el);
19707 initEvents : function()
19709 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19710 this.el.enableDisplayMode('block');
19712 if (this.over === false) {
19715 if (this.triggers === false) {
19718 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19719 var triggers = this.trigger ? this.trigger.split(' ') : [];
19720 Roo.each(triggers, function(trigger) {
19722 if (trigger == 'click') {
19723 on_el.on('click', this.toggle, this);
19724 } else if (trigger != 'manual') {
19725 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19726 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19728 on_el.on(eventIn ,this.enter, this);
19729 on_el.on(eventOut, this.leave, this);
19740 toggle : function () {
19741 this.hoverState == 'in' ? this.leave() : this.enter();
19744 enter : function () {
19746 clearTimeout(this.timeout);
19748 this.hoverState = 'in';
19750 if (!this.delay || !this.delay.show) {
19755 this.timeout = setTimeout(function () {
19756 if (_t.hoverState == 'in') {
19759 }, this.delay.show)
19762 leave : function() {
19763 clearTimeout(this.timeout);
19765 this.hoverState = 'out';
19767 if (!this.delay || !this.delay.hide) {
19772 this.timeout = setTimeout(function () {
19773 if (_t.hoverState == 'out') {
19776 }, this.delay.hide)
19779 show : function (on_el)
19782 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19786 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19787 if (this.html !== false) {
19788 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19790 this.el.removeClass([
19791 'fade','top','bottom', 'left', 'right','in',
19792 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19794 if (!this.title.length) {
19795 this.el.select('.popover-title',true).hide();
19798 var placement = typeof this.placement == 'function' ?
19799 this.placement.call(this, this.el, on_el) :
19802 var autoToken = /\s?auto?\s?/i;
19803 var autoPlace = autoToken.test(placement);
19805 placement = placement.replace(autoToken, '') || 'top';
19809 //this.el.setXY([0,0]);
19811 this.el.dom.style.display='block';
19812 this.el.addClass(placement);
19814 //this.el.appendTo(on_el);
19816 var p = this.getPosition();
19817 var box = this.el.getBox();
19822 var align = Roo.bootstrap.Popover.alignment[placement];
19825 this.el.alignTo(on_el, align[0],align[1]);
19826 //var arrow = this.el.select('.arrow',true).first();
19827 //arrow.set(align[2],
19829 this.el.addClass('in');
19832 if (this.el.hasClass('fade')) {
19836 this.hoverState = 'in';
19838 this.fireEvent('show', this);
19843 this.el.setXY([0,0]);
19844 this.el.removeClass('in');
19846 this.hoverState = null;
19848 this.fireEvent('hide', this);
19853 Roo.bootstrap.Popover.alignment = {
19854 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19855 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19856 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19857 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19868 * @class Roo.bootstrap.Progress
19869 * @extends Roo.bootstrap.Component
19870 * Bootstrap Progress class
19871 * @cfg {Boolean} striped striped of the progress bar
19872 * @cfg {Boolean} active animated of the progress bar
19876 * Create a new Progress
19877 * @param {Object} config The config object
19880 Roo.bootstrap.Progress = function(config){
19881 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19884 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19889 getAutoCreate : function(){
19897 cfg.cls += ' progress-striped';
19901 cfg.cls += ' active';
19920 * @class Roo.bootstrap.ProgressBar
19921 * @extends Roo.bootstrap.Component
19922 * Bootstrap ProgressBar class
19923 * @cfg {Number} aria_valuenow aria-value now
19924 * @cfg {Number} aria_valuemin aria-value min
19925 * @cfg {Number} aria_valuemax aria-value max
19926 * @cfg {String} label label for the progress bar
19927 * @cfg {String} panel (success | info | warning | danger )
19928 * @cfg {String} role role of the progress bar
19929 * @cfg {String} sr_only text
19933 * Create a new ProgressBar
19934 * @param {Object} config The config object
19937 Roo.bootstrap.ProgressBar = function(config){
19938 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19941 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19945 aria_valuemax : 100,
19951 getAutoCreate : function()
19956 cls: 'progress-bar',
19957 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19969 cfg.role = this.role;
19972 if(this.aria_valuenow){
19973 cfg['aria-valuenow'] = this.aria_valuenow;
19976 if(this.aria_valuemin){
19977 cfg['aria-valuemin'] = this.aria_valuemin;
19980 if(this.aria_valuemax){
19981 cfg['aria-valuemax'] = this.aria_valuemax;
19984 if(this.label && !this.sr_only){
19985 cfg.html = this.label;
19989 cfg.cls += ' progress-bar-' + this.panel;
19995 update : function(aria_valuenow)
19997 this.aria_valuenow = aria_valuenow;
19999 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20014 * @class Roo.bootstrap.TabGroup
20015 * @extends Roo.bootstrap.Column
20016 * Bootstrap Column class
20017 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20018 * @cfg {Boolean} carousel true to make the group behave like a carousel
20019 * @cfg {Boolean} bullets show bullets for the panels
20020 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20021 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20022 * @cfg {Boolean} showarrow (true|false) show arrow default true
20025 * Create a new TabGroup
20026 * @param {Object} config The config object
20029 Roo.bootstrap.TabGroup = function(config){
20030 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20032 this.navId = Roo.id();
20035 Roo.bootstrap.TabGroup.register(this);
20039 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20042 transition : false,
20047 slideOnTouch : false,
20050 getAutoCreate : function()
20052 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20054 cfg.cls += ' tab-content';
20056 if (this.carousel) {
20057 cfg.cls += ' carousel slide';
20060 cls : 'carousel-inner',
20064 if(this.bullets && !Roo.isTouch){
20067 cls : 'carousel-bullets',
20071 if(this.bullets_cls){
20072 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20079 cfg.cn[0].cn.push(bullets);
20082 if(this.showarrow){
20083 cfg.cn[0].cn.push({
20085 class : 'carousel-arrow',
20089 class : 'carousel-prev',
20093 class : 'fa fa-chevron-left'
20099 class : 'carousel-next',
20103 class : 'fa fa-chevron-right'
20116 initEvents: function()
20118 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20119 // this.el.on("touchstart", this.onTouchStart, this);
20122 if(this.autoslide){
20125 this.slideFn = window.setInterval(function() {
20126 _this.showPanelNext();
20130 if(this.showarrow){
20131 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20132 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20138 // onTouchStart : function(e, el, o)
20140 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20144 // this.showPanelNext();
20148 getChildContainer : function()
20150 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20154 * register a Navigation item
20155 * @param {Roo.bootstrap.NavItem} the navitem to add
20157 register : function(item)
20159 this.tabs.push( item);
20160 item.navId = this.navId; // not really needed..
20165 getActivePanel : function()
20168 Roo.each(this.tabs, function(t) {
20178 getPanelByName : function(n)
20181 Roo.each(this.tabs, function(t) {
20182 if (t.tabId == n) {
20190 indexOfPanel : function(p)
20193 Roo.each(this.tabs, function(t,i) {
20194 if (t.tabId == p.tabId) {
20203 * show a specific panel
20204 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20205 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20207 showPanel : function (pan)
20209 if(this.transition || typeof(pan) == 'undefined'){
20210 Roo.log("waiting for the transitionend");
20214 if (typeof(pan) == 'number') {
20215 pan = this.tabs[pan];
20218 if (typeof(pan) == 'string') {
20219 pan = this.getPanelByName(pan);
20222 var cur = this.getActivePanel();
20225 Roo.log('pan or acitve pan is undefined');
20229 if (pan.tabId == this.getActivePanel().tabId) {
20233 if (false === cur.fireEvent('beforedeactivate')) {
20237 if(this.bullets > 0 && !Roo.isTouch){
20238 this.setActiveBullet(this.indexOfPanel(pan));
20241 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20243 //class="carousel-item carousel-item-next carousel-item-left"
20245 this.transition = true;
20246 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20247 var lr = dir == 'next' ? 'left' : 'right';
20248 pan.el.addClass(dir); // or prev
20249 pan.el.addClass('carousel-item-' + dir); // or prev
20250 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20251 cur.el.addClass(lr); // or right
20252 pan.el.addClass(lr);
20253 cur.el.addClass('carousel-item-' +lr); // or right
20254 pan.el.addClass('carousel-item-' +lr);
20258 cur.el.on('transitionend', function() {
20259 Roo.log("trans end?");
20261 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20262 pan.setActive(true);
20264 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20265 cur.setActive(false);
20267 _this.transition = false;
20269 }, this, { single: true } );
20274 cur.setActive(false);
20275 pan.setActive(true);
20280 showPanelNext : function()
20282 var i = this.indexOfPanel(this.getActivePanel());
20284 if (i >= this.tabs.length - 1 && !this.autoslide) {
20288 if (i >= this.tabs.length - 1 && this.autoslide) {
20292 this.showPanel(this.tabs[i+1]);
20295 showPanelPrev : function()
20297 var i = this.indexOfPanel(this.getActivePanel());
20299 if (i < 1 && !this.autoslide) {
20303 if (i < 1 && this.autoslide) {
20304 i = this.tabs.length;
20307 this.showPanel(this.tabs[i-1]);
20311 addBullet: function()
20313 if(!this.bullets || Roo.isTouch){
20316 var ctr = this.el.select('.carousel-bullets',true).first();
20317 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20318 var bullet = ctr.createChild({
20319 cls : 'bullet bullet-' + i
20320 },ctr.dom.lastChild);
20325 bullet.on('click', (function(e, el, o, ii, t){
20327 e.preventDefault();
20329 this.showPanel(ii);
20331 if(this.autoslide && this.slideFn){
20332 clearInterval(this.slideFn);
20333 this.slideFn = window.setInterval(function() {
20334 _this.showPanelNext();
20338 }).createDelegate(this, [i, bullet], true));
20343 setActiveBullet : function(i)
20349 Roo.each(this.el.select('.bullet', true).elements, function(el){
20350 el.removeClass('selected');
20353 var bullet = this.el.select('.bullet-' + i, true).first();
20359 bullet.addClass('selected');
20370 Roo.apply(Roo.bootstrap.TabGroup, {
20374 * register a Navigation Group
20375 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20377 register : function(navgrp)
20379 this.groups[navgrp.navId] = navgrp;
20383 * fetch a Navigation Group based on the navigation ID
20384 * if one does not exist , it will get created.
20385 * @param {string} the navgroup to add
20386 * @returns {Roo.bootstrap.NavGroup} the navgroup
20388 get: function(navId) {
20389 if (typeof(this.groups[navId]) == 'undefined') {
20390 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20392 return this.groups[navId] ;
20407 * @class Roo.bootstrap.TabPanel
20408 * @extends Roo.bootstrap.Component
20409 * Bootstrap TabPanel class
20410 * @cfg {Boolean} active panel active
20411 * @cfg {String} html panel content
20412 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20413 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20414 * @cfg {String} href click to link..
20415 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20419 * Create a new TabPanel
20420 * @param {Object} config The config object
20423 Roo.bootstrap.TabPanel = function(config){
20424 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20428 * Fires when the active status changes
20429 * @param {Roo.bootstrap.TabPanel} this
20430 * @param {Boolean} state the new state
20435 * @event beforedeactivate
20436 * Fires before a tab is de-activated - can be used to do validation on a form.
20437 * @param {Roo.bootstrap.TabPanel} this
20438 * @return {Boolean} false if there is an error
20441 'beforedeactivate': true
20444 this.tabId = this.tabId || Roo.id();
20448 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20455 touchSlide : false,
20456 getAutoCreate : function(){
20461 // item is needed for carousel - not sure if it has any effect otherwise
20462 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20463 html: this.html || ''
20467 cfg.cls += ' active';
20471 cfg.tabId = this.tabId;
20479 initEvents: function()
20481 var p = this.parent();
20483 this.navId = this.navId || p.navId;
20485 if (typeof(this.navId) != 'undefined') {
20486 // not really needed.. but just in case.. parent should be a NavGroup.
20487 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20491 var i = tg.tabs.length - 1;
20493 if(this.active && tg.bullets > 0 && i < tg.bullets){
20494 tg.setActiveBullet(i);
20498 this.el.on('click', this.onClick, this);
20500 if(Roo.isTouch && this.touchSlide){
20501 this.el.on("touchstart", this.onTouchStart, this);
20502 this.el.on("touchmove", this.onTouchMove, this);
20503 this.el.on("touchend", this.onTouchEnd, this);
20508 onRender : function(ct, position)
20510 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20513 setActive : function(state)
20515 Roo.log("panel - set active " + this.tabId + "=" + state);
20517 this.active = state;
20519 this.el.removeClass('active');
20521 } else if (!this.el.hasClass('active')) {
20522 this.el.addClass('active');
20525 this.fireEvent('changed', this, state);
20528 onClick : function(e)
20530 e.preventDefault();
20532 if(!this.href.length){
20536 window.location.href = this.href;
20545 onTouchStart : function(e)
20547 this.swiping = false;
20549 this.startX = e.browserEvent.touches[0].clientX;
20550 this.startY = e.browserEvent.touches[0].clientY;
20553 onTouchMove : function(e)
20555 this.swiping = true;
20557 this.endX = e.browserEvent.touches[0].clientX;
20558 this.endY = e.browserEvent.touches[0].clientY;
20561 onTouchEnd : function(e)
20568 var tabGroup = this.parent();
20570 if(this.endX > this.startX){ // swiping right
20571 tabGroup.showPanelPrev();
20575 if(this.startX > this.endX){ // swiping left
20576 tabGroup.showPanelNext();
20595 * @class Roo.bootstrap.DateField
20596 * @extends Roo.bootstrap.Input
20597 * Bootstrap DateField class
20598 * @cfg {Number} weekStart default 0
20599 * @cfg {String} viewMode default empty, (months|years)
20600 * @cfg {String} minViewMode default empty, (months|years)
20601 * @cfg {Number} startDate default -Infinity
20602 * @cfg {Number} endDate default Infinity
20603 * @cfg {Boolean} todayHighlight default false
20604 * @cfg {Boolean} todayBtn default false
20605 * @cfg {Boolean} calendarWeeks default false
20606 * @cfg {Object} daysOfWeekDisabled default empty
20607 * @cfg {Boolean} singleMode default false (true | false)
20609 * @cfg {Boolean} keyboardNavigation default true
20610 * @cfg {String} language default en
20613 * Create a new DateField
20614 * @param {Object} config The config object
20617 Roo.bootstrap.DateField = function(config){
20618 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20622 * Fires when this field show.
20623 * @param {Roo.bootstrap.DateField} this
20624 * @param {Mixed} date The date value
20629 * Fires when this field hide.
20630 * @param {Roo.bootstrap.DateField} this
20631 * @param {Mixed} date The date value
20636 * Fires when select a date.
20637 * @param {Roo.bootstrap.DateField} this
20638 * @param {Mixed} date The date value
20642 * @event beforeselect
20643 * Fires when before select a date.
20644 * @param {Roo.bootstrap.DateField} this
20645 * @param {Mixed} date The date value
20647 beforeselect : true
20651 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20654 * @cfg {String} format
20655 * The default date format string which can be overriden for localization support. The format must be
20656 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20660 * @cfg {String} altFormats
20661 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20662 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20664 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20672 todayHighlight : false,
20678 keyboardNavigation: true,
20680 calendarWeeks: false,
20682 startDate: -Infinity,
20686 daysOfWeekDisabled: [],
20690 singleMode : false,
20692 UTCDate: function()
20694 return new Date(Date.UTC.apply(Date, arguments));
20697 UTCToday: function()
20699 var today = new Date();
20700 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20703 getDate: function() {
20704 var d = this.getUTCDate();
20705 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20708 getUTCDate: function() {
20712 setDate: function(d) {
20713 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20716 setUTCDate: function(d) {
20718 this.setValue(this.formatDate(this.date));
20721 onRender: function(ct, position)
20724 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20726 this.language = this.language || 'en';
20727 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20728 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20730 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20731 this.format = this.format || 'm/d/y';
20732 this.isInline = false;
20733 this.isInput = true;
20734 this.component = this.el.select('.add-on', true).first() || false;
20735 this.component = (this.component && this.component.length === 0) ? false : this.component;
20736 this.hasInput = this.component && this.inputEl().length;
20738 if (typeof(this.minViewMode === 'string')) {
20739 switch (this.minViewMode) {
20741 this.minViewMode = 1;
20744 this.minViewMode = 2;
20747 this.minViewMode = 0;
20752 if (typeof(this.viewMode === 'string')) {
20753 switch (this.viewMode) {
20766 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20768 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20770 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20772 this.picker().on('mousedown', this.onMousedown, this);
20773 this.picker().on('click', this.onClick, this);
20775 this.picker().addClass('datepicker-dropdown');
20777 this.startViewMode = this.viewMode;
20779 if(this.singleMode){
20780 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20781 v.setVisibilityMode(Roo.Element.DISPLAY);
20785 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20786 v.setStyle('width', '189px');
20790 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20791 if(!this.calendarWeeks){
20796 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20797 v.attr('colspan', function(i, val){
20798 return parseInt(val) + 1;
20803 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20805 this.setStartDate(this.startDate);
20806 this.setEndDate(this.endDate);
20808 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20815 if(this.isInline) {
20820 picker : function()
20822 return this.pickerEl;
20823 // return this.el.select('.datepicker', true).first();
20826 fillDow: function()
20828 var dowCnt = this.weekStart;
20837 if(this.calendarWeeks){
20845 while (dowCnt < this.weekStart + 7) {
20849 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20853 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20856 fillMonths: function()
20859 var months = this.picker().select('>.datepicker-months td', true).first();
20861 months.dom.innerHTML = '';
20867 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20870 months.createChild(month);
20877 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;
20879 if (this.date < this.startDate) {
20880 this.viewDate = new Date(this.startDate);
20881 } else if (this.date > this.endDate) {
20882 this.viewDate = new Date(this.endDate);
20884 this.viewDate = new Date(this.date);
20892 var d = new Date(this.viewDate),
20893 year = d.getUTCFullYear(),
20894 month = d.getUTCMonth(),
20895 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20896 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20897 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20898 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20899 currentDate = this.date && this.date.valueOf(),
20900 today = this.UTCToday();
20902 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20904 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20906 // this.picker.select('>tfoot th.today').
20907 // .text(dates[this.language].today)
20908 // .toggle(this.todayBtn !== false);
20910 this.updateNavArrows();
20913 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20915 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20917 prevMonth.setUTCDate(day);
20919 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20921 var nextMonth = new Date(prevMonth);
20923 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20925 nextMonth = nextMonth.valueOf();
20927 var fillMonths = false;
20929 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20931 while(prevMonth.valueOf() <= nextMonth) {
20934 if (prevMonth.getUTCDay() === this.weekStart) {
20936 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20944 if(this.calendarWeeks){
20945 // ISO 8601: First week contains first thursday.
20946 // ISO also states week starts on Monday, but we can be more abstract here.
20948 // Start of current week: based on weekstart/current date
20949 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20950 // Thursday of this week
20951 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20952 // First Thursday of year, year from thursday
20953 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20954 // Calendar week: ms between thursdays, div ms per day, div 7 days
20955 calWeek = (th - yth) / 864e5 / 7 + 1;
20957 fillMonths.cn.push({
20965 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20967 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20970 if (this.todayHighlight &&
20971 prevMonth.getUTCFullYear() == today.getFullYear() &&
20972 prevMonth.getUTCMonth() == today.getMonth() &&
20973 prevMonth.getUTCDate() == today.getDate()) {
20974 clsName += ' today';
20977 if (currentDate && prevMonth.valueOf() === currentDate) {
20978 clsName += ' active';
20981 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20982 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20983 clsName += ' disabled';
20986 fillMonths.cn.push({
20988 cls: 'day ' + clsName,
20989 html: prevMonth.getDate()
20992 prevMonth.setDate(prevMonth.getDate()+1);
20995 var currentYear = this.date && this.date.getUTCFullYear();
20996 var currentMonth = this.date && this.date.getUTCMonth();
20998 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21000 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21001 v.removeClass('active');
21003 if(currentYear === year && k === currentMonth){
21004 v.addClass('active');
21007 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21008 v.addClass('disabled');
21014 year = parseInt(year/10, 10) * 10;
21016 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21018 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21021 for (var i = -1; i < 11; i++) {
21022 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21024 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21032 showMode: function(dir)
21035 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21038 Roo.each(this.picker().select('>div',true).elements, function(v){
21039 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21042 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21047 if(this.isInline) {
21051 this.picker().removeClass(['bottom', 'top']);
21053 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21055 * place to the top of element!
21059 this.picker().addClass('top');
21060 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21065 this.picker().addClass('bottom');
21067 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21070 parseDate : function(value)
21072 if(!value || value instanceof Date){
21075 var v = Date.parseDate(value, this.format);
21076 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21077 v = Date.parseDate(value, 'Y-m-d');
21079 if(!v && this.altFormats){
21080 if(!this.altFormatsArray){
21081 this.altFormatsArray = this.altFormats.split("|");
21083 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21084 v = Date.parseDate(value, this.altFormatsArray[i]);
21090 formatDate : function(date, fmt)
21092 return (!date || !(date instanceof Date)) ?
21093 date : date.dateFormat(fmt || this.format);
21096 onFocus : function()
21098 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21102 onBlur : function()
21104 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21106 var d = this.inputEl().getValue();
21113 showPopup : function()
21115 this.picker().show();
21119 this.fireEvent('showpopup', this, this.date);
21122 hidePopup : function()
21124 if(this.isInline) {
21127 this.picker().hide();
21128 this.viewMode = this.startViewMode;
21131 this.fireEvent('hidepopup', this, this.date);
21135 onMousedown: function(e)
21137 e.stopPropagation();
21138 e.preventDefault();
21143 Roo.bootstrap.DateField.superclass.keyup.call(this);
21147 setValue: function(v)
21149 if(this.fireEvent('beforeselect', this, v) !== false){
21150 var d = new Date(this.parseDate(v) ).clearTime();
21152 if(isNaN(d.getTime())){
21153 this.date = this.viewDate = '';
21154 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21158 v = this.formatDate(d);
21160 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21162 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21166 this.fireEvent('select', this, this.date);
21170 getValue: function()
21172 return this.formatDate(this.date);
21175 fireKey: function(e)
21177 if (!this.picker().isVisible()){
21178 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21184 var dateChanged = false,
21186 newDate, newViewDate;
21191 e.preventDefault();
21195 if (!this.keyboardNavigation) {
21198 dir = e.keyCode == 37 ? -1 : 1;
21201 newDate = this.moveYear(this.date, dir);
21202 newViewDate = this.moveYear(this.viewDate, dir);
21203 } else if (e.shiftKey){
21204 newDate = this.moveMonth(this.date, dir);
21205 newViewDate = this.moveMonth(this.viewDate, dir);
21207 newDate = new Date(this.date);
21208 newDate.setUTCDate(this.date.getUTCDate() + dir);
21209 newViewDate = new Date(this.viewDate);
21210 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21212 if (this.dateWithinRange(newDate)){
21213 this.date = newDate;
21214 this.viewDate = newViewDate;
21215 this.setValue(this.formatDate(this.date));
21217 e.preventDefault();
21218 dateChanged = true;
21223 if (!this.keyboardNavigation) {
21226 dir = e.keyCode == 38 ? -1 : 1;
21228 newDate = this.moveYear(this.date, dir);
21229 newViewDate = this.moveYear(this.viewDate, dir);
21230 } else if (e.shiftKey){
21231 newDate = this.moveMonth(this.date, dir);
21232 newViewDate = this.moveMonth(this.viewDate, dir);
21234 newDate = new Date(this.date);
21235 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21236 newViewDate = new Date(this.viewDate);
21237 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21239 if (this.dateWithinRange(newDate)){
21240 this.date = newDate;
21241 this.viewDate = newViewDate;
21242 this.setValue(this.formatDate(this.date));
21244 e.preventDefault();
21245 dateChanged = true;
21249 this.setValue(this.formatDate(this.date));
21251 e.preventDefault();
21254 this.setValue(this.formatDate(this.date));
21268 onClick: function(e)
21270 e.stopPropagation();
21271 e.preventDefault();
21273 var target = e.getTarget();
21275 if(target.nodeName.toLowerCase() === 'i'){
21276 target = Roo.get(target).dom.parentNode;
21279 var nodeName = target.nodeName;
21280 var className = target.className;
21281 var html = target.innerHTML;
21282 //Roo.log(nodeName);
21284 switch(nodeName.toLowerCase()) {
21286 switch(className) {
21292 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21293 switch(this.viewMode){
21295 this.viewDate = this.moveMonth(this.viewDate, dir);
21299 this.viewDate = this.moveYear(this.viewDate, dir);
21305 var date = new Date();
21306 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21308 this.setValue(this.formatDate(this.date));
21315 if (className.indexOf('disabled') < 0) {
21316 this.viewDate.setUTCDate(1);
21317 if (className.indexOf('month') > -1) {
21318 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21320 var year = parseInt(html, 10) || 0;
21321 this.viewDate.setUTCFullYear(year);
21325 if(this.singleMode){
21326 this.setValue(this.formatDate(this.viewDate));
21337 //Roo.log(className);
21338 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21339 var day = parseInt(html, 10) || 1;
21340 var year = this.viewDate.getUTCFullYear(),
21341 month = this.viewDate.getUTCMonth();
21343 if (className.indexOf('old') > -1) {
21350 } else if (className.indexOf('new') > -1) {
21358 //Roo.log([year,month,day]);
21359 this.date = this.UTCDate(year, month, day,0,0,0,0);
21360 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21362 //Roo.log(this.formatDate(this.date));
21363 this.setValue(this.formatDate(this.date));
21370 setStartDate: function(startDate)
21372 this.startDate = startDate || -Infinity;
21373 if (this.startDate !== -Infinity) {
21374 this.startDate = this.parseDate(this.startDate);
21377 this.updateNavArrows();
21380 setEndDate: function(endDate)
21382 this.endDate = endDate || Infinity;
21383 if (this.endDate !== Infinity) {
21384 this.endDate = this.parseDate(this.endDate);
21387 this.updateNavArrows();
21390 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21392 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21393 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21394 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21396 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21397 return parseInt(d, 10);
21400 this.updateNavArrows();
21403 updateNavArrows: function()
21405 if(this.singleMode){
21409 var d = new Date(this.viewDate),
21410 year = d.getUTCFullYear(),
21411 month = d.getUTCMonth();
21413 Roo.each(this.picker().select('.prev', true).elements, function(v){
21415 switch (this.viewMode) {
21418 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21424 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21431 Roo.each(this.picker().select('.next', true).elements, function(v){
21433 switch (this.viewMode) {
21436 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21442 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21450 moveMonth: function(date, dir)
21455 var new_date = new Date(date.valueOf()),
21456 day = new_date.getUTCDate(),
21457 month = new_date.getUTCMonth(),
21458 mag = Math.abs(dir),
21460 dir = dir > 0 ? 1 : -1;
21463 // If going back one month, make sure month is not current month
21464 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21466 return new_date.getUTCMonth() == month;
21468 // If going forward one month, make sure month is as expected
21469 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21471 return new_date.getUTCMonth() != new_month;
21473 new_month = month + dir;
21474 new_date.setUTCMonth(new_month);
21475 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21476 if (new_month < 0 || new_month > 11) {
21477 new_month = (new_month + 12) % 12;
21480 // For magnitudes >1, move one month at a time...
21481 for (var i=0; i<mag; i++) {
21482 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21483 new_date = this.moveMonth(new_date, dir);
21485 // ...then reset the day, keeping it in the new month
21486 new_month = new_date.getUTCMonth();
21487 new_date.setUTCDate(day);
21489 return new_month != new_date.getUTCMonth();
21492 // Common date-resetting loop -- if date is beyond end of month, make it
21495 new_date.setUTCDate(--day);
21496 new_date.setUTCMonth(new_month);
21501 moveYear: function(date, dir)
21503 return this.moveMonth(date, dir*12);
21506 dateWithinRange: function(date)
21508 return date >= this.startDate && date <= this.endDate;
21514 this.picker().remove();
21517 validateValue : function(value)
21519 if(this.getVisibilityEl().hasClass('hidden')){
21523 if(value.length < 1) {
21524 if(this.allowBlank){
21530 if(value.length < this.minLength){
21533 if(value.length > this.maxLength){
21537 var vt = Roo.form.VTypes;
21538 if(!vt[this.vtype](value, this)){
21542 if(typeof this.validator == "function"){
21543 var msg = this.validator(value);
21549 if(this.regex && !this.regex.test(value)){
21553 if(typeof(this.parseDate(value)) == 'undefined'){
21557 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21561 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21571 this.date = this.viewDate = '';
21573 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21578 Roo.apply(Roo.bootstrap.DateField, {
21589 html: '<i class="fa fa-arrow-left"/>'
21599 html: '<i class="fa fa-arrow-right"/>'
21641 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21642 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21643 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21644 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21645 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21658 navFnc: 'FullYear',
21663 navFnc: 'FullYear',
21668 Roo.apply(Roo.bootstrap.DateField, {
21672 cls: 'datepicker dropdown-menu roo-dynamic',
21676 cls: 'datepicker-days',
21680 cls: 'table-condensed',
21682 Roo.bootstrap.DateField.head,
21686 Roo.bootstrap.DateField.footer
21693 cls: 'datepicker-months',
21697 cls: 'table-condensed',
21699 Roo.bootstrap.DateField.head,
21700 Roo.bootstrap.DateField.content,
21701 Roo.bootstrap.DateField.footer
21708 cls: 'datepicker-years',
21712 cls: 'table-condensed',
21714 Roo.bootstrap.DateField.head,
21715 Roo.bootstrap.DateField.content,
21716 Roo.bootstrap.DateField.footer
21735 * @class Roo.bootstrap.TimeField
21736 * @extends Roo.bootstrap.Input
21737 * Bootstrap DateField class
21741 * Create a new TimeField
21742 * @param {Object} config The config object
21745 Roo.bootstrap.TimeField = function(config){
21746 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21750 * Fires when this field show.
21751 * @param {Roo.bootstrap.DateField} thisthis
21752 * @param {Mixed} date The date value
21757 * Fires when this field hide.
21758 * @param {Roo.bootstrap.DateField} this
21759 * @param {Mixed} date The date value
21764 * Fires when select a date.
21765 * @param {Roo.bootstrap.DateField} this
21766 * @param {Mixed} date The date value
21772 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21775 * @cfg {String} format
21776 * The default time format string which can be overriden for localization support. The format must be
21777 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21781 onRender: function(ct, position)
21784 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21786 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21788 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21790 this.pop = this.picker().select('>.datepicker-time',true).first();
21791 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21793 this.picker().on('mousedown', this.onMousedown, this);
21794 this.picker().on('click', this.onClick, this);
21796 this.picker().addClass('datepicker-dropdown');
21801 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21802 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21803 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21804 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21805 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21806 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21810 fireKey: function(e){
21811 if (!this.picker().isVisible()){
21812 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21818 e.preventDefault();
21826 this.onTogglePeriod();
21829 this.onIncrementMinutes();
21832 this.onDecrementMinutes();
21841 onClick: function(e) {
21842 e.stopPropagation();
21843 e.preventDefault();
21846 picker : function()
21848 return this.el.select('.datepicker', true).first();
21851 fillTime: function()
21853 var time = this.pop.select('tbody', true).first();
21855 time.dom.innerHTML = '';
21870 cls: 'hours-up glyphicon glyphicon-chevron-up'
21890 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21911 cls: 'timepicker-hour',
21926 cls: 'timepicker-minute',
21941 cls: 'btn btn-primary period',
21963 cls: 'hours-down glyphicon glyphicon-chevron-down'
21983 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22001 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22008 var hours = this.time.getHours();
22009 var minutes = this.time.getMinutes();
22022 hours = hours - 12;
22026 hours = '0' + hours;
22030 minutes = '0' + minutes;
22033 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22034 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22035 this.pop.select('button', true).first().dom.innerHTML = period;
22041 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22043 var cls = ['bottom'];
22045 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22052 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22057 this.picker().addClass(cls.join('-'));
22061 Roo.each(cls, function(c){
22063 _this.picker().setTop(_this.inputEl().getHeight());
22067 _this.picker().setTop(0 - _this.picker().getHeight());
22072 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22076 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22083 onFocus : function()
22085 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22089 onBlur : function()
22091 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22097 this.picker().show();
22102 this.fireEvent('show', this, this.date);
22107 this.picker().hide();
22110 this.fireEvent('hide', this, this.date);
22113 setTime : function()
22116 this.setValue(this.time.format(this.format));
22118 this.fireEvent('select', this, this.date);
22123 onMousedown: function(e){
22124 e.stopPropagation();
22125 e.preventDefault();
22128 onIncrementHours: function()
22130 Roo.log('onIncrementHours');
22131 this.time = this.time.add(Date.HOUR, 1);
22136 onDecrementHours: function()
22138 Roo.log('onDecrementHours');
22139 this.time = this.time.add(Date.HOUR, -1);
22143 onIncrementMinutes: function()
22145 Roo.log('onIncrementMinutes');
22146 this.time = this.time.add(Date.MINUTE, 1);
22150 onDecrementMinutes: function()
22152 Roo.log('onDecrementMinutes');
22153 this.time = this.time.add(Date.MINUTE, -1);
22157 onTogglePeriod: function()
22159 Roo.log('onTogglePeriod');
22160 this.time = this.time.add(Date.HOUR, 12);
22167 Roo.apply(Roo.bootstrap.TimeField, {
22197 cls: 'btn btn-info ok',
22209 Roo.apply(Roo.bootstrap.TimeField, {
22213 cls: 'datepicker dropdown-menu',
22217 cls: 'datepicker-time',
22221 cls: 'table-condensed',
22223 Roo.bootstrap.TimeField.content,
22224 Roo.bootstrap.TimeField.footer
22243 * @class Roo.bootstrap.MonthField
22244 * @extends Roo.bootstrap.Input
22245 * Bootstrap MonthField class
22247 * @cfg {String} language default en
22250 * Create a new MonthField
22251 * @param {Object} config The config object
22254 Roo.bootstrap.MonthField = function(config){
22255 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22260 * Fires when this field show.
22261 * @param {Roo.bootstrap.MonthField} this
22262 * @param {Mixed} date The date value
22267 * Fires when this field hide.
22268 * @param {Roo.bootstrap.MonthField} this
22269 * @param {Mixed} date The date value
22274 * Fires when select a date.
22275 * @param {Roo.bootstrap.MonthField} this
22276 * @param {String} oldvalue The old value
22277 * @param {String} newvalue The new value
22283 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22285 onRender: function(ct, position)
22288 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22290 this.language = this.language || 'en';
22291 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22292 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22294 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22295 this.isInline = false;
22296 this.isInput = true;
22297 this.component = this.el.select('.add-on', true).first() || false;
22298 this.component = (this.component && this.component.length === 0) ? false : this.component;
22299 this.hasInput = this.component && this.inputEL().length;
22301 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22303 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22305 this.picker().on('mousedown', this.onMousedown, this);
22306 this.picker().on('click', this.onClick, this);
22308 this.picker().addClass('datepicker-dropdown');
22310 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22311 v.setStyle('width', '189px');
22318 if(this.isInline) {
22324 setValue: function(v, suppressEvent)
22326 var o = this.getValue();
22328 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22332 if(suppressEvent !== true){
22333 this.fireEvent('select', this, o, v);
22338 getValue: function()
22343 onClick: function(e)
22345 e.stopPropagation();
22346 e.preventDefault();
22348 var target = e.getTarget();
22350 if(target.nodeName.toLowerCase() === 'i'){
22351 target = Roo.get(target).dom.parentNode;
22354 var nodeName = target.nodeName;
22355 var className = target.className;
22356 var html = target.innerHTML;
22358 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22362 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22364 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22370 picker : function()
22372 return this.pickerEl;
22375 fillMonths: function()
22378 var months = this.picker().select('>.datepicker-months td', true).first();
22380 months.dom.innerHTML = '';
22386 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22389 months.createChild(month);
22398 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22399 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22402 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22403 e.removeClass('active');
22405 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22406 e.addClass('active');
22413 if(this.isInline) {
22417 this.picker().removeClass(['bottom', 'top']);
22419 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22421 * place to the top of element!
22425 this.picker().addClass('top');
22426 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22431 this.picker().addClass('bottom');
22433 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22436 onFocus : function()
22438 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22442 onBlur : function()
22444 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22446 var d = this.inputEl().getValue();
22455 this.picker().show();
22456 this.picker().select('>.datepicker-months', true).first().show();
22460 this.fireEvent('show', this, this.date);
22465 if(this.isInline) {
22468 this.picker().hide();
22469 this.fireEvent('hide', this, this.date);
22473 onMousedown: function(e)
22475 e.stopPropagation();
22476 e.preventDefault();
22481 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22485 fireKey: function(e)
22487 if (!this.picker().isVisible()){
22488 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22499 e.preventDefault();
22503 dir = e.keyCode == 37 ? -1 : 1;
22505 this.vIndex = this.vIndex + dir;
22507 if(this.vIndex < 0){
22511 if(this.vIndex > 11){
22515 if(isNaN(this.vIndex)){
22519 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22525 dir = e.keyCode == 38 ? -1 : 1;
22527 this.vIndex = this.vIndex + dir * 4;
22529 if(this.vIndex < 0){
22533 if(this.vIndex > 11){
22537 if(isNaN(this.vIndex)){
22541 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22546 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22547 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22551 e.preventDefault();
22554 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22555 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22571 this.picker().remove();
22576 Roo.apply(Roo.bootstrap.MonthField, {
22595 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22596 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22601 Roo.apply(Roo.bootstrap.MonthField, {
22605 cls: 'datepicker dropdown-menu roo-dynamic',
22609 cls: 'datepicker-months',
22613 cls: 'table-condensed',
22615 Roo.bootstrap.DateField.content
22635 * @class Roo.bootstrap.CheckBox
22636 * @extends Roo.bootstrap.Input
22637 * Bootstrap CheckBox class
22639 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22640 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22641 * @cfg {String} boxLabel The text that appears beside the checkbox
22642 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22643 * @cfg {Boolean} checked initnal the element
22644 * @cfg {Boolean} inline inline the element (default false)
22645 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22646 * @cfg {String} tooltip label tooltip
22649 * Create a new CheckBox
22650 * @param {Object} config The config object
22653 Roo.bootstrap.CheckBox = function(config){
22654 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22659 * Fires when the element is checked or unchecked.
22660 * @param {Roo.bootstrap.CheckBox} this This input
22661 * @param {Boolean} checked The new checked value
22666 * Fires when the element is click.
22667 * @param {Roo.bootstrap.CheckBox} this This input
22674 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22676 inputType: 'checkbox',
22685 // checkbox success does not make any sense really..
22690 getAutoCreate : function()
22692 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22698 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22701 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22707 type : this.inputType,
22708 value : this.inputValue,
22709 cls : 'roo-' + this.inputType, //'form-box',
22710 placeholder : this.placeholder || ''
22714 if(this.inputType != 'radio'){
22718 cls : 'roo-hidden-value',
22719 value : this.checked ? this.inputValue : this.valueOff
22724 if (this.weight) { // Validity check?
22725 cfg.cls += " " + this.inputType + "-" + this.weight;
22728 if (this.disabled) {
22729 input.disabled=true;
22733 input.checked = this.checked;
22738 input.name = this.name;
22740 if(this.inputType != 'radio'){
22741 hidden.name = this.name;
22742 input.name = '_hidden_' + this.name;
22747 input.cls += ' input-' + this.size;
22752 ['xs','sm','md','lg'].map(function(size){
22753 if (settings[size]) {
22754 cfg.cls += ' col-' + size + '-' + settings[size];
22758 var inputblock = input;
22760 if (this.before || this.after) {
22763 cls : 'input-group',
22768 inputblock.cn.push({
22770 cls : 'input-group-addon',
22775 inputblock.cn.push(input);
22777 if(this.inputType != 'radio'){
22778 inputblock.cn.push(hidden);
22782 inputblock.cn.push({
22784 cls : 'input-group-addon',
22790 var boxLabelCfg = false;
22796 //'for': id, // box label is handled by onclick - so no for...
22798 html: this.boxLabel
22801 boxLabelCfg.tooltip = this.tooltip;
22807 if (align ==='left' && this.fieldLabel.length) {
22808 // Roo.log("left and has label");
22813 cls : 'control-label',
22814 html : this.fieldLabel
22825 cfg.cn[1].cn.push(boxLabelCfg);
22828 if(this.labelWidth > 12){
22829 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22832 if(this.labelWidth < 13 && this.labelmd == 0){
22833 this.labelmd = this.labelWidth;
22836 if(this.labellg > 0){
22837 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22838 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22841 if(this.labelmd > 0){
22842 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22843 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22846 if(this.labelsm > 0){
22847 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22848 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22851 if(this.labelxs > 0){
22852 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22853 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22856 } else if ( this.fieldLabel.length) {
22857 // Roo.log(" label");
22861 tag: this.boxLabel ? 'span' : 'label',
22863 cls: 'control-label box-input-label',
22864 //cls : 'input-group-addon',
22865 html : this.fieldLabel
22872 cfg.cn.push(boxLabelCfg);
22877 // Roo.log(" no label && no align");
22878 cfg.cn = [ inputblock ] ;
22880 cfg.cn.push(boxLabelCfg);
22888 if(this.inputType != 'radio'){
22889 cfg.cn.push(hidden);
22897 * return the real input element.
22899 inputEl: function ()
22901 return this.el.select('input.roo-' + this.inputType,true).first();
22903 hiddenEl: function ()
22905 return this.el.select('input.roo-hidden-value',true).first();
22908 labelEl: function()
22910 return this.el.select('label.control-label',true).first();
22912 /* depricated... */
22916 return this.labelEl();
22919 boxLabelEl: function()
22921 return this.el.select('label.box-label',true).first();
22924 initEvents : function()
22926 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22928 this.inputEl().on('click', this.onClick, this);
22930 if (this.boxLabel) {
22931 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22934 this.startValue = this.getValue();
22937 Roo.bootstrap.CheckBox.register(this);
22941 onClick : function(e)
22943 if(this.fireEvent('click', this, e) !== false){
22944 this.setChecked(!this.checked);
22949 setChecked : function(state,suppressEvent)
22951 this.startValue = this.getValue();
22953 if(this.inputType == 'radio'){
22955 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22956 e.dom.checked = false;
22959 this.inputEl().dom.checked = true;
22961 this.inputEl().dom.value = this.inputValue;
22963 if(suppressEvent !== true){
22964 this.fireEvent('check', this, true);
22972 this.checked = state;
22974 this.inputEl().dom.checked = state;
22977 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22979 if(suppressEvent !== true){
22980 this.fireEvent('check', this, state);
22986 getValue : function()
22988 if(this.inputType == 'radio'){
22989 return this.getGroupValue();
22992 return this.hiddenEl().dom.value;
22996 getGroupValue : function()
22998 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23002 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23005 setValue : function(v,suppressEvent)
23007 if(this.inputType == 'radio'){
23008 this.setGroupValue(v, suppressEvent);
23012 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23017 setGroupValue : function(v, suppressEvent)
23019 this.startValue = this.getValue();
23021 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23022 e.dom.checked = false;
23024 if(e.dom.value == v){
23025 e.dom.checked = true;
23029 if(suppressEvent !== true){
23030 this.fireEvent('check', this, true);
23038 validate : function()
23040 if(this.getVisibilityEl().hasClass('hidden')){
23046 (this.inputType == 'radio' && this.validateRadio()) ||
23047 (this.inputType == 'checkbox' && this.validateCheckbox())
23053 this.markInvalid();
23057 validateRadio : function()
23059 if(this.getVisibilityEl().hasClass('hidden')){
23063 if(this.allowBlank){
23069 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23070 if(!e.dom.checked){
23082 validateCheckbox : function()
23085 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23086 //return (this.getValue() == this.inputValue) ? true : false;
23089 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23097 for(var i in group){
23098 if(group[i].el.isVisible(true)){
23106 for(var i in group){
23111 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23118 * Mark this field as valid
23120 markValid : function()
23124 this.fireEvent('valid', this);
23126 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23129 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23136 if(this.inputType == 'radio'){
23137 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23138 var fg = e.findParent('.form-group', false, true);
23139 if (Roo.bootstrap.version == 3) {
23140 fg.removeClass([_this.invalidClass, _this.validClass]);
23141 fg.addClass(_this.validClass);
23143 fg.removeClass(['is-valid', 'is-invalid']);
23144 fg.addClass('is-valid');
23152 var fg = this.el.findParent('.form-group', false, true);
23153 if (Roo.bootstrap.version == 3) {
23154 fg.removeClass([this.invalidClass, this.validClass]);
23155 fg.addClass(this.validClass);
23157 fg.removeClass(['is-valid', 'is-invalid']);
23158 fg.addClass('is-valid');
23163 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23169 for(var i in group){
23170 var fg = group[i].el.findParent('.form-group', false, true);
23171 if (Roo.bootstrap.version == 3) {
23172 fg.removeClass([this.invalidClass, this.validClass]);
23173 fg.addClass(this.validClass);
23175 fg.removeClass(['is-valid', 'is-invalid']);
23176 fg.addClass('is-valid');
23182 * Mark this field as invalid
23183 * @param {String} msg The validation message
23185 markInvalid : function(msg)
23187 if(this.allowBlank){
23193 this.fireEvent('invalid', this, msg);
23195 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23198 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23202 label.markInvalid();
23205 if(this.inputType == 'radio'){
23207 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23208 var fg = e.findParent('.form-group', false, true);
23209 if (Roo.bootstrap.version == 3) {
23210 fg.removeClass([_this.invalidClass, _this.validClass]);
23211 fg.addClass(_this.invalidClass);
23213 fg.removeClass(['is-invalid', 'is-valid']);
23214 fg.addClass('is-invalid');
23222 var fg = this.el.findParent('.form-group', false, true);
23223 if (Roo.bootstrap.version == 3) {
23224 fg.removeClass([_this.invalidClass, _this.validClass]);
23225 fg.addClass(_this.invalidClass);
23227 fg.removeClass(['is-invalid', 'is-valid']);
23228 fg.addClass('is-invalid');
23233 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23239 for(var i in group){
23240 var fg = group[i].el.findParent('.form-group', false, true);
23241 if (Roo.bootstrap.version == 3) {
23242 fg.removeClass([_this.invalidClass, _this.validClass]);
23243 fg.addClass(_this.invalidClass);
23245 fg.removeClass(['is-invalid', 'is-valid']);
23246 fg.addClass('is-invalid');
23252 clearInvalid : function()
23254 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23256 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23258 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23260 if (label && label.iconEl) {
23261 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23262 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23266 disable : function()
23268 if(this.inputType != 'radio'){
23269 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23276 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23277 _this.getActionEl().addClass(this.disabledClass);
23278 e.dom.disabled = true;
23282 this.disabled = true;
23283 this.fireEvent("disable", this);
23287 enable : function()
23289 if(this.inputType != 'radio'){
23290 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23297 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23298 _this.getActionEl().removeClass(this.disabledClass);
23299 e.dom.disabled = false;
23303 this.disabled = false;
23304 this.fireEvent("enable", this);
23308 setBoxLabel : function(v)
23313 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23319 Roo.apply(Roo.bootstrap.CheckBox, {
23324 * register a CheckBox Group
23325 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23327 register : function(checkbox)
23329 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23330 this.groups[checkbox.groupId] = {};
23333 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23337 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23341 * fetch a CheckBox Group based on the group ID
23342 * @param {string} the group ID
23343 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23345 get: function(groupId) {
23346 if (typeof(this.groups[groupId]) == 'undefined') {
23350 return this.groups[groupId] ;
23363 * @class Roo.bootstrap.Radio
23364 * @extends Roo.bootstrap.Component
23365 * Bootstrap Radio class
23366 * @cfg {String} boxLabel - the label associated
23367 * @cfg {String} value - the value of radio
23370 * Create a new Radio
23371 * @param {Object} config The config object
23373 Roo.bootstrap.Radio = function(config){
23374 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23378 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23384 getAutoCreate : function()
23388 cls : 'form-group radio',
23393 html : this.boxLabel
23401 initEvents : function()
23403 this.parent().register(this);
23405 this.el.on('click', this.onClick, this);
23409 onClick : function(e)
23411 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23412 this.setChecked(true);
23416 setChecked : function(state, suppressEvent)
23418 this.parent().setValue(this.value, suppressEvent);
23422 setBoxLabel : function(v)
23427 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23442 * @class Roo.bootstrap.SecurePass
23443 * @extends Roo.bootstrap.Input
23444 * Bootstrap SecurePass class
23448 * Create a new SecurePass
23449 * @param {Object} config The config object
23452 Roo.bootstrap.SecurePass = function (config) {
23453 // these go here, so the translation tool can replace them..
23455 PwdEmpty: "Please type a password, and then retype it to confirm.",
23456 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23457 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23458 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23459 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23460 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23461 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23462 TooWeak: "Your password is Too Weak."
23464 this.meterLabel = "Password strength:";
23465 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23466 this.meterClass = [
23467 "roo-password-meter-tooweak",
23468 "roo-password-meter-weak",
23469 "roo-password-meter-medium",
23470 "roo-password-meter-strong",
23471 "roo-password-meter-grey"
23476 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23479 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23481 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23483 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23484 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23485 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23486 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23487 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23488 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23489 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23499 * @cfg {String/Object} Label for the strength meter (defaults to
23500 * 'Password strength:')
23505 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23506 * ['Weak', 'Medium', 'Strong'])
23509 pwdStrengths: false,
23522 initEvents: function ()
23524 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23526 if (this.el.is('input[type=password]') && Roo.isSafari) {
23527 this.el.on('keydown', this.SafariOnKeyDown, this);
23530 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23533 onRender: function (ct, position)
23535 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23536 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23537 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23539 this.trigger.createChild({
23544 cls: 'roo-password-meter-grey col-xs-12',
23547 //width: this.meterWidth + 'px'
23551 cls: 'roo-password-meter-text'
23557 if (this.hideTrigger) {
23558 this.trigger.setDisplayed(false);
23560 this.setSize(this.width || '', this.height || '');
23563 onDestroy: function ()
23565 if (this.trigger) {
23566 this.trigger.removeAllListeners();
23567 this.trigger.remove();
23570 this.wrap.remove();
23572 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23575 checkStrength: function ()
23577 var pwd = this.inputEl().getValue();
23578 if (pwd == this._lastPwd) {
23583 if (this.ClientSideStrongPassword(pwd)) {
23585 } else if (this.ClientSideMediumPassword(pwd)) {
23587 } else if (this.ClientSideWeakPassword(pwd)) {
23593 Roo.log('strength1: ' + strength);
23595 //var pm = this.trigger.child('div/div/div').dom;
23596 var pm = this.trigger.child('div/div');
23597 pm.removeClass(this.meterClass);
23598 pm.addClass(this.meterClass[strength]);
23601 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23603 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23605 this._lastPwd = pwd;
23609 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23611 this._lastPwd = '';
23613 var pm = this.trigger.child('div/div');
23614 pm.removeClass(this.meterClass);
23615 pm.addClass('roo-password-meter-grey');
23618 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23621 this.inputEl().dom.type='password';
23624 validateValue: function (value)
23626 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23629 if (value.length == 0) {
23630 if (this.allowBlank) {
23631 this.clearInvalid();
23635 this.markInvalid(this.errors.PwdEmpty);
23636 this.errorMsg = this.errors.PwdEmpty;
23644 if (!value.match(/[\x21-\x7e]+/)) {
23645 this.markInvalid(this.errors.PwdBadChar);
23646 this.errorMsg = this.errors.PwdBadChar;
23649 if (value.length < 6) {
23650 this.markInvalid(this.errors.PwdShort);
23651 this.errorMsg = this.errors.PwdShort;
23654 if (value.length > 16) {
23655 this.markInvalid(this.errors.PwdLong);
23656 this.errorMsg = this.errors.PwdLong;
23660 if (this.ClientSideStrongPassword(value)) {
23662 } else if (this.ClientSideMediumPassword(value)) {
23664 } else if (this.ClientSideWeakPassword(value)) {
23671 if (strength < 2) {
23672 //this.markInvalid(this.errors.TooWeak);
23673 this.errorMsg = this.errors.TooWeak;
23678 console.log('strength2: ' + strength);
23680 //var pm = this.trigger.child('div/div/div').dom;
23682 var pm = this.trigger.child('div/div');
23683 pm.removeClass(this.meterClass);
23684 pm.addClass(this.meterClass[strength]);
23686 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23688 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23690 this.errorMsg = '';
23694 CharacterSetChecks: function (type)
23697 this.fResult = false;
23700 isctype: function (character, type)
23703 case this.kCapitalLetter:
23704 if (character >= 'A' && character <= 'Z') {
23709 case this.kSmallLetter:
23710 if (character >= 'a' && character <= 'z') {
23716 if (character >= '0' && character <= '9') {
23721 case this.kPunctuation:
23722 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23733 IsLongEnough: function (pwd, size)
23735 return !(pwd == null || isNaN(size) || pwd.length < size);
23738 SpansEnoughCharacterSets: function (word, nb)
23740 if (!this.IsLongEnough(word, nb))
23745 var characterSetChecks = new Array(
23746 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23747 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23750 for (var index = 0; index < word.length; ++index) {
23751 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23752 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23753 characterSetChecks[nCharSet].fResult = true;
23760 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23761 if (characterSetChecks[nCharSet].fResult) {
23766 if (nCharSets < nb) {
23772 ClientSideStrongPassword: function (pwd)
23774 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23777 ClientSideMediumPassword: function (pwd)
23779 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23782 ClientSideWeakPassword: function (pwd)
23784 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23787 })//<script type="text/javascript">
23790 * Based Ext JS Library 1.1.1
23791 * Copyright(c) 2006-2007, Ext JS, LLC.
23797 * @class Roo.HtmlEditorCore
23798 * @extends Roo.Component
23799 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23801 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23804 Roo.HtmlEditorCore = function(config){
23807 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23812 * @event initialize
23813 * Fires when the editor is fully initialized (including the iframe)
23814 * @param {Roo.HtmlEditorCore} this
23819 * Fires when the editor is first receives the focus. Any insertion must wait
23820 * until after this event.
23821 * @param {Roo.HtmlEditorCore} this
23825 * @event beforesync
23826 * Fires before the textarea is updated with content from the editor iframe. Return false
23827 * to cancel the sync.
23828 * @param {Roo.HtmlEditorCore} this
23829 * @param {String} html
23833 * @event beforepush
23834 * Fires before the iframe editor is updated with content from the textarea. Return false
23835 * to cancel the push.
23836 * @param {Roo.HtmlEditorCore} this
23837 * @param {String} html
23842 * Fires when the textarea is updated with content from the editor iframe.
23843 * @param {Roo.HtmlEditorCore} this
23844 * @param {String} html
23849 * Fires when the iframe editor is updated with content from the textarea.
23850 * @param {Roo.HtmlEditorCore} this
23851 * @param {String} html
23856 * @event editorevent
23857 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23858 * @param {Roo.HtmlEditorCore} this
23864 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23866 // defaults : white / black...
23867 this.applyBlacklists();
23874 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23878 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23884 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23889 * @cfg {Number} height (in pixels)
23893 * @cfg {Number} width (in pixels)
23898 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23901 stylesheets: false,
23906 // private properties
23907 validationEvent : false,
23909 initialized : false,
23911 sourceEditMode : false,
23912 onFocus : Roo.emptyFn,
23914 hideMode:'offsets',
23918 // blacklist + whitelisted elements..
23925 * Protected method that will not generally be called directly. It
23926 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23927 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23929 getDocMarkup : function(){
23933 // inherit styels from page...??
23934 if (this.stylesheets === false) {
23936 Roo.get(document.head).select('style').each(function(node) {
23937 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23940 Roo.get(document.head).select('link').each(function(node) {
23941 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23944 } else if (!this.stylesheets.length) {
23946 st = '<style type="text/css">' +
23947 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23950 for (var i in this.stylesheets) {
23951 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23956 st += '<style type="text/css">' +
23957 'IMG { cursor: pointer } ' +
23960 var cls = 'roo-htmleditor-body';
23962 if(this.bodyCls.length){
23963 cls += ' ' + this.bodyCls;
23966 return '<html><head>' + st +
23967 //<style type="text/css">' +
23968 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23970 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23974 onRender : function(ct, position)
23977 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23978 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23981 this.el.dom.style.border = '0 none';
23982 this.el.dom.setAttribute('tabIndex', -1);
23983 this.el.addClass('x-hidden hide');
23987 if(Roo.isIE){ // fix IE 1px bogus margin
23988 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23992 this.frameId = Roo.id();
23996 var iframe = this.owner.wrap.createChild({
23998 cls: 'form-control', // bootstrap..
24000 name: this.frameId,
24001 frameBorder : 'no',
24002 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24007 this.iframe = iframe.dom;
24009 this.assignDocWin();
24011 this.doc.designMode = 'on';
24014 this.doc.write(this.getDocMarkup());
24018 var task = { // must defer to wait for browser to be ready
24020 //console.log("run task?" + this.doc.readyState);
24021 this.assignDocWin();
24022 if(this.doc.body || this.doc.readyState == 'complete'){
24024 this.doc.designMode="on";
24028 Roo.TaskMgr.stop(task);
24029 this.initEditor.defer(10, this);
24036 Roo.TaskMgr.start(task);
24041 onResize : function(w, h)
24043 Roo.log('resize: ' +w + ',' + h );
24044 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24048 if(typeof w == 'number'){
24050 this.iframe.style.width = w + 'px';
24052 if(typeof h == 'number'){
24054 this.iframe.style.height = h + 'px';
24056 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24063 * Toggles the editor between standard and source edit mode.
24064 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24066 toggleSourceEdit : function(sourceEditMode){
24068 this.sourceEditMode = sourceEditMode === true;
24070 if(this.sourceEditMode){
24072 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24075 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24076 //this.iframe.className = '';
24079 //this.setSize(this.owner.wrap.getSize());
24080 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24087 * Protected method that will not generally be called directly. If you need/want
24088 * custom HTML cleanup, this is the method you should override.
24089 * @param {String} html The HTML to be cleaned
24090 * return {String} The cleaned HTML
24092 cleanHtml : function(html){
24093 html = String(html);
24094 if(html.length > 5){
24095 if(Roo.isSafari){ // strip safari nonsense
24096 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24099 if(html == ' '){
24106 * HTML Editor -> Textarea
24107 * Protected method that will not generally be called directly. Syncs the contents
24108 * of the editor iframe with the textarea.
24110 syncValue : function(){
24111 if(this.initialized){
24112 var bd = (this.doc.body || this.doc.documentElement);
24113 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24114 var html = bd.innerHTML;
24116 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24117 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24119 html = '<div style="'+m[0]+'">' + html + '</div>';
24122 html = this.cleanHtml(html);
24123 // fix up the special chars.. normaly like back quotes in word...
24124 // however we do not want to do this with chinese..
24125 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24127 var cc = match.charCodeAt();
24129 // Get the character value, handling surrogate pairs
24130 if (match.length == 2) {
24131 // It's a surrogate pair, calculate the Unicode code point
24132 var high = match.charCodeAt(0) - 0xD800;
24133 var low = match.charCodeAt(1) - 0xDC00;
24134 cc = (high * 0x400) + low + 0x10000;
24136 (cc >= 0x4E00 && cc < 0xA000 ) ||
24137 (cc >= 0x3400 && cc < 0x4E00 ) ||
24138 (cc >= 0xf900 && cc < 0xfb00 )
24143 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24144 return "&#" + cc + ";";
24151 if(this.owner.fireEvent('beforesync', this, html) !== false){
24152 this.el.dom.value = html;
24153 this.owner.fireEvent('sync', this, html);
24159 * Protected method that will not generally be called directly. Pushes the value of the textarea
24160 * into the iframe editor.
24162 pushValue : function(){
24163 if(this.initialized){
24164 var v = this.el.dom.value.trim();
24166 // if(v.length < 1){
24170 if(this.owner.fireEvent('beforepush', this, v) !== false){
24171 var d = (this.doc.body || this.doc.documentElement);
24173 this.cleanUpPaste();
24174 this.el.dom.value = d.innerHTML;
24175 this.owner.fireEvent('push', this, v);
24181 deferFocus : function(){
24182 this.focus.defer(10, this);
24186 focus : function(){
24187 if(this.win && !this.sourceEditMode){
24194 assignDocWin: function()
24196 var iframe = this.iframe;
24199 this.doc = iframe.contentWindow.document;
24200 this.win = iframe.contentWindow;
24202 // if (!Roo.get(this.frameId)) {
24205 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24206 // this.win = Roo.get(this.frameId).dom.contentWindow;
24208 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24212 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24213 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24218 initEditor : function(){
24219 //console.log("INIT EDITOR");
24220 this.assignDocWin();
24224 this.doc.designMode="on";
24226 this.doc.write(this.getDocMarkup());
24229 var dbody = (this.doc.body || this.doc.documentElement);
24230 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24231 // this copies styles from the containing element into thsi one..
24232 // not sure why we need all of this..
24233 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24235 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24236 //ss['background-attachment'] = 'fixed'; // w3c
24237 dbody.bgProperties = 'fixed'; // ie
24238 //Roo.DomHelper.applyStyles(dbody, ss);
24239 Roo.EventManager.on(this.doc, {
24240 //'mousedown': this.onEditorEvent,
24241 'mouseup': this.onEditorEvent,
24242 'dblclick': this.onEditorEvent,
24243 'click': this.onEditorEvent,
24244 'keyup': this.onEditorEvent,
24249 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24251 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24252 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24254 this.initialized = true;
24256 this.owner.fireEvent('initialize', this);
24261 onDestroy : function(){
24267 //for (var i =0; i < this.toolbars.length;i++) {
24268 // // fixme - ask toolbars for heights?
24269 // this.toolbars[i].onDestroy();
24272 //this.wrap.dom.innerHTML = '';
24273 //this.wrap.remove();
24278 onFirstFocus : function(){
24280 this.assignDocWin();
24283 this.activated = true;
24286 if(Roo.isGecko){ // prevent silly gecko errors
24288 var s = this.win.getSelection();
24289 if(!s.focusNode || s.focusNode.nodeType != 3){
24290 var r = s.getRangeAt(0);
24291 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24296 this.execCmd('useCSS', true);
24297 this.execCmd('styleWithCSS', false);
24300 this.owner.fireEvent('activate', this);
24304 adjustFont: function(btn){
24305 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24306 //if(Roo.isSafari){ // safari
24309 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24310 if(Roo.isSafari){ // safari
24311 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24312 v = (v < 10) ? 10 : v;
24313 v = (v > 48) ? 48 : v;
24314 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24319 v = Math.max(1, v+adjust);
24321 this.execCmd('FontSize', v );
24324 onEditorEvent : function(e)
24326 this.owner.fireEvent('editorevent', this, e);
24327 // this.updateToolbar();
24328 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24331 insertTag : function(tg)
24333 // could be a bit smarter... -> wrap the current selected tRoo..
24334 if (tg.toLowerCase() == 'span' ||
24335 tg.toLowerCase() == 'code' ||
24336 tg.toLowerCase() == 'sup' ||
24337 tg.toLowerCase() == 'sub'
24340 range = this.createRange(this.getSelection());
24341 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24342 wrappingNode.appendChild(range.extractContents());
24343 range.insertNode(wrappingNode);
24350 this.execCmd("formatblock", tg);
24354 insertText : function(txt)
24358 var range = this.createRange();
24359 range.deleteContents();
24360 //alert(Sender.getAttribute('label'));
24362 range.insertNode(this.doc.createTextNode(txt));
24368 * Executes a Midas editor command on the editor document and performs necessary focus and
24369 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24370 * @param {String} cmd The Midas command
24371 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24373 relayCmd : function(cmd, value){
24375 this.execCmd(cmd, value);
24376 this.owner.fireEvent('editorevent', this);
24377 //this.updateToolbar();
24378 this.owner.deferFocus();
24382 * Executes a Midas editor command directly on the editor document.
24383 * For visual commands, you should use {@link #relayCmd} instead.
24384 * <b>This should only be called after the editor is initialized.</b>
24385 * @param {String} cmd The Midas command
24386 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24388 execCmd : function(cmd, value){
24389 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24396 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24398 * @param {String} text | dom node..
24400 insertAtCursor : function(text)
24403 if(!this.activated){
24409 var r = this.doc.selection.createRange();
24420 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24424 // from jquery ui (MIT licenced)
24426 var win = this.win;
24428 if (win.getSelection && win.getSelection().getRangeAt) {
24429 range = win.getSelection().getRangeAt(0);
24430 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24431 range.insertNode(node);
24432 } else if (win.document.selection && win.document.selection.createRange) {
24433 // no firefox support
24434 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24435 win.document.selection.createRange().pasteHTML(txt);
24437 // no firefox support
24438 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24439 this.execCmd('InsertHTML', txt);
24448 mozKeyPress : function(e){
24450 var c = e.getCharCode(), cmd;
24453 c = String.fromCharCode(c).toLowerCase();
24467 this.cleanUpPaste.defer(100, this);
24475 e.preventDefault();
24483 fixKeys : function(){ // load time branching for fastest keydown performance
24485 return function(e){
24486 var k = e.getKey(), r;
24489 r = this.doc.selection.createRange();
24492 r.pasteHTML('    ');
24499 r = this.doc.selection.createRange();
24501 var target = r.parentElement();
24502 if(!target || target.tagName.toLowerCase() != 'li'){
24504 r.pasteHTML('<br />');
24510 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24511 this.cleanUpPaste.defer(100, this);
24517 }else if(Roo.isOpera){
24518 return function(e){
24519 var k = e.getKey();
24523 this.execCmd('InsertHTML','    ');
24526 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24527 this.cleanUpPaste.defer(100, this);
24532 }else if(Roo.isSafari){
24533 return function(e){
24534 var k = e.getKey();
24538 this.execCmd('InsertText','\t');
24542 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24543 this.cleanUpPaste.defer(100, this);
24551 getAllAncestors: function()
24553 var p = this.getSelectedNode();
24556 a.push(p); // push blank onto stack..
24557 p = this.getParentElement();
24561 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24565 a.push(this.doc.body);
24569 lastSelNode : false,
24572 getSelection : function()
24574 this.assignDocWin();
24575 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24578 getSelectedNode: function()
24580 // this may only work on Gecko!!!
24582 // should we cache this!!!!
24587 var range = this.createRange(this.getSelection()).cloneRange();
24590 var parent = range.parentElement();
24592 var testRange = range.duplicate();
24593 testRange.moveToElementText(parent);
24594 if (testRange.inRange(range)) {
24597 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24600 parent = parent.parentElement;
24605 // is ancestor a text element.
24606 var ac = range.commonAncestorContainer;
24607 if (ac.nodeType == 3) {
24608 ac = ac.parentNode;
24611 var ar = ac.childNodes;
24614 var other_nodes = [];
24615 var has_other_nodes = false;
24616 for (var i=0;i<ar.length;i++) {
24617 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24620 // fullly contained node.
24622 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24627 // probably selected..
24628 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24629 other_nodes.push(ar[i]);
24633 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24638 has_other_nodes = true;
24640 if (!nodes.length && other_nodes.length) {
24641 nodes= other_nodes;
24643 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24649 createRange: function(sel)
24651 // this has strange effects when using with
24652 // top toolbar - not sure if it's a great idea.
24653 //this.editor.contentWindow.focus();
24654 if (typeof sel != "undefined") {
24656 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24658 return this.doc.createRange();
24661 return this.doc.createRange();
24664 getParentElement: function()
24667 this.assignDocWin();
24668 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24670 var range = this.createRange(sel);
24673 var p = range.commonAncestorContainer;
24674 while (p.nodeType == 3) { // text node
24685 * Range intersection.. the hard stuff...
24689 * [ -- selected range --- ]
24693 * if end is before start or hits it. fail.
24694 * if start is after end or hits it fail.
24696 * if either hits (but other is outside. - then it's not
24702 // @see http://www.thismuchiknow.co.uk/?p=64.
24703 rangeIntersectsNode : function(range, node)
24705 var nodeRange = node.ownerDocument.createRange();
24707 nodeRange.selectNode(node);
24709 nodeRange.selectNodeContents(node);
24712 var rangeStartRange = range.cloneRange();
24713 rangeStartRange.collapse(true);
24715 var rangeEndRange = range.cloneRange();
24716 rangeEndRange.collapse(false);
24718 var nodeStartRange = nodeRange.cloneRange();
24719 nodeStartRange.collapse(true);
24721 var nodeEndRange = nodeRange.cloneRange();
24722 nodeEndRange.collapse(false);
24724 return rangeStartRange.compareBoundaryPoints(
24725 Range.START_TO_START, nodeEndRange) == -1 &&
24726 rangeEndRange.compareBoundaryPoints(
24727 Range.START_TO_START, nodeStartRange) == 1;
24731 rangeCompareNode : function(range, node)
24733 var nodeRange = node.ownerDocument.createRange();
24735 nodeRange.selectNode(node);
24737 nodeRange.selectNodeContents(node);
24741 range.collapse(true);
24743 nodeRange.collapse(true);
24745 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24746 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24748 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24750 var nodeIsBefore = ss == 1;
24751 var nodeIsAfter = ee == -1;
24753 if (nodeIsBefore && nodeIsAfter) {
24756 if (!nodeIsBefore && nodeIsAfter) {
24757 return 1; //right trailed.
24760 if (nodeIsBefore && !nodeIsAfter) {
24761 return 2; // left trailed.
24767 // private? - in a new class?
24768 cleanUpPaste : function()
24770 // cleans up the whole document..
24771 Roo.log('cleanuppaste');
24773 this.cleanUpChildren(this.doc.body);
24774 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24775 if (clean != this.doc.body.innerHTML) {
24776 this.doc.body.innerHTML = clean;
24781 cleanWordChars : function(input) {// change the chars to hex code
24782 var he = Roo.HtmlEditorCore;
24784 var output = input;
24785 Roo.each(he.swapCodes, function(sw) {
24786 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24788 output = output.replace(swapper, sw[1]);
24795 cleanUpChildren : function (n)
24797 if (!n.childNodes.length) {
24800 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24801 this.cleanUpChild(n.childNodes[i]);
24808 cleanUpChild : function (node)
24811 //console.log(node);
24812 if (node.nodeName == "#text") {
24813 // clean up silly Windows -- stuff?
24816 if (node.nodeName == "#comment") {
24817 node.parentNode.removeChild(node);
24818 // clean up silly Windows -- stuff?
24821 var lcname = node.tagName.toLowerCase();
24822 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24823 // whitelist of tags..
24825 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24827 node.parentNode.removeChild(node);
24832 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24834 // spans with no attributes - just remove them..
24835 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24836 remove_keep_children = true;
24839 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24840 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24842 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24843 // remove_keep_children = true;
24846 if (remove_keep_children) {
24847 this.cleanUpChildren(node);
24848 // inserts everything just before this node...
24849 while (node.childNodes.length) {
24850 var cn = node.childNodes[0];
24851 node.removeChild(cn);
24852 node.parentNode.insertBefore(cn, node);
24854 node.parentNode.removeChild(node);
24858 if (!node.attributes || !node.attributes.length) {
24863 this.cleanUpChildren(node);
24867 function cleanAttr(n,v)
24870 if (v.match(/^\./) || v.match(/^\//)) {
24873 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24876 if (v.match(/^#/)) {
24879 if (v.match(/^\{/)) { // allow template editing.
24882 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24883 node.removeAttribute(n);
24887 var cwhite = this.cwhite;
24888 var cblack = this.cblack;
24890 function cleanStyle(n,v)
24892 if (v.match(/expression/)) { //XSS?? should we even bother..
24893 node.removeAttribute(n);
24897 var parts = v.split(/;/);
24900 Roo.each(parts, function(p) {
24901 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24905 var l = p.split(':').shift().replace(/\s+/g,'');
24906 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24908 if ( cwhite.length && cblack.indexOf(l) > -1) {
24909 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24910 //node.removeAttribute(n);
24914 // only allow 'c whitelisted system attributes'
24915 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24916 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24917 //node.removeAttribute(n);
24927 if (clean.length) {
24928 node.setAttribute(n, clean.join(';'));
24930 node.removeAttribute(n);
24936 for (var i = node.attributes.length-1; i > -1 ; i--) {
24937 var a = node.attributes[i];
24940 if (a.name.toLowerCase().substr(0,2)=='on') {
24941 node.removeAttribute(a.name);
24944 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24945 node.removeAttribute(a.name);
24948 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24949 cleanAttr(a.name,a.value); // fixme..
24952 if (a.name == 'style') {
24953 cleanStyle(a.name,a.value);
24956 /// clean up MS crap..
24957 // tecnically this should be a list of valid class'es..
24960 if (a.name == 'class') {
24961 if (a.value.match(/^Mso/)) {
24962 node.removeAttribute('class');
24965 if (a.value.match(/^body$/)) {
24966 node.removeAttribute('class');
24977 this.cleanUpChildren(node);
24983 * Clean up MS wordisms...
24985 cleanWord : function(node)
24988 this.cleanWord(this.doc.body);
24993 node.nodeName == 'SPAN' &&
24994 !node.hasAttributes() &&
24995 node.childNodes.length == 1 &&
24996 node.firstChild.nodeName == "#text"
24998 var textNode = node.firstChild;
24999 node.removeChild(textNode);
25000 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25001 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25003 node.parentNode.insertBefore(textNode, node);
25004 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25005 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25007 node.parentNode.removeChild(node);
25010 if (node.nodeName == "#text") {
25011 // clean up silly Windows -- stuff?
25014 if (node.nodeName == "#comment") {
25015 node.parentNode.removeChild(node);
25016 // clean up silly Windows -- stuff?
25020 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25021 node.parentNode.removeChild(node);
25024 //Roo.log(node.tagName);
25025 // remove - but keep children..
25026 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25027 //Roo.log('-- removed');
25028 while (node.childNodes.length) {
25029 var cn = node.childNodes[0];
25030 node.removeChild(cn);
25031 node.parentNode.insertBefore(cn, node);
25032 // move node to parent - and clean it..
25033 this.cleanWord(cn);
25035 node.parentNode.removeChild(node);
25036 /// no need to iterate chidlren = it's got none..
25037 //this.iterateChildren(node, this.cleanWord);
25041 if (node.className.length) {
25043 var cn = node.className.split(/\W+/);
25045 Roo.each(cn, function(cls) {
25046 if (cls.match(/Mso[a-zA-Z]+/)) {
25051 node.className = cna.length ? cna.join(' ') : '';
25053 node.removeAttribute("class");
25057 if (node.hasAttribute("lang")) {
25058 node.removeAttribute("lang");
25061 if (node.hasAttribute("style")) {
25063 var styles = node.getAttribute("style").split(";");
25065 Roo.each(styles, function(s) {
25066 if (!s.match(/:/)) {
25069 var kv = s.split(":");
25070 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25073 // what ever is left... we allow.
25076 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25077 if (!nstyle.length) {
25078 node.removeAttribute('style');
25081 this.iterateChildren(node, this.cleanWord);
25087 * iterateChildren of a Node, calling fn each time, using this as the scole..
25088 * @param {DomNode} node node to iterate children of.
25089 * @param {Function} fn method of this class to call on each item.
25091 iterateChildren : function(node, fn)
25093 if (!node.childNodes.length) {
25096 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25097 fn.call(this, node.childNodes[i])
25103 * cleanTableWidths.
25105 * Quite often pasting from word etc.. results in tables with column and widths.
25106 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25109 cleanTableWidths : function(node)
25114 this.cleanTableWidths(this.doc.body);
25119 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25122 Roo.log(node.tagName);
25123 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25124 this.iterateChildren(node, this.cleanTableWidths);
25127 if (node.hasAttribute('width')) {
25128 node.removeAttribute('width');
25132 if (node.hasAttribute("style")) {
25135 var styles = node.getAttribute("style").split(";");
25137 Roo.each(styles, function(s) {
25138 if (!s.match(/:/)) {
25141 var kv = s.split(":");
25142 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25145 // what ever is left... we allow.
25148 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25149 if (!nstyle.length) {
25150 node.removeAttribute('style');
25154 this.iterateChildren(node, this.cleanTableWidths);
25162 domToHTML : function(currentElement, depth, nopadtext) {
25164 depth = depth || 0;
25165 nopadtext = nopadtext || false;
25167 if (!currentElement) {
25168 return this.domToHTML(this.doc.body);
25171 //Roo.log(currentElement);
25173 var allText = false;
25174 var nodeName = currentElement.nodeName;
25175 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25177 if (nodeName == '#text') {
25179 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25184 if (nodeName != 'BODY') {
25187 // Prints the node tagName, such as <A>, <IMG>, etc
25190 for(i = 0; i < currentElement.attributes.length;i++) {
25192 var aname = currentElement.attributes.item(i).name;
25193 if (!currentElement.attributes.item(i).value.length) {
25196 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25199 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25208 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25211 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25216 // Traverse the tree
25218 var currentElementChild = currentElement.childNodes.item(i);
25219 var allText = true;
25220 var innerHTML = '';
25222 while (currentElementChild) {
25223 // Formatting code (indent the tree so it looks nice on the screen)
25224 var nopad = nopadtext;
25225 if (lastnode == 'SPAN') {
25229 if (currentElementChild.nodeName == '#text') {
25230 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25231 toadd = nopadtext ? toadd : toadd.trim();
25232 if (!nopad && toadd.length > 80) {
25233 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25235 innerHTML += toadd;
25238 currentElementChild = currentElement.childNodes.item(i);
25244 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25246 // Recursively traverse the tree structure of the child node
25247 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25248 lastnode = currentElementChild.nodeName;
25250 currentElementChild=currentElement.childNodes.item(i);
25256 // The remaining code is mostly for formatting the tree
25257 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25262 ret+= "</"+tagName+">";
25268 applyBlacklists : function()
25270 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25271 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25275 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25276 if (b.indexOf(tag) > -1) {
25279 this.white.push(tag);
25283 Roo.each(w, function(tag) {
25284 if (b.indexOf(tag) > -1) {
25287 if (this.white.indexOf(tag) > -1) {
25290 this.white.push(tag);
25295 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25296 if (w.indexOf(tag) > -1) {
25299 this.black.push(tag);
25303 Roo.each(b, function(tag) {
25304 if (w.indexOf(tag) > -1) {
25307 if (this.black.indexOf(tag) > -1) {
25310 this.black.push(tag);
25315 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25316 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25320 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25321 if (b.indexOf(tag) > -1) {
25324 this.cwhite.push(tag);
25328 Roo.each(w, function(tag) {
25329 if (b.indexOf(tag) > -1) {
25332 if (this.cwhite.indexOf(tag) > -1) {
25335 this.cwhite.push(tag);
25340 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25341 if (w.indexOf(tag) > -1) {
25344 this.cblack.push(tag);
25348 Roo.each(b, function(tag) {
25349 if (w.indexOf(tag) > -1) {
25352 if (this.cblack.indexOf(tag) > -1) {
25355 this.cblack.push(tag);
25360 setStylesheets : function(stylesheets)
25362 if(typeof(stylesheets) == 'string'){
25363 Roo.get(this.iframe.contentDocument.head).createChild({
25365 rel : 'stylesheet',
25374 Roo.each(stylesheets, function(s) {
25379 Roo.get(_this.iframe.contentDocument.head).createChild({
25381 rel : 'stylesheet',
25390 removeStylesheets : function()
25394 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25399 setStyle : function(style)
25401 Roo.get(this.iframe.contentDocument.head).createChild({
25410 // hide stuff that is not compatible
25424 * @event specialkey
25428 * @cfg {String} fieldClass @hide
25431 * @cfg {String} focusClass @hide
25434 * @cfg {String} autoCreate @hide
25437 * @cfg {String} inputType @hide
25440 * @cfg {String} invalidClass @hide
25443 * @cfg {String} invalidText @hide
25446 * @cfg {String} msgFx @hide
25449 * @cfg {String} validateOnBlur @hide
25453 Roo.HtmlEditorCore.white = [
25454 'area', 'br', 'img', 'input', 'hr', 'wbr',
25456 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25457 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25458 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25459 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25460 'table', 'ul', 'xmp',
25462 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25465 'dir', 'menu', 'ol', 'ul', 'dl',
25471 Roo.HtmlEditorCore.black = [
25472 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25474 'base', 'basefont', 'bgsound', 'blink', 'body',
25475 'frame', 'frameset', 'head', 'html', 'ilayer',
25476 'iframe', 'layer', 'link', 'meta', 'object',
25477 'script', 'style' ,'title', 'xml' // clean later..
25479 Roo.HtmlEditorCore.clean = [
25480 'script', 'style', 'title', 'xml'
25482 Roo.HtmlEditorCore.remove = [
25487 Roo.HtmlEditorCore.ablack = [
25491 Roo.HtmlEditorCore.aclean = [
25492 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25496 Roo.HtmlEditorCore.pwhite= [
25497 'http', 'https', 'mailto'
25500 // white listed style attributes.
25501 Roo.HtmlEditorCore.cwhite= [
25502 // 'text-align', /// default is to allow most things..
25508 // black listed style attributes.
25509 Roo.HtmlEditorCore.cblack= [
25510 // 'font-size' -- this can be set by the project
25514 Roo.HtmlEditorCore.swapCodes =[
25533 * @class Roo.bootstrap.HtmlEditor
25534 * @extends Roo.bootstrap.TextArea
25535 * Bootstrap HtmlEditor class
25538 * Create a new HtmlEditor
25539 * @param {Object} config The config object
25542 Roo.bootstrap.HtmlEditor = function(config){
25543 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25544 if (!this.toolbars) {
25545 this.toolbars = [];
25548 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25551 * @event initialize
25552 * Fires when the editor is fully initialized (including the iframe)
25553 * @param {HtmlEditor} this
25558 * Fires when the editor is first receives the focus. Any insertion must wait
25559 * until after this event.
25560 * @param {HtmlEditor} this
25564 * @event beforesync
25565 * Fires before the textarea is updated with content from the editor iframe. Return false
25566 * to cancel the sync.
25567 * @param {HtmlEditor} this
25568 * @param {String} html
25572 * @event beforepush
25573 * Fires before the iframe editor is updated with content from the textarea. Return false
25574 * to cancel the push.
25575 * @param {HtmlEditor} this
25576 * @param {String} html
25581 * Fires when the textarea is updated with content from the editor iframe.
25582 * @param {HtmlEditor} this
25583 * @param {String} html
25588 * Fires when the iframe editor is updated with content from the textarea.
25589 * @param {HtmlEditor} this
25590 * @param {String} html
25594 * @event editmodechange
25595 * Fires when the editor switches edit modes
25596 * @param {HtmlEditor} this
25597 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25599 editmodechange: true,
25601 * @event editorevent
25602 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25603 * @param {HtmlEditor} this
25607 * @event firstfocus
25608 * Fires when on first focus - needed by toolbars..
25609 * @param {HtmlEditor} this
25614 * Auto save the htmlEditor value as a file into Events
25615 * @param {HtmlEditor} this
25619 * @event savedpreview
25620 * preview the saved version of htmlEditor
25621 * @param {HtmlEditor} this
25628 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25632 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25637 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25642 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25647 * @cfg {Number} height (in pixels)
25651 * @cfg {Number} width (in pixels)
25656 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25659 stylesheets: false,
25664 // private properties
25665 validationEvent : false,
25667 initialized : false,
25670 onFocus : Roo.emptyFn,
25672 hideMode:'offsets',
25674 tbContainer : false,
25678 toolbarContainer :function() {
25679 return this.wrap.select('.x-html-editor-tb',true).first();
25683 * Protected method that will not generally be called directly. It
25684 * is called when the editor creates its toolbar. Override this method if you need to
25685 * add custom toolbar buttons.
25686 * @param {HtmlEditor} editor
25688 createToolbar : function(){
25689 Roo.log('renewing');
25690 Roo.log("create toolbars");
25692 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25693 this.toolbars[0].render(this.toolbarContainer());
25697 // if (!editor.toolbars || !editor.toolbars.length) {
25698 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25701 // for (var i =0 ; i < editor.toolbars.length;i++) {
25702 // editor.toolbars[i] = Roo.factory(
25703 // typeof(editor.toolbars[i]) == 'string' ?
25704 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25705 // Roo.bootstrap.HtmlEditor);
25706 // editor.toolbars[i].init(editor);
25712 onRender : function(ct, position)
25714 // Roo.log("Call onRender: " + this.xtype);
25716 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25718 this.wrap = this.inputEl().wrap({
25719 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25722 this.editorcore.onRender(ct, position);
25724 if (this.resizable) {
25725 this.resizeEl = new Roo.Resizable(this.wrap, {
25729 minHeight : this.height,
25730 height: this.height,
25731 handles : this.resizable,
25734 resize : function(r, w, h) {
25735 _t.onResize(w,h); // -something
25741 this.createToolbar(this);
25744 if(!this.width && this.resizable){
25745 this.setSize(this.wrap.getSize());
25747 if (this.resizeEl) {
25748 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25749 // should trigger onReize..
25755 onResize : function(w, h)
25757 Roo.log('resize: ' +w + ',' + h );
25758 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25762 if(this.inputEl() ){
25763 if(typeof w == 'number'){
25764 var aw = w - this.wrap.getFrameWidth('lr');
25765 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25768 if(typeof h == 'number'){
25769 var tbh = -11; // fixme it needs to tool bar size!
25770 for (var i =0; i < this.toolbars.length;i++) {
25771 // fixme - ask toolbars for heights?
25772 tbh += this.toolbars[i].el.getHeight();
25773 //if (this.toolbars[i].footer) {
25774 // tbh += this.toolbars[i].footer.el.getHeight();
25782 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25783 ah -= 5; // knock a few pixes off for look..
25784 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25788 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25789 this.editorcore.onResize(ew,eh);
25794 * Toggles the editor between standard and source edit mode.
25795 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25797 toggleSourceEdit : function(sourceEditMode)
25799 this.editorcore.toggleSourceEdit(sourceEditMode);
25801 if(this.editorcore.sourceEditMode){
25802 Roo.log('editor - showing textarea');
25805 // Roo.log(this.syncValue());
25807 this.inputEl().removeClass(['hide', 'x-hidden']);
25808 this.inputEl().dom.removeAttribute('tabIndex');
25809 this.inputEl().focus();
25811 Roo.log('editor - hiding textarea');
25813 // Roo.log(this.pushValue());
25816 this.inputEl().addClass(['hide', 'x-hidden']);
25817 this.inputEl().dom.setAttribute('tabIndex', -1);
25818 //this.deferFocus();
25821 if(this.resizable){
25822 this.setSize(this.wrap.getSize());
25825 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25828 // private (for BoxComponent)
25829 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25831 // private (for BoxComponent)
25832 getResizeEl : function(){
25836 // private (for BoxComponent)
25837 getPositionEl : function(){
25842 initEvents : function(){
25843 this.originalValue = this.getValue();
25847 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25850 // markInvalid : Roo.emptyFn,
25852 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25855 // clearInvalid : Roo.emptyFn,
25857 setValue : function(v){
25858 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25859 this.editorcore.pushValue();
25864 deferFocus : function(){
25865 this.focus.defer(10, this);
25869 focus : function(){
25870 this.editorcore.focus();
25876 onDestroy : function(){
25882 for (var i =0; i < this.toolbars.length;i++) {
25883 // fixme - ask toolbars for heights?
25884 this.toolbars[i].onDestroy();
25887 this.wrap.dom.innerHTML = '';
25888 this.wrap.remove();
25893 onFirstFocus : function(){
25894 //Roo.log("onFirstFocus");
25895 this.editorcore.onFirstFocus();
25896 for (var i =0; i < this.toolbars.length;i++) {
25897 this.toolbars[i].onFirstFocus();
25903 syncValue : function()
25905 this.editorcore.syncValue();
25908 pushValue : function()
25910 this.editorcore.pushValue();
25914 // hide stuff that is not compatible
25928 * @event specialkey
25932 * @cfg {String} fieldClass @hide
25935 * @cfg {String} focusClass @hide
25938 * @cfg {String} autoCreate @hide
25941 * @cfg {String} inputType @hide
25945 * @cfg {String} invalidText @hide
25948 * @cfg {String} msgFx @hide
25951 * @cfg {String} validateOnBlur @hide
25960 Roo.namespace('Roo.bootstrap.htmleditor');
25962 * @class Roo.bootstrap.HtmlEditorToolbar1
25968 new Roo.bootstrap.HtmlEditor({
25971 new Roo.bootstrap.HtmlEditorToolbar1({
25972 disable : { fonts: 1 , format: 1, ..., ... , ...],
25978 * @cfg {Object} disable List of elements to disable..
25979 * @cfg {Array} btns List of additional buttons.
25983 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25986 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25989 Roo.apply(this, config);
25991 // default disabled, based on 'good practice'..
25992 this.disable = this.disable || {};
25993 Roo.applyIf(this.disable, {
25996 specialElements : true
25998 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26000 this.editor = config.editor;
26001 this.editorcore = config.editor.editorcore;
26003 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26005 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26006 // dont call parent... till later.
26008 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26013 editorcore : false,
26018 "h1","h2","h3","h4","h5","h6",
26020 "abbr", "acronym", "address", "cite", "samp", "var",
26024 onRender : function(ct, position)
26026 // Roo.log("Call onRender: " + this.xtype);
26028 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26030 this.el.dom.style.marginBottom = '0';
26032 var editorcore = this.editorcore;
26033 var editor= this.editor;
26036 var btn = function(id,cmd , toggle, handler, html){
26038 var event = toggle ? 'toggle' : 'click';
26043 xns: Roo.bootstrap,
26047 enableToggle:toggle !== false,
26049 pressed : toggle ? false : null,
26052 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26053 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26059 // var cb_box = function...
26064 xns: Roo.bootstrap,
26069 xns: Roo.bootstrap,
26073 Roo.each(this.formats, function(f) {
26074 style.menu.items.push({
26076 xns: Roo.bootstrap,
26077 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26082 editorcore.insertTag(this.tagname);
26089 children.push(style);
26091 btn('bold',false,true);
26092 btn('italic',false,true);
26093 btn('align-left', 'justifyleft',true);
26094 btn('align-center', 'justifycenter',true);
26095 btn('align-right' , 'justifyright',true);
26096 btn('link', false, false, function(btn) {
26097 //Roo.log("create link?");
26098 var url = prompt(this.createLinkText, this.defaultLinkValue);
26099 if(url && url != 'http:/'+'/'){
26100 this.editorcore.relayCmd('createlink', url);
26103 btn('list','insertunorderedlist',true);
26104 btn('pencil', false,true, function(btn){
26106 this.toggleSourceEdit(btn.pressed);
26109 if (this.editor.btns.length > 0) {
26110 for (var i = 0; i<this.editor.btns.length; i++) {
26111 children.push(this.editor.btns[i]);
26119 xns: Roo.bootstrap,
26124 xns: Roo.bootstrap,
26129 cog.menu.items.push({
26131 xns: Roo.bootstrap,
26132 html : Clean styles,
26137 editorcore.insertTag(this.tagname);
26146 this.xtype = 'NavSimplebar';
26148 for(var i=0;i< children.length;i++) {
26150 this.buttons.add(this.addxtypeChild(children[i]));
26154 editor.on('editorevent', this.updateToolbar, this);
26156 onBtnClick : function(id)
26158 this.editorcore.relayCmd(id);
26159 this.editorcore.focus();
26163 * Protected method that will not generally be called directly. It triggers
26164 * a toolbar update by reading the markup state of the current selection in the editor.
26166 updateToolbar: function(){
26168 if(!this.editorcore.activated){
26169 this.editor.onFirstFocus(); // is this neeed?
26173 var btns = this.buttons;
26174 var doc = this.editorcore.doc;
26175 btns.get('bold').setActive(doc.queryCommandState('bold'));
26176 btns.get('italic').setActive(doc.queryCommandState('italic'));
26177 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26179 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26180 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26181 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26183 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26184 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26187 var ans = this.editorcore.getAllAncestors();
26188 if (this.formatCombo) {
26191 var store = this.formatCombo.store;
26192 this.formatCombo.setValue("");
26193 for (var i =0; i < ans.length;i++) {
26194 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26196 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26204 // hides menus... - so this cant be on a menu...
26205 Roo.bootstrap.MenuMgr.hideAll();
26207 Roo.bootstrap.MenuMgr.hideAll();
26208 //this.editorsyncValue();
26210 onFirstFocus: function() {
26211 this.buttons.each(function(item){
26215 toggleSourceEdit : function(sourceEditMode){
26218 if(sourceEditMode){
26219 Roo.log("disabling buttons");
26220 this.buttons.each( function(item){
26221 if(item.cmd != 'pencil'){
26227 Roo.log("enabling buttons");
26228 if(this.editorcore.initialized){
26229 this.buttons.each( function(item){
26235 Roo.log("calling toggole on editor");
26236 // tell the editor that it's been pressed..
26237 this.editor.toggleSourceEdit(sourceEditMode);
26251 * @class Roo.bootstrap.Markdown
26252 * @extends Roo.bootstrap.TextArea
26253 * Bootstrap Showdown editable area
26254 * @cfg {string} content
26257 * Create a new Showdown
26260 Roo.bootstrap.Markdown = function(config){
26261 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26265 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26269 initEvents : function()
26272 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26273 this.markdownEl = this.el.createChild({
26274 cls : 'roo-markdown-area'
26276 this.inputEl().addClass('d-none');
26277 if (this.getValue() == '') {
26278 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26281 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26283 this.markdownEl.on('click', this.toggleTextEdit, this);
26284 this.on('blur', this.toggleTextEdit, this);
26285 this.on('specialkey', this.resizeTextArea, this);
26288 toggleTextEdit : function()
26290 var sh = this.markdownEl.getHeight();
26291 this.inputEl().addClass('d-none');
26292 this.markdownEl.addClass('d-none');
26293 if (!this.editing) {
26295 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26296 this.inputEl().removeClass('d-none');
26297 this.inputEl().focus();
26298 this.editing = true;
26301 // show showdown...
26302 this.updateMarkdown();
26303 this.markdownEl.removeClass('d-none');
26304 this.editing = false;
26307 updateMarkdown : function()
26309 if (this.getValue() == '') {
26310 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26314 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26317 resizeTextArea: function () {
26320 Roo.log([sh, this.getValue().split("\n").length * 30]);
26321 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26323 setValue : function(val)
26325 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26326 if (!this.editing) {
26327 this.updateMarkdown();
26333 if (!this.editing) {
26334 this.toggleTextEdit();
26342 * @class Roo.bootstrap.Table.AbstractSelectionModel
26343 * @extends Roo.util.Observable
26344 * Abstract base class for grid SelectionModels. It provides the interface that should be
26345 * implemented by descendant classes. This class should not be directly instantiated.
26348 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26349 this.locked = false;
26350 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26354 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26355 /** @ignore Called by the grid automatically. Do not call directly. */
26356 init : function(grid){
26362 * Locks the selections.
26365 this.locked = true;
26369 * Unlocks the selections.
26371 unlock : function(){
26372 this.locked = false;
26376 * Returns true if the selections are locked.
26377 * @return {Boolean}
26379 isLocked : function(){
26380 return this.locked;
26384 initEvents : function ()
26390 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26391 * @class Roo.bootstrap.Table.RowSelectionModel
26392 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26393 * It supports multiple selections and keyboard selection/navigation.
26395 * @param {Object} config
26398 Roo.bootstrap.Table.RowSelectionModel = function(config){
26399 Roo.apply(this, config);
26400 this.selections = new Roo.util.MixedCollection(false, function(o){
26405 this.lastActive = false;
26409 * @event selectionchange
26410 * Fires when the selection changes
26411 * @param {SelectionModel} this
26413 "selectionchange" : true,
26415 * @event afterselectionchange
26416 * Fires after the selection changes (eg. by key press or clicking)
26417 * @param {SelectionModel} this
26419 "afterselectionchange" : true,
26421 * @event beforerowselect
26422 * Fires when a row is selected being selected, return false to cancel.
26423 * @param {SelectionModel} this
26424 * @param {Number} rowIndex The selected index
26425 * @param {Boolean} keepExisting False if other selections will be cleared
26427 "beforerowselect" : true,
26430 * Fires when a row is selected.
26431 * @param {SelectionModel} this
26432 * @param {Number} rowIndex The selected index
26433 * @param {Roo.data.Record} r The record
26435 "rowselect" : true,
26437 * @event rowdeselect
26438 * Fires when a row is deselected.
26439 * @param {SelectionModel} this
26440 * @param {Number} rowIndex The selected index
26442 "rowdeselect" : true
26444 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26445 this.locked = false;
26448 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26450 * @cfg {Boolean} singleSelect
26451 * True to allow selection of only one row at a time (defaults to false)
26453 singleSelect : false,
26456 initEvents : function()
26459 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26460 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26461 //}else{ // allow click to work like normal
26462 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26464 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26465 this.grid.on("rowclick", this.handleMouseDown, this);
26467 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26468 "up" : function(e){
26470 this.selectPrevious(e.shiftKey);
26471 }else if(this.last !== false && this.lastActive !== false){
26472 var last = this.last;
26473 this.selectRange(this.last, this.lastActive-1);
26474 this.grid.getView().focusRow(this.lastActive);
26475 if(last !== false){
26479 this.selectFirstRow();
26481 this.fireEvent("afterselectionchange", this);
26483 "down" : function(e){
26485 this.selectNext(e.shiftKey);
26486 }else if(this.last !== false && this.lastActive !== false){
26487 var last = this.last;
26488 this.selectRange(this.last, this.lastActive+1);
26489 this.grid.getView().focusRow(this.lastActive);
26490 if(last !== false){
26494 this.selectFirstRow();
26496 this.fireEvent("afterselectionchange", this);
26500 this.grid.store.on('load', function(){
26501 this.selections.clear();
26504 var view = this.grid.view;
26505 view.on("refresh", this.onRefresh, this);
26506 view.on("rowupdated", this.onRowUpdated, this);
26507 view.on("rowremoved", this.onRemove, this);
26512 onRefresh : function()
26514 var ds = this.grid.store, i, v = this.grid.view;
26515 var s = this.selections;
26516 s.each(function(r){
26517 if((i = ds.indexOfId(r.id)) != -1){
26526 onRemove : function(v, index, r){
26527 this.selections.remove(r);
26531 onRowUpdated : function(v, index, r){
26532 if(this.isSelected(r)){
26533 v.onRowSelect(index);
26539 * @param {Array} records The records to select
26540 * @param {Boolean} keepExisting (optional) True to keep existing selections
26542 selectRecords : function(records, keepExisting)
26545 this.clearSelections();
26547 var ds = this.grid.store;
26548 for(var i = 0, len = records.length; i < len; i++){
26549 this.selectRow(ds.indexOf(records[i]), true);
26554 * Gets the number of selected rows.
26557 getCount : function(){
26558 return this.selections.length;
26562 * Selects the first row in the grid.
26564 selectFirstRow : function(){
26569 * Select the last row.
26570 * @param {Boolean} keepExisting (optional) True to keep existing selections
26572 selectLastRow : function(keepExisting){
26573 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26574 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26578 * Selects the row immediately following the last selected row.
26579 * @param {Boolean} keepExisting (optional) True to keep existing selections
26581 selectNext : function(keepExisting)
26583 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26584 this.selectRow(this.last+1, keepExisting);
26585 this.grid.getView().focusRow(this.last);
26590 * Selects the row that precedes the last selected row.
26591 * @param {Boolean} keepExisting (optional) True to keep existing selections
26593 selectPrevious : function(keepExisting){
26595 this.selectRow(this.last-1, keepExisting);
26596 this.grid.getView().focusRow(this.last);
26601 * Returns the selected records
26602 * @return {Array} Array of selected records
26604 getSelections : function(){
26605 return [].concat(this.selections.items);
26609 * Returns the first selected record.
26612 getSelected : function(){
26613 return this.selections.itemAt(0);
26618 * Clears all selections.
26620 clearSelections : function(fast)
26626 var ds = this.grid.store;
26627 var s = this.selections;
26628 s.each(function(r){
26629 this.deselectRow(ds.indexOfId(r.id));
26633 this.selections.clear();
26640 * Selects all rows.
26642 selectAll : function(){
26646 this.selections.clear();
26647 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26648 this.selectRow(i, true);
26653 * Returns True if there is a selection.
26654 * @return {Boolean}
26656 hasSelection : function(){
26657 return this.selections.length > 0;
26661 * Returns True if the specified row is selected.
26662 * @param {Number/Record} record The record or index of the record to check
26663 * @return {Boolean}
26665 isSelected : function(index){
26666 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26667 return (r && this.selections.key(r.id) ? true : false);
26671 * Returns True if the specified record id is selected.
26672 * @param {String} id The id of record to check
26673 * @return {Boolean}
26675 isIdSelected : function(id){
26676 return (this.selections.key(id) ? true : false);
26681 handleMouseDBClick : function(e, t){
26685 handleMouseDown : function(e, t)
26687 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26688 if(this.isLocked() || rowIndex < 0 ){
26691 if(e.shiftKey && this.last !== false){
26692 var last = this.last;
26693 this.selectRange(last, rowIndex, e.ctrlKey);
26694 this.last = last; // reset the last
26698 var isSelected = this.isSelected(rowIndex);
26699 //Roo.log("select row:" + rowIndex);
26701 this.deselectRow(rowIndex);
26703 this.selectRow(rowIndex, true);
26707 if(e.button !== 0 && isSelected){
26708 alert('rowIndex 2: ' + rowIndex);
26709 view.focusRow(rowIndex);
26710 }else if(e.ctrlKey && isSelected){
26711 this.deselectRow(rowIndex);
26712 }else if(!isSelected){
26713 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26714 view.focusRow(rowIndex);
26718 this.fireEvent("afterselectionchange", this);
26721 handleDragableRowClick : function(grid, rowIndex, e)
26723 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26724 this.selectRow(rowIndex, false);
26725 grid.view.focusRow(rowIndex);
26726 this.fireEvent("afterselectionchange", this);
26731 * Selects multiple rows.
26732 * @param {Array} rows Array of the indexes of the row to select
26733 * @param {Boolean} keepExisting (optional) True to keep existing selections
26735 selectRows : function(rows, keepExisting){
26737 this.clearSelections();
26739 for(var i = 0, len = rows.length; i < len; i++){
26740 this.selectRow(rows[i], true);
26745 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26746 * @param {Number} startRow The index of the first row in the range
26747 * @param {Number} endRow The index of the last row in the range
26748 * @param {Boolean} keepExisting (optional) True to retain existing selections
26750 selectRange : function(startRow, endRow, keepExisting){
26755 this.clearSelections();
26757 if(startRow <= endRow){
26758 for(var i = startRow; i <= endRow; i++){
26759 this.selectRow(i, true);
26762 for(var i = startRow; i >= endRow; i--){
26763 this.selectRow(i, true);
26769 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26770 * @param {Number} startRow The index of the first row in the range
26771 * @param {Number} endRow The index of the last row in the range
26773 deselectRange : function(startRow, endRow, preventViewNotify){
26777 for(var i = startRow; i <= endRow; i++){
26778 this.deselectRow(i, preventViewNotify);
26784 * @param {Number} row The index of the row to select
26785 * @param {Boolean} keepExisting (optional) True to keep existing selections
26787 selectRow : function(index, keepExisting, preventViewNotify)
26789 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26792 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26793 if(!keepExisting || this.singleSelect){
26794 this.clearSelections();
26797 var r = this.grid.store.getAt(index);
26798 //console.log('selectRow - record id :' + r.id);
26800 this.selections.add(r);
26801 this.last = this.lastActive = index;
26802 if(!preventViewNotify){
26803 var proxy = new Roo.Element(
26804 this.grid.getRowDom(index)
26806 proxy.addClass('bg-info info');
26808 this.fireEvent("rowselect", this, index, r);
26809 this.fireEvent("selectionchange", this);
26815 * @param {Number} row The index of the row to deselect
26817 deselectRow : function(index, preventViewNotify)
26822 if(this.last == index){
26825 if(this.lastActive == index){
26826 this.lastActive = false;
26829 var r = this.grid.store.getAt(index);
26834 this.selections.remove(r);
26835 //.console.log('deselectRow - record id :' + r.id);
26836 if(!preventViewNotify){
26838 var proxy = new Roo.Element(
26839 this.grid.getRowDom(index)
26841 proxy.removeClass('bg-info info');
26843 this.fireEvent("rowdeselect", this, index);
26844 this.fireEvent("selectionchange", this);
26848 restoreLast : function(){
26850 this.last = this._last;
26855 acceptsNav : function(row, col, cm){
26856 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26860 onEditorKey : function(field, e){
26861 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26866 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26868 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26870 }else if(k == e.ENTER && !e.ctrlKey){
26874 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26876 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26878 }else if(k == e.ESC){
26882 g.startEditing(newCell[0], newCell[1]);
26888 * Ext JS Library 1.1.1
26889 * Copyright(c) 2006-2007, Ext JS, LLC.
26891 * Originally Released Under LGPL - original licence link has changed is not relivant.
26894 * <script type="text/javascript">
26898 * @class Roo.bootstrap.PagingToolbar
26899 * @extends Roo.bootstrap.NavSimplebar
26900 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26902 * Create a new PagingToolbar
26903 * @param {Object} config The config object
26904 * @param {Roo.data.Store} store
26906 Roo.bootstrap.PagingToolbar = function(config)
26908 // old args format still supported... - xtype is prefered..
26909 // created from xtype...
26911 this.ds = config.dataSource;
26913 if (config.store && !this.ds) {
26914 this.store= Roo.factory(config.store, Roo.data);
26915 this.ds = this.store;
26916 this.ds.xmodule = this.xmodule || false;
26919 this.toolbarItems = [];
26920 if (config.items) {
26921 this.toolbarItems = config.items;
26924 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26929 this.bind(this.ds);
26932 if (Roo.bootstrap.version == 4) {
26933 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26935 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26940 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26942 * @cfg {Roo.data.Store} dataSource
26943 * The underlying data store providing the paged data
26946 * @cfg {String/HTMLElement/Element} container
26947 * container The id or element that will contain the toolbar
26950 * @cfg {Boolean} displayInfo
26951 * True to display the displayMsg (defaults to false)
26954 * @cfg {Number} pageSize
26955 * The number of records to display per page (defaults to 20)
26959 * @cfg {String} displayMsg
26960 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26962 displayMsg : 'Displaying {0} - {1} of {2}',
26964 * @cfg {String} emptyMsg
26965 * The message to display when no records are found (defaults to "No data to display")
26967 emptyMsg : 'No data to display',
26969 * Customizable piece of the default paging text (defaults to "Page")
26972 beforePageText : "Page",
26974 * Customizable piece of the default paging text (defaults to "of %0")
26977 afterPageText : "of {0}",
26979 * Customizable piece of the default paging text (defaults to "First Page")
26982 firstText : "First Page",
26984 * Customizable piece of the default paging text (defaults to "Previous Page")
26987 prevText : "Previous Page",
26989 * Customizable piece of the default paging text (defaults to "Next Page")
26992 nextText : "Next Page",
26994 * Customizable piece of the default paging text (defaults to "Last Page")
26997 lastText : "Last Page",
26999 * Customizable piece of the default paging text (defaults to "Refresh")
27002 refreshText : "Refresh",
27006 onRender : function(ct, position)
27008 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27009 this.navgroup.parentId = this.id;
27010 this.navgroup.onRender(this.el, null);
27011 // add the buttons to the navgroup
27013 if(this.displayInfo){
27014 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27015 this.displayEl = this.el.select('.x-paging-info', true).first();
27016 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27017 // this.displayEl = navel.el.select('span',true).first();
27023 Roo.each(_this.buttons, function(e){ // this might need to use render????
27024 Roo.factory(e).render(_this.el);
27028 Roo.each(_this.toolbarItems, function(e) {
27029 _this.navgroup.addItem(e);
27033 this.first = this.navgroup.addItem({
27034 tooltip: this.firstText,
27035 cls: "prev btn-outline-secondary",
27036 html : ' <i class="fa fa-step-backward"></i>',
27038 preventDefault: true,
27039 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27042 this.prev = this.navgroup.addItem({
27043 tooltip: this.prevText,
27044 cls: "prev btn-outline-secondary",
27045 html : ' <i class="fa fa-backward"></i>',
27047 preventDefault: true,
27048 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27050 //this.addSeparator();
27053 var field = this.navgroup.addItem( {
27055 cls : 'x-paging-position btn-outline-secondary',
27057 html : this.beforePageText +
27058 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27059 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27062 this.field = field.el.select('input', true).first();
27063 this.field.on("keydown", this.onPagingKeydown, this);
27064 this.field.on("focus", function(){this.dom.select();});
27067 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27068 //this.field.setHeight(18);
27069 //this.addSeparator();
27070 this.next = this.navgroup.addItem({
27071 tooltip: this.nextText,
27072 cls: "next btn-outline-secondary",
27073 html : ' <i class="fa fa-forward"></i>',
27075 preventDefault: true,
27076 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27078 this.last = this.navgroup.addItem({
27079 tooltip: this.lastText,
27080 html : ' <i class="fa fa-step-forward"></i>',
27081 cls: "next btn-outline-secondary",
27083 preventDefault: true,
27084 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27086 //this.addSeparator();
27087 this.loading = this.navgroup.addItem({
27088 tooltip: this.refreshText,
27089 cls: "btn-outline-secondary",
27090 html : ' <i class="fa fa-refresh"></i>',
27091 preventDefault: true,
27092 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27098 updateInfo : function(){
27099 if(this.displayEl){
27100 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27101 var msg = count == 0 ?
27105 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27107 this.displayEl.update(msg);
27112 onLoad : function(ds, r, o)
27114 this.cursor = o.params.start ? o.params.start : 0;
27116 var d = this.getPageData(),
27121 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27122 this.field.dom.value = ap;
27123 this.first.setDisabled(ap == 1);
27124 this.prev.setDisabled(ap == 1);
27125 this.next.setDisabled(ap == ps);
27126 this.last.setDisabled(ap == ps);
27127 this.loading.enable();
27132 getPageData : function(){
27133 var total = this.ds.getTotalCount();
27136 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27137 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27142 onLoadError : function(){
27143 this.loading.enable();
27147 onPagingKeydown : function(e){
27148 var k = e.getKey();
27149 var d = this.getPageData();
27151 var v = this.field.dom.value, pageNum;
27152 if(!v || isNaN(pageNum = parseInt(v, 10))){
27153 this.field.dom.value = d.activePage;
27156 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27157 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27160 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))
27162 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27163 this.field.dom.value = pageNum;
27164 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27167 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27169 var v = this.field.dom.value, pageNum;
27170 var increment = (e.shiftKey) ? 10 : 1;
27171 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27174 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27175 this.field.dom.value = d.activePage;
27178 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27180 this.field.dom.value = parseInt(v, 10) + increment;
27181 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27182 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27189 beforeLoad : function(){
27191 this.loading.disable();
27196 onClick : function(which){
27205 ds.load({params:{start: 0, limit: this.pageSize}});
27208 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27211 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27214 var total = ds.getTotalCount();
27215 var extra = total % this.pageSize;
27216 var lastStart = extra ? (total - extra) : total-this.pageSize;
27217 ds.load({params:{start: lastStart, limit: this.pageSize}});
27220 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27226 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27227 * @param {Roo.data.Store} store The data store to unbind
27229 unbind : function(ds){
27230 ds.un("beforeload", this.beforeLoad, this);
27231 ds.un("load", this.onLoad, this);
27232 ds.un("loadexception", this.onLoadError, this);
27233 ds.un("remove", this.updateInfo, this);
27234 ds.un("add", this.updateInfo, this);
27235 this.ds = undefined;
27239 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27240 * @param {Roo.data.Store} store The data store to bind
27242 bind : function(ds){
27243 ds.on("beforeload", this.beforeLoad, this);
27244 ds.on("load", this.onLoad, this);
27245 ds.on("loadexception", this.onLoadError, this);
27246 ds.on("remove", this.updateInfo, this);
27247 ds.on("add", this.updateInfo, this);
27258 * @class Roo.bootstrap.MessageBar
27259 * @extends Roo.bootstrap.Component
27260 * Bootstrap MessageBar class
27261 * @cfg {String} html contents of the MessageBar
27262 * @cfg {String} weight (info | success | warning | danger) default info
27263 * @cfg {String} beforeClass insert the bar before the given class
27264 * @cfg {Boolean} closable (true | false) default false
27265 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27268 * Create a new Element
27269 * @param {Object} config The config object
27272 Roo.bootstrap.MessageBar = function(config){
27273 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27276 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27282 beforeClass: 'bootstrap-sticky-wrap',
27284 getAutoCreate : function(){
27288 cls: 'alert alert-dismissable alert-' + this.weight,
27293 html: this.html || ''
27299 cfg.cls += ' alert-messages-fixed';
27313 onRender : function(ct, position)
27315 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27318 var cfg = Roo.apply({}, this.getAutoCreate());
27322 cfg.cls += ' ' + this.cls;
27325 cfg.style = this.style;
27327 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27329 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27332 this.el.select('>button.close').on('click', this.hide, this);
27338 if (!this.rendered) {
27344 this.fireEvent('show', this);
27350 if (!this.rendered) {
27356 this.fireEvent('hide', this);
27359 update : function()
27361 // var e = this.el.dom.firstChild;
27363 // if(this.closable){
27364 // e = e.nextSibling;
27367 // e.data = this.html || '';
27369 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27385 * @class Roo.bootstrap.Graph
27386 * @extends Roo.bootstrap.Component
27387 * Bootstrap Graph class
27391 @cfg {String} graphtype bar | vbar | pie
27392 @cfg {number} g_x coodinator | centre x (pie)
27393 @cfg {number} g_y coodinator | centre y (pie)
27394 @cfg {number} g_r radius (pie)
27395 @cfg {number} g_height height of the chart (respected by all elements in the set)
27396 @cfg {number} g_width width of the chart (respected by all elements in the set)
27397 @cfg {Object} title The title of the chart
27400 -opts (object) options for the chart
27402 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27403 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27405 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.
27406 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27408 o stretch (boolean)
27410 -opts (object) options for the pie
27413 o startAngle (number)
27414 o endAngle (number)
27418 * Create a new Input
27419 * @param {Object} config The config object
27422 Roo.bootstrap.Graph = function(config){
27423 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27429 * The img click event for the img.
27430 * @param {Roo.EventObject} e
27436 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27447 //g_colors: this.colors,
27454 getAutoCreate : function(){
27465 onRender : function(ct,position){
27468 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27470 if (typeof(Raphael) == 'undefined') {
27471 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27475 this.raphael = Raphael(this.el.dom);
27477 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27478 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27479 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27480 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27482 r.text(160, 10, "Single Series Chart").attr(txtattr);
27483 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27484 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27485 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27487 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27488 r.barchart(330, 10, 300, 220, data1);
27489 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27490 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27493 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27494 // r.barchart(30, 30, 560, 250, xdata, {
27495 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27496 // axis : "0 0 1 1",
27497 // axisxlabels : xdata
27498 // //yvalues : cols,
27501 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27503 // this.load(null,xdata,{
27504 // axis : "0 0 1 1",
27505 // axisxlabels : xdata
27510 load : function(graphtype,xdata,opts)
27512 this.raphael.clear();
27514 graphtype = this.graphtype;
27519 var r = this.raphael,
27520 fin = function () {
27521 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27523 fout = function () {
27524 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27526 pfin = function() {
27527 this.sector.stop();
27528 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27531 this.label[0].stop();
27532 this.label[0].attr({ r: 7.5 });
27533 this.label[1].attr({ "font-weight": 800 });
27536 pfout = function() {
27537 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27540 this.label[0].animate({ r: 5 }, 500, "bounce");
27541 this.label[1].attr({ "font-weight": 400 });
27547 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27550 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27553 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27554 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27556 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27563 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27568 setTitle: function(o)
27573 initEvents: function() {
27576 this.el.on('click', this.onClick, this);
27580 onClick : function(e)
27582 Roo.log('img onclick');
27583 this.fireEvent('click', this, e);
27595 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27598 * @class Roo.bootstrap.dash.NumberBox
27599 * @extends Roo.bootstrap.Component
27600 * Bootstrap NumberBox class
27601 * @cfg {String} headline Box headline
27602 * @cfg {String} content Box content
27603 * @cfg {String} icon Box icon
27604 * @cfg {String} footer Footer text
27605 * @cfg {String} fhref Footer href
27608 * Create a new NumberBox
27609 * @param {Object} config The config object
27613 Roo.bootstrap.dash.NumberBox = function(config){
27614 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27618 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27627 getAutoCreate : function(){
27631 cls : 'small-box ',
27639 cls : 'roo-headline',
27640 html : this.headline
27644 cls : 'roo-content',
27645 html : this.content
27659 cls : 'ion ' + this.icon
27668 cls : 'small-box-footer',
27669 href : this.fhref || '#',
27673 cfg.cn.push(footer);
27680 onRender : function(ct,position){
27681 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27688 setHeadline: function (value)
27690 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27693 setFooter: function (value, href)
27695 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27698 this.el.select('a.small-box-footer',true).first().attr('href', href);
27703 setContent: function (value)
27705 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27708 initEvents: function()
27722 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27725 * @class Roo.bootstrap.dash.TabBox
27726 * @extends Roo.bootstrap.Component
27727 * Bootstrap TabBox class
27728 * @cfg {String} title Title of the TabBox
27729 * @cfg {String} icon Icon of the TabBox
27730 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27731 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27734 * Create a new TabBox
27735 * @param {Object} config The config object
27739 Roo.bootstrap.dash.TabBox = function(config){
27740 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27745 * When a pane is added
27746 * @param {Roo.bootstrap.dash.TabPane} pane
27750 * @event activatepane
27751 * When a pane is activated
27752 * @param {Roo.bootstrap.dash.TabPane} pane
27754 "activatepane" : true
27762 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27767 tabScrollable : false,
27769 getChildContainer : function()
27771 return this.el.select('.tab-content', true).first();
27774 getAutoCreate : function(){
27778 cls: 'pull-left header',
27786 cls: 'fa ' + this.icon
27792 cls: 'nav nav-tabs pull-right',
27798 if(this.tabScrollable){
27805 cls: 'nav nav-tabs pull-right',
27816 cls: 'nav-tabs-custom',
27821 cls: 'tab-content no-padding',
27829 initEvents : function()
27831 //Roo.log('add add pane handler');
27832 this.on('addpane', this.onAddPane, this);
27835 * Updates the box title
27836 * @param {String} html to set the title to.
27838 setTitle : function(value)
27840 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27842 onAddPane : function(pane)
27844 this.panes.push(pane);
27845 //Roo.log('addpane');
27847 // tabs are rendere left to right..
27848 if(!this.showtabs){
27852 var ctr = this.el.select('.nav-tabs', true).first();
27855 var existing = ctr.select('.nav-tab',true);
27856 var qty = existing.getCount();;
27859 var tab = ctr.createChild({
27861 cls : 'nav-tab' + (qty ? '' : ' active'),
27869 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27872 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27874 pane.el.addClass('active');
27879 onTabClick : function(ev,un,ob,pane)
27881 //Roo.log('tab - prev default');
27882 ev.preventDefault();
27885 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27886 pane.tab.addClass('active');
27887 //Roo.log(pane.title);
27888 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27889 // technically we should have a deactivate event.. but maybe add later.
27890 // and it should not de-activate the selected tab...
27891 this.fireEvent('activatepane', pane);
27892 pane.el.addClass('active');
27893 pane.fireEvent('activate');
27898 getActivePane : function()
27901 Roo.each(this.panes, function(p) {
27902 if(p.el.hasClass('active')){
27923 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27925 * @class Roo.bootstrap.TabPane
27926 * @extends Roo.bootstrap.Component
27927 * Bootstrap TabPane class
27928 * @cfg {Boolean} active (false | true) Default false
27929 * @cfg {String} title title of panel
27933 * Create a new TabPane
27934 * @param {Object} config The config object
27937 Roo.bootstrap.dash.TabPane = function(config){
27938 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27944 * When a pane is activated
27945 * @param {Roo.bootstrap.dash.TabPane} pane
27952 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27957 // the tabBox that this is attached to.
27960 getAutoCreate : function()
27968 cfg.cls += ' active';
27973 initEvents : function()
27975 //Roo.log('trigger add pane handler');
27976 this.parent().fireEvent('addpane', this)
27980 * Updates the tab title
27981 * @param {String} html to set the title to.
27983 setTitle: function(str)
27989 this.tab.select('a', true).first().dom.innerHTML = str;
28006 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28009 * @class Roo.bootstrap.menu.Menu
28010 * @extends Roo.bootstrap.Component
28011 * Bootstrap Menu class - container for Menu
28012 * @cfg {String} html Text of the menu
28013 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28014 * @cfg {String} icon Font awesome icon
28015 * @cfg {String} pos Menu align to (top | bottom) default bottom
28019 * Create a new Menu
28020 * @param {Object} config The config object
28024 Roo.bootstrap.menu.Menu = function(config){
28025 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28029 * @event beforeshow
28030 * Fires before this menu is displayed
28031 * @param {Roo.bootstrap.menu.Menu} this
28035 * @event beforehide
28036 * Fires before this menu is hidden
28037 * @param {Roo.bootstrap.menu.Menu} this
28042 * Fires after this menu is displayed
28043 * @param {Roo.bootstrap.menu.Menu} this
28048 * Fires after this menu is hidden
28049 * @param {Roo.bootstrap.menu.Menu} this
28054 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28055 * @param {Roo.bootstrap.menu.Menu} this
28056 * @param {Roo.EventObject} e
28063 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28067 weight : 'default',
28072 getChildContainer : function() {
28073 if(this.isSubMenu){
28077 return this.el.select('ul.dropdown-menu', true).first();
28080 getAutoCreate : function()
28085 cls : 'roo-menu-text',
28093 cls : 'fa ' + this.icon
28104 cls : 'dropdown-button btn btn-' + this.weight,
28109 cls : 'dropdown-toggle btn btn-' + this.weight,
28119 cls : 'dropdown-menu'
28125 if(this.pos == 'top'){
28126 cfg.cls += ' dropup';
28129 if(this.isSubMenu){
28132 cls : 'dropdown-menu'
28139 onRender : function(ct, position)
28141 this.isSubMenu = ct.hasClass('dropdown-submenu');
28143 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28146 initEvents : function()
28148 if(this.isSubMenu){
28152 this.hidden = true;
28154 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28155 this.triggerEl.on('click', this.onTriggerPress, this);
28157 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28158 this.buttonEl.on('click', this.onClick, this);
28164 if(this.isSubMenu){
28168 return this.el.select('ul.dropdown-menu', true).first();
28171 onClick : function(e)
28173 this.fireEvent("click", this, e);
28176 onTriggerPress : function(e)
28178 if (this.isVisible()) {
28185 isVisible : function(){
28186 return !this.hidden;
28191 this.fireEvent("beforeshow", this);
28193 this.hidden = false;
28194 this.el.addClass('open');
28196 Roo.get(document).on("mouseup", this.onMouseUp, this);
28198 this.fireEvent("show", this);
28205 this.fireEvent("beforehide", this);
28207 this.hidden = true;
28208 this.el.removeClass('open');
28210 Roo.get(document).un("mouseup", this.onMouseUp);
28212 this.fireEvent("hide", this);
28215 onMouseUp : function()
28229 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28232 * @class Roo.bootstrap.menu.Item
28233 * @extends Roo.bootstrap.Component
28234 * Bootstrap MenuItem class
28235 * @cfg {Boolean} submenu (true | false) default false
28236 * @cfg {String} html text of the item
28237 * @cfg {String} href the link
28238 * @cfg {Boolean} disable (true | false) default false
28239 * @cfg {Boolean} preventDefault (true | false) default true
28240 * @cfg {String} icon Font awesome icon
28241 * @cfg {String} pos Submenu align to (left | right) default right
28245 * Create a new Item
28246 * @param {Object} config The config object
28250 Roo.bootstrap.menu.Item = function(config){
28251 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28255 * Fires when the mouse is hovering over this menu
28256 * @param {Roo.bootstrap.menu.Item} this
28257 * @param {Roo.EventObject} e
28262 * Fires when the mouse exits this menu
28263 * @param {Roo.bootstrap.menu.Item} this
28264 * @param {Roo.EventObject} e
28270 * The raw click event for the entire grid.
28271 * @param {Roo.EventObject} e
28277 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28282 preventDefault: true,
28287 getAutoCreate : function()
28292 cls : 'roo-menu-item-text',
28300 cls : 'fa ' + this.icon
28309 href : this.href || '#',
28316 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28320 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28322 if(this.pos == 'left'){
28323 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28330 initEvents : function()
28332 this.el.on('mouseover', this.onMouseOver, this);
28333 this.el.on('mouseout', this.onMouseOut, this);
28335 this.el.select('a', true).first().on('click', this.onClick, this);
28339 onClick : function(e)
28341 if(this.preventDefault){
28342 e.preventDefault();
28345 this.fireEvent("click", this, e);
28348 onMouseOver : function(e)
28350 if(this.submenu && this.pos == 'left'){
28351 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28354 this.fireEvent("mouseover", this, e);
28357 onMouseOut : function(e)
28359 this.fireEvent("mouseout", this, e);
28371 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28374 * @class Roo.bootstrap.menu.Separator
28375 * @extends Roo.bootstrap.Component
28376 * Bootstrap Separator class
28379 * Create a new Separator
28380 * @param {Object} config The config object
28384 Roo.bootstrap.menu.Separator = function(config){
28385 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28388 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28390 getAutoCreate : function(){
28411 * @class Roo.bootstrap.Tooltip
28412 * Bootstrap Tooltip class
28413 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28414 * to determine which dom element triggers the tooltip.
28416 * It needs to add support for additional attributes like tooltip-position
28419 * Create a new Toolti
28420 * @param {Object} config The config object
28423 Roo.bootstrap.Tooltip = function(config){
28424 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28426 this.alignment = Roo.bootstrap.Tooltip.alignment;
28428 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28429 this.alignment = config.alignment;
28434 Roo.apply(Roo.bootstrap.Tooltip, {
28436 * @function init initialize tooltip monitoring.
28440 currentTip : false,
28441 currentRegion : false,
28447 Roo.get(document).on('mouseover', this.enter ,this);
28448 Roo.get(document).on('mouseout', this.leave, this);
28451 this.currentTip = new Roo.bootstrap.Tooltip();
28454 enter : function(ev)
28456 var dom = ev.getTarget();
28458 //Roo.log(['enter',dom]);
28459 var el = Roo.fly(dom);
28460 if (this.currentEl) {
28462 //Roo.log(this.currentEl);
28463 //Roo.log(this.currentEl.contains(dom));
28464 if (this.currentEl == el) {
28467 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28473 if (this.currentTip.el) {
28474 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28478 if(!el || el.dom == document){
28484 // you can not look for children, as if el is the body.. then everythign is the child..
28485 if (!el.attr('tooltip')) { //
28486 if (!el.select("[tooltip]").elements.length) {
28489 // is the mouse over this child...?
28490 bindEl = el.select("[tooltip]").first();
28491 var xy = ev.getXY();
28492 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28493 //Roo.log("not in region.");
28496 //Roo.log("child element over..");
28499 this.currentEl = bindEl;
28500 this.currentTip.bind(bindEl);
28501 this.currentRegion = Roo.lib.Region.getRegion(dom);
28502 this.currentTip.enter();
28505 leave : function(ev)
28507 var dom = ev.getTarget();
28508 //Roo.log(['leave',dom]);
28509 if (!this.currentEl) {
28514 if (dom != this.currentEl.dom) {
28517 var xy = ev.getXY();
28518 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28521 // only activate leave if mouse cursor is outside... bounding box..
28526 if (this.currentTip) {
28527 this.currentTip.leave();
28529 //Roo.log('clear currentEl');
28530 this.currentEl = false;
28535 'left' : ['r-l', [-2,0], 'right'],
28536 'right' : ['l-r', [2,0], 'left'],
28537 'bottom' : ['t-b', [0,2], 'top'],
28538 'top' : [ 'b-t', [0,-2], 'bottom']
28544 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28549 delay : null, // can be { show : 300 , hide: 500}
28553 hoverState : null, //???
28555 placement : 'bottom',
28559 getAutoCreate : function(){
28566 cls : 'tooltip-arrow arrow'
28569 cls : 'tooltip-inner'
28576 bind : function(el)
28581 initEvents : function()
28583 this.arrowEl = this.el.select('.arrow', true).first();
28584 this.innerEl = this.el.select('.tooltip-inner', true).first();
28587 enter : function () {
28589 if (this.timeout != null) {
28590 clearTimeout(this.timeout);
28593 this.hoverState = 'in';
28594 //Roo.log("enter - show");
28595 if (!this.delay || !this.delay.show) {
28600 this.timeout = setTimeout(function () {
28601 if (_t.hoverState == 'in') {
28604 }, this.delay.show);
28608 clearTimeout(this.timeout);
28610 this.hoverState = 'out';
28611 if (!this.delay || !this.delay.hide) {
28617 this.timeout = setTimeout(function () {
28618 //Roo.log("leave - timeout");
28620 if (_t.hoverState == 'out') {
28622 Roo.bootstrap.Tooltip.currentEl = false;
28627 show : function (msg)
28630 this.render(document.body);
28633 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28635 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28637 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28639 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28640 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28642 var placement = typeof this.placement == 'function' ?
28643 this.placement.call(this, this.el, on_el) :
28646 var autoToken = /\s?auto?\s?/i;
28647 var autoPlace = autoToken.test(placement);
28649 placement = placement.replace(autoToken, '') || 'top';
28653 //this.el.setXY([0,0]);
28655 //this.el.dom.style.display='block';
28657 //this.el.appendTo(on_el);
28659 var p = this.getPosition();
28660 var box = this.el.getBox();
28666 var align = this.alignment[placement];
28668 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28670 if(placement == 'top' || placement == 'bottom'){
28672 placement = 'right';
28675 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28676 placement = 'left';
28679 var scroll = Roo.select('body', true).first().getScroll();
28681 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28685 align = this.alignment[placement];
28687 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28691 this.el.alignTo(this.bindEl, align[0],align[1]);
28692 //var arrow = this.el.select('.arrow',true).first();
28693 //arrow.set(align[2],
28695 this.el.addClass(placement);
28696 this.el.addClass("bs-tooltip-"+ placement);
28698 this.el.addClass('in fade show');
28700 this.hoverState = null;
28702 if (this.el.hasClass('fade')) {
28717 //this.el.setXY([0,0]);
28718 this.el.removeClass(['show', 'in']);
28734 * @class Roo.bootstrap.LocationPicker
28735 * @extends Roo.bootstrap.Component
28736 * Bootstrap LocationPicker class
28737 * @cfg {Number} latitude Position when init default 0
28738 * @cfg {Number} longitude Position when init default 0
28739 * @cfg {Number} zoom default 15
28740 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28741 * @cfg {Boolean} mapTypeControl default false
28742 * @cfg {Boolean} disableDoubleClickZoom default false
28743 * @cfg {Boolean} scrollwheel default true
28744 * @cfg {Boolean} streetViewControl default false
28745 * @cfg {Number} radius default 0
28746 * @cfg {String} locationName
28747 * @cfg {Boolean} draggable default true
28748 * @cfg {Boolean} enableAutocomplete default false
28749 * @cfg {Boolean} enableReverseGeocode default true
28750 * @cfg {String} markerTitle
28753 * Create a new LocationPicker
28754 * @param {Object} config The config object
28758 Roo.bootstrap.LocationPicker = function(config){
28760 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28765 * Fires when the picker initialized.
28766 * @param {Roo.bootstrap.LocationPicker} this
28767 * @param {Google Location} location
28771 * @event positionchanged
28772 * Fires when the picker position changed.
28773 * @param {Roo.bootstrap.LocationPicker} this
28774 * @param {Google Location} location
28776 positionchanged : true,
28779 * Fires when the map resize.
28780 * @param {Roo.bootstrap.LocationPicker} this
28785 * Fires when the map show.
28786 * @param {Roo.bootstrap.LocationPicker} this
28791 * Fires when the map hide.
28792 * @param {Roo.bootstrap.LocationPicker} this
28797 * Fires when click the map.
28798 * @param {Roo.bootstrap.LocationPicker} this
28799 * @param {Map event} e
28803 * @event mapRightClick
28804 * Fires when right click the map.
28805 * @param {Roo.bootstrap.LocationPicker} this
28806 * @param {Map event} e
28808 mapRightClick : true,
28810 * @event markerClick
28811 * Fires when click the marker.
28812 * @param {Roo.bootstrap.LocationPicker} this
28813 * @param {Map event} e
28815 markerClick : true,
28817 * @event markerRightClick
28818 * Fires when right click the marker.
28819 * @param {Roo.bootstrap.LocationPicker} this
28820 * @param {Map event} e
28822 markerRightClick : true,
28824 * @event OverlayViewDraw
28825 * Fires when OverlayView Draw
28826 * @param {Roo.bootstrap.LocationPicker} this
28828 OverlayViewDraw : true,
28830 * @event OverlayViewOnAdd
28831 * Fires when OverlayView Draw
28832 * @param {Roo.bootstrap.LocationPicker} this
28834 OverlayViewOnAdd : true,
28836 * @event OverlayViewOnRemove
28837 * Fires when OverlayView Draw
28838 * @param {Roo.bootstrap.LocationPicker} this
28840 OverlayViewOnRemove : true,
28842 * @event OverlayViewShow
28843 * Fires when OverlayView Draw
28844 * @param {Roo.bootstrap.LocationPicker} this
28845 * @param {Pixel} cpx
28847 OverlayViewShow : true,
28849 * @event OverlayViewHide
28850 * Fires when OverlayView Draw
28851 * @param {Roo.bootstrap.LocationPicker} this
28853 OverlayViewHide : true,
28855 * @event loadexception
28856 * Fires when load google lib failed.
28857 * @param {Roo.bootstrap.LocationPicker} this
28859 loadexception : true
28864 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28866 gMapContext: false,
28872 mapTypeControl: false,
28873 disableDoubleClickZoom: false,
28875 streetViewControl: false,
28879 enableAutocomplete: false,
28880 enableReverseGeocode: true,
28883 getAutoCreate: function()
28888 cls: 'roo-location-picker'
28894 initEvents: function(ct, position)
28896 if(!this.el.getWidth() || this.isApplied()){
28900 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28905 initial: function()
28907 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28908 this.fireEvent('loadexception', this);
28912 if(!this.mapTypeId){
28913 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28916 this.gMapContext = this.GMapContext();
28918 this.initOverlayView();
28920 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28924 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28925 _this.setPosition(_this.gMapContext.marker.position);
28928 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28929 _this.fireEvent('mapClick', this, event);
28933 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28934 _this.fireEvent('mapRightClick', this, event);
28938 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28939 _this.fireEvent('markerClick', this, event);
28943 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28944 _this.fireEvent('markerRightClick', this, event);
28948 this.setPosition(this.gMapContext.location);
28950 this.fireEvent('initial', this, this.gMapContext.location);
28953 initOverlayView: function()
28957 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28961 _this.fireEvent('OverlayViewDraw', _this);
28966 _this.fireEvent('OverlayViewOnAdd', _this);
28969 onRemove: function()
28971 _this.fireEvent('OverlayViewOnRemove', _this);
28974 show: function(cpx)
28976 _this.fireEvent('OverlayViewShow', _this, cpx);
28981 _this.fireEvent('OverlayViewHide', _this);
28987 fromLatLngToContainerPixel: function(event)
28989 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28992 isApplied: function()
28994 return this.getGmapContext() == false ? false : true;
28997 getGmapContext: function()
28999 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29002 GMapContext: function()
29004 var position = new google.maps.LatLng(this.latitude, this.longitude);
29006 var _map = new google.maps.Map(this.el.dom, {
29009 mapTypeId: this.mapTypeId,
29010 mapTypeControl: this.mapTypeControl,
29011 disableDoubleClickZoom: this.disableDoubleClickZoom,
29012 scrollwheel: this.scrollwheel,
29013 streetViewControl: this.streetViewControl,
29014 locationName: this.locationName,
29015 draggable: this.draggable,
29016 enableAutocomplete: this.enableAutocomplete,
29017 enableReverseGeocode: this.enableReverseGeocode
29020 var _marker = new google.maps.Marker({
29021 position: position,
29023 title: this.markerTitle,
29024 draggable: this.draggable
29031 location: position,
29032 radius: this.radius,
29033 locationName: this.locationName,
29034 addressComponents: {
29035 formatted_address: null,
29036 addressLine1: null,
29037 addressLine2: null,
29039 streetNumber: null,
29043 stateOrProvince: null
29046 domContainer: this.el.dom,
29047 geodecoder: new google.maps.Geocoder()
29051 drawCircle: function(center, radius, options)
29053 if (this.gMapContext.circle != null) {
29054 this.gMapContext.circle.setMap(null);
29058 options = Roo.apply({}, options, {
29059 strokeColor: "#0000FF",
29060 strokeOpacity: .35,
29062 fillColor: "#0000FF",
29066 options.map = this.gMapContext.map;
29067 options.radius = radius;
29068 options.center = center;
29069 this.gMapContext.circle = new google.maps.Circle(options);
29070 return this.gMapContext.circle;
29076 setPosition: function(location)
29078 this.gMapContext.location = location;
29079 this.gMapContext.marker.setPosition(location);
29080 this.gMapContext.map.panTo(location);
29081 this.drawCircle(location, this.gMapContext.radius, {});
29085 if (this.gMapContext.settings.enableReverseGeocode) {
29086 this.gMapContext.geodecoder.geocode({
29087 latLng: this.gMapContext.location
29088 }, function(results, status) {
29090 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29091 _this.gMapContext.locationName = results[0].formatted_address;
29092 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29094 _this.fireEvent('positionchanged', this, location);
29101 this.fireEvent('positionchanged', this, location);
29106 google.maps.event.trigger(this.gMapContext.map, "resize");
29108 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29110 this.fireEvent('resize', this);
29113 setPositionByLatLng: function(latitude, longitude)
29115 this.setPosition(new google.maps.LatLng(latitude, longitude));
29118 getCurrentPosition: function()
29121 latitude: this.gMapContext.location.lat(),
29122 longitude: this.gMapContext.location.lng()
29126 getAddressName: function()
29128 return this.gMapContext.locationName;
29131 getAddressComponents: function()
29133 return this.gMapContext.addressComponents;
29136 address_component_from_google_geocode: function(address_components)
29140 for (var i = 0; i < address_components.length; i++) {
29141 var component = address_components[i];
29142 if (component.types.indexOf("postal_code") >= 0) {
29143 result.postalCode = component.short_name;
29144 } else if (component.types.indexOf("street_number") >= 0) {
29145 result.streetNumber = component.short_name;
29146 } else if (component.types.indexOf("route") >= 0) {
29147 result.streetName = component.short_name;
29148 } else if (component.types.indexOf("neighborhood") >= 0) {
29149 result.city = component.short_name;
29150 } else if (component.types.indexOf("locality") >= 0) {
29151 result.city = component.short_name;
29152 } else if (component.types.indexOf("sublocality") >= 0) {
29153 result.district = component.short_name;
29154 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29155 result.stateOrProvince = component.short_name;
29156 } else if (component.types.indexOf("country") >= 0) {
29157 result.country = component.short_name;
29161 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29162 result.addressLine2 = "";
29166 setZoomLevel: function(zoom)
29168 this.gMapContext.map.setZoom(zoom);
29181 this.fireEvent('show', this);
29192 this.fireEvent('hide', this);
29197 Roo.apply(Roo.bootstrap.LocationPicker, {
29199 OverlayView : function(map, options)
29201 options = options || {};
29208 * @class Roo.bootstrap.Alert
29209 * @extends Roo.bootstrap.Component
29210 * Bootstrap Alert class - shows an alert area box
29212 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29213 Enter a valid email address
29216 * @cfg {String} title The title of alert
29217 * @cfg {String} html The content of alert
29218 * @cfg {String} weight ( success | info | warning | danger )
29219 * @cfg {String} faicon font-awesomeicon
29222 * Create a new alert
29223 * @param {Object} config The config object
29227 Roo.bootstrap.Alert = function(config){
29228 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29232 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29239 getAutoCreate : function()
29248 cls : 'roo-alert-icon'
29253 cls : 'roo-alert-title',
29258 cls : 'roo-alert-text',
29265 cfg.cn[0].cls += ' fa ' + this.faicon;
29269 cfg.cls += ' alert-' + this.weight;
29275 initEvents: function()
29277 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29280 setTitle : function(str)
29282 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29285 setText : function(str)
29287 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29290 setWeight : function(weight)
29293 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29296 this.weight = weight;
29298 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29301 setIcon : function(icon)
29304 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29307 this.faicon = icon;
29309 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29330 * @class Roo.bootstrap.UploadCropbox
29331 * @extends Roo.bootstrap.Component
29332 * Bootstrap UploadCropbox class
29333 * @cfg {String} emptyText show when image has been loaded
29334 * @cfg {String} rotateNotify show when image too small to rotate
29335 * @cfg {Number} errorTimeout default 3000
29336 * @cfg {Number} minWidth default 300
29337 * @cfg {Number} minHeight default 300
29338 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29339 * @cfg {Boolean} isDocument (true|false) default false
29340 * @cfg {String} url action url
29341 * @cfg {String} paramName default 'imageUpload'
29342 * @cfg {String} method default POST
29343 * @cfg {Boolean} loadMask (true|false) default true
29344 * @cfg {Boolean} loadingText default 'Loading...'
29347 * Create a new UploadCropbox
29348 * @param {Object} config The config object
29351 Roo.bootstrap.UploadCropbox = function(config){
29352 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29356 * @event beforeselectfile
29357 * Fire before select file
29358 * @param {Roo.bootstrap.UploadCropbox} this
29360 "beforeselectfile" : true,
29363 * Fire after initEvent
29364 * @param {Roo.bootstrap.UploadCropbox} this
29369 * Fire after initEvent
29370 * @param {Roo.bootstrap.UploadCropbox} this
29371 * @param {String} data
29376 * Fire when preparing the file data
29377 * @param {Roo.bootstrap.UploadCropbox} this
29378 * @param {Object} file
29383 * Fire when get exception
29384 * @param {Roo.bootstrap.UploadCropbox} this
29385 * @param {XMLHttpRequest} xhr
29387 "exception" : true,
29389 * @event beforeloadcanvas
29390 * Fire before load the canvas
29391 * @param {Roo.bootstrap.UploadCropbox} this
29392 * @param {String} src
29394 "beforeloadcanvas" : true,
29397 * Fire when trash image
29398 * @param {Roo.bootstrap.UploadCropbox} this
29403 * Fire when download the image
29404 * @param {Roo.bootstrap.UploadCropbox} this
29408 * @event footerbuttonclick
29409 * Fire when footerbuttonclick
29410 * @param {Roo.bootstrap.UploadCropbox} this
29411 * @param {String} type
29413 "footerbuttonclick" : true,
29417 * @param {Roo.bootstrap.UploadCropbox} this
29422 * Fire when rotate the image
29423 * @param {Roo.bootstrap.UploadCropbox} this
29424 * @param {String} pos
29429 * Fire when inspect the file
29430 * @param {Roo.bootstrap.UploadCropbox} this
29431 * @param {Object} file
29436 * Fire when xhr upload the file
29437 * @param {Roo.bootstrap.UploadCropbox} this
29438 * @param {Object} data
29443 * Fire when arrange the file data
29444 * @param {Roo.bootstrap.UploadCropbox} this
29445 * @param {Object} formData
29450 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29453 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29455 emptyText : 'Click to upload image',
29456 rotateNotify : 'Image is too small to rotate',
29457 errorTimeout : 3000,
29471 cropType : 'image/jpeg',
29473 canvasLoaded : false,
29474 isDocument : false,
29476 paramName : 'imageUpload',
29478 loadingText : 'Loading...',
29481 getAutoCreate : function()
29485 cls : 'roo-upload-cropbox',
29489 cls : 'roo-upload-cropbox-selector',
29494 cls : 'roo-upload-cropbox-body',
29495 style : 'cursor:pointer',
29499 cls : 'roo-upload-cropbox-preview'
29503 cls : 'roo-upload-cropbox-thumb'
29507 cls : 'roo-upload-cropbox-empty-notify',
29508 html : this.emptyText
29512 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29513 html : this.rotateNotify
29519 cls : 'roo-upload-cropbox-footer',
29522 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29532 onRender : function(ct, position)
29534 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29536 if (this.buttons.length) {
29538 Roo.each(this.buttons, function(bb) {
29540 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29542 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29548 this.maskEl = this.el;
29552 initEvents : function()
29554 this.urlAPI = (window.createObjectURL && window) ||
29555 (window.URL && URL.revokeObjectURL && URL) ||
29556 (window.webkitURL && webkitURL);
29558 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29559 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29561 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29562 this.selectorEl.hide();
29564 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29565 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29567 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29568 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29569 this.thumbEl.hide();
29571 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29572 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29574 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29575 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29576 this.errorEl.hide();
29578 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29579 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29580 this.footerEl.hide();
29582 this.setThumbBoxSize();
29588 this.fireEvent('initial', this);
29595 window.addEventListener("resize", function() { _this.resize(); } );
29597 this.bodyEl.on('click', this.beforeSelectFile, this);
29600 this.bodyEl.on('touchstart', this.onTouchStart, this);
29601 this.bodyEl.on('touchmove', this.onTouchMove, this);
29602 this.bodyEl.on('touchend', this.onTouchEnd, this);
29606 this.bodyEl.on('mousedown', this.onMouseDown, this);
29607 this.bodyEl.on('mousemove', this.onMouseMove, this);
29608 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29609 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29610 Roo.get(document).on('mouseup', this.onMouseUp, this);
29613 this.selectorEl.on('change', this.onFileSelected, this);
29619 this.baseScale = 1;
29621 this.baseRotate = 1;
29622 this.dragable = false;
29623 this.pinching = false;
29626 this.cropData = false;
29627 this.notifyEl.dom.innerHTML = this.emptyText;
29629 this.selectorEl.dom.value = '';
29633 resize : function()
29635 if(this.fireEvent('resize', this) != false){
29636 this.setThumbBoxPosition();
29637 this.setCanvasPosition();
29641 onFooterButtonClick : function(e, el, o, type)
29644 case 'rotate-left' :
29645 this.onRotateLeft(e);
29647 case 'rotate-right' :
29648 this.onRotateRight(e);
29651 this.beforeSelectFile(e);
29666 this.fireEvent('footerbuttonclick', this, type);
29669 beforeSelectFile : function(e)
29671 e.preventDefault();
29673 if(this.fireEvent('beforeselectfile', this) != false){
29674 this.selectorEl.dom.click();
29678 onFileSelected : function(e)
29680 e.preventDefault();
29682 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29686 var file = this.selectorEl.dom.files[0];
29688 if(this.fireEvent('inspect', this, file) != false){
29689 this.prepare(file);
29694 trash : function(e)
29696 this.fireEvent('trash', this);
29699 download : function(e)
29701 this.fireEvent('download', this);
29704 loadCanvas : function(src)
29706 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29710 this.imageEl = document.createElement('img');
29714 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29716 this.imageEl.src = src;
29720 onLoadCanvas : function()
29722 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29723 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29725 this.bodyEl.un('click', this.beforeSelectFile, this);
29727 this.notifyEl.hide();
29728 this.thumbEl.show();
29729 this.footerEl.show();
29731 this.baseRotateLevel();
29733 if(this.isDocument){
29734 this.setThumbBoxSize();
29737 this.setThumbBoxPosition();
29739 this.baseScaleLevel();
29745 this.canvasLoaded = true;
29748 this.maskEl.unmask();
29753 setCanvasPosition : function()
29755 if(!this.canvasEl){
29759 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29760 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29762 this.previewEl.setLeft(pw);
29763 this.previewEl.setTop(ph);
29767 onMouseDown : function(e)
29771 this.dragable = true;
29772 this.pinching = false;
29774 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29775 this.dragable = false;
29779 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29780 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29784 onMouseMove : function(e)
29788 if(!this.canvasLoaded){
29792 if (!this.dragable){
29796 var minX = Math.ceil(this.thumbEl.getLeft(true));
29797 var minY = Math.ceil(this.thumbEl.getTop(true));
29799 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29800 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29802 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29803 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29805 x = x - this.mouseX;
29806 y = y - this.mouseY;
29808 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29809 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29811 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29812 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29814 this.previewEl.setLeft(bgX);
29815 this.previewEl.setTop(bgY);
29817 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29818 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29821 onMouseUp : function(e)
29825 this.dragable = false;
29828 onMouseWheel : function(e)
29832 this.startScale = this.scale;
29834 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29836 if(!this.zoomable()){
29837 this.scale = this.startScale;
29846 zoomable : function()
29848 var minScale = this.thumbEl.getWidth() / this.minWidth;
29850 if(this.minWidth < this.minHeight){
29851 minScale = this.thumbEl.getHeight() / this.minHeight;
29854 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29855 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29859 (this.rotate == 0 || this.rotate == 180) &&
29861 width > this.imageEl.OriginWidth ||
29862 height > this.imageEl.OriginHeight ||
29863 (width < this.minWidth && height < this.minHeight)
29871 (this.rotate == 90 || this.rotate == 270) &&
29873 width > this.imageEl.OriginWidth ||
29874 height > this.imageEl.OriginHeight ||
29875 (width < this.minHeight && height < this.minWidth)
29882 !this.isDocument &&
29883 (this.rotate == 0 || this.rotate == 180) &&
29885 width < this.minWidth ||
29886 width > this.imageEl.OriginWidth ||
29887 height < this.minHeight ||
29888 height > this.imageEl.OriginHeight
29895 !this.isDocument &&
29896 (this.rotate == 90 || this.rotate == 270) &&
29898 width < this.minHeight ||
29899 width > this.imageEl.OriginWidth ||
29900 height < this.minWidth ||
29901 height > this.imageEl.OriginHeight
29911 onRotateLeft : function(e)
29913 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29915 var minScale = this.thumbEl.getWidth() / this.minWidth;
29917 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29918 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29920 this.startScale = this.scale;
29922 while (this.getScaleLevel() < minScale){
29924 this.scale = this.scale + 1;
29926 if(!this.zoomable()){
29931 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29932 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29937 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29944 this.scale = this.startScale;
29946 this.onRotateFail();
29951 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29953 if(this.isDocument){
29954 this.setThumbBoxSize();
29955 this.setThumbBoxPosition();
29956 this.setCanvasPosition();
29961 this.fireEvent('rotate', this, 'left');
29965 onRotateRight : function(e)
29967 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29969 var minScale = this.thumbEl.getWidth() / this.minWidth;
29971 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29972 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29974 this.startScale = this.scale;
29976 while (this.getScaleLevel() < minScale){
29978 this.scale = this.scale + 1;
29980 if(!this.zoomable()){
29985 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29986 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29991 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29998 this.scale = this.startScale;
30000 this.onRotateFail();
30005 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30007 if(this.isDocument){
30008 this.setThumbBoxSize();
30009 this.setThumbBoxPosition();
30010 this.setCanvasPosition();
30015 this.fireEvent('rotate', this, 'right');
30018 onRotateFail : function()
30020 this.errorEl.show(true);
30024 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30029 this.previewEl.dom.innerHTML = '';
30031 var canvasEl = document.createElement("canvas");
30033 var contextEl = canvasEl.getContext("2d");
30035 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30036 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30037 var center = this.imageEl.OriginWidth / 2;
30039 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30040 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30041 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30042 center = this.imageEl.OriginHeight / 2;
30045 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30047 contextEl.translate(center, center);
30048 contextEl.rotate(this.rotate * Math.PI / 180);
30050 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30052 this.canvasEl = document.createElement("canvas");
30054 this.contextEl = this.canvasEl.getContext("2d");
30056 switch (this.rotate) {
30059 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30060 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30062 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30067 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30068 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30070 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30071 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);
30075 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30080 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30081 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30083 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30084 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);
30088 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);
30093 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30094 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30096 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30097 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30101 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);
30108 this.previewEl.appendChild(this.canvasEl);
30110 this.setCanvasPosition();
30115 if(!this.canvasLoaded){
30119 var imageCanvas = document.createElement("canvas");
30121 var imageContext = imageCanvas.getContext("2d");
30123 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30124 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30126 var center = imageCanvas.width / 2;
30128 imageContext.translate(center, center);
30130 imageContext.rotate(this.rotate * Math.PI / 180);
30132 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30134 var canvas = document.createElement("canvas");
30136 var context = canvas.getContext("2d");
30138 canvas.width = this.minWidth;
30139 canvas.height = this.minHeight;
30141 switch (this.rotate) {
30144 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30145 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30147 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30148 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30150 var targetWidth = this.minWidth - 2 * x;
30151 var targetHeight = this.minHeight - 2 * y;
30155 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30156 scale = targetWidth / width;
30159 if(x > 0 && y == 0){
30160 scale = targetHeight / height;
30163 if(x > 0 && y > 0){
30164 scale = targetWidth / width;
30166 if(width < height){
30167 scale = targetHeight / height;
30171 context.scale(scale, scale);
30173 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30174 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30176 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30177 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30179 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30184 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30185 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30187 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30188 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30190 var targetWidth = this.minWidth - 2 * x;
30191 var targetHeight = this.minHeight - 2 * y;
30195 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30196 scale = targetWidth / width;
30199 if(x > 0 && y == 0){
30200 scale = targetHeight / height;
30203 if(x > 0 && y > 0){
30204 scale = targetWidth / width;
30206 if(width < height){
30207 scale = targetHeight / height;
30211 context.scale(scale, scale);
30213 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30214 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30216 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30217 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30219 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30221 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30226 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30227 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30229 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30230 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30232 var targetWidth = this.minWidth - 2 * x;
30233 var targetHeight = this.minHeight - 2 * y;
30237 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30238 scale = targetWidth / width;
30241 if(x > 0 && y == 0){
30242 scale = targetHeight / height;
30245 if(x > 0 && y > 0){
30246 scale = targetWidth / width;
30248 if(width < height){
30249 scale = targetHeight / height;
30253 context.scale(scale, scale);
30255 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30256 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30258 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30259 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30261 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30262 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30264 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30269 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30270 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30272 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30273 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30275 var targetWidth = this.minWidth - 2 * x;
30276 var targetHeight = this.minHeight - 2 * y;
30280 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30281 scale = targetWidth / width;
30284 if(x > 0 && y == 0){
30285 scale = targetHeight / height;
30288 if(x > 0 && y > 0){
30289 scale = targetWidth / width;
30291 if(width < height){
30292 scale = targetHeight / height;
30296 context.scale(scale, scale);
30298 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30299 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30301 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30302 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30304 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30306 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30313 this.cropData = canvas.toDataURL(this.cropType);
30315 if(this.fireEvent('crop', this, this.cropData) !== false){
30316 this.process(this.file, this.cropData);
30323 setThumbBoxSize : function()
30327 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30328 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30329 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30331 this.minWidth = width;
30332 this.minHeight = height;
30334 if(this.rotate == 90 || this.rotate == 270){
30335 this.minWidth = height;
30336 this.minHeight = width;
30341 width = Math.ceil(this.minWidth * height / this.minHeight);
30343 if(this.minWidth > this.minHeight){
30345 height = Math.ceil(this.minHeight * width / this.minWidth);
30348 this.thumbEl.setStyle({
30349 width : width + 'px',
30350 height : height + 'px'
30357 setThumbBoxPosition : function()
30359 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30360 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30362 this.thumbEl.setLeft(x);
30363 this.thumbEl.setTop(y);
30367 baseRotateLevel : function()
30369 this.baseRotate = 1;
30372 typeof(this.exif) != 'undefined' &&
30373 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30374 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30376 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30379 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30383 baseScaleLevel : function()
30387 if(this.isDocument){
30389 if(this.baseRotate == 6 || this.baseRotate == 8){
30391 height = this.thumbEl.getHeight();
30392 this.baseScale = height / this.imageEl.OriginWidth;
30394 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30395 width = this.thumbEl.getWidth();
30396 this.baseScale = width / this.imageEl.OriginHeight;
30402 height = this.thumbEl.getHeight();
30403 this.baseScale = height / this.imageEl.OriginHeight;
30405 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30406 width = this.thumbEl.getWidth();
30407 this.baseScale = width / this.imageEl.OriginWidth;
30413 if(this.baseRotate == 6 || this.baseRotate == 8){
30415 width = this.thumbEl.getHeight();
30416 this.baseScale = width / this.imageEl.OriginHeight;
30418 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30419 height = this.thumbEl.getWidth();
30420 this.baseScale = height / this.imageEl.OriginHeight;
30423 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30424 height = this.thumbEl.getWidth();
30425 this.baseScale = height / this.imageEl.OriginHeight;
30427 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30428 width = this.thumbEl.getHeight();
30429 this.baseScale = width / this.imageEl.OriginWidth;
30436 width = this.thumbEl.getWidth();
30437 this.baseScale = width / this.imageEl.OriginWidth;
30439 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30440 height = this.thumbEl.getHeight();
30441 this.baseScale = height / this.imageEl.OriginHeight;
30444 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30446 height = this.thumbEl.getHeight();
30447 this.baseScale = height / this.imageEl.OriginHeight;
30449 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30450 width = this.thumbEl.getWidth();
30451 this.baseScale = width / this.imageEl.OriginWidth;
30459 getScaleLevel : function()
30461 return this.baseScale * Math.pow(1.1, this.scale);
30464 onTouchStart : function(e)
30466 if(!this.canvasLoaded){
30467 this.beforeSelectFile(e);
30471 var touches = e.browserEvent.touches;
30477 if(touches.length == 1){
30478 this.onMouseDown(e);
30482 if(touches.length != 2){
30488 for(var i = 0, finger; finger = touches[i]; i++){
30489 coords.push(finger.pageX, finger.pageY);
30492 var x = Math.pow(coords[0] - coords[2], 2);
30493 var y = Math.pow(coords[1] - coords[3], 2);
30495 this.startDistance = Math.sqrt(x + y);
30497 this.startScale = this.scale;
30499 this.pinching = true;
30500 this.dragable = false;
30504 onTouchMove : function(e)
30506 if(!this.pinching && !this.dragable){
30510 var touches = e.browserEvent.touches;
30517 this.onMouseMove(e);
30523 for(var i = 0, finger; finger = touches[i]; i++){
30524 coords.push(finger.pageX, finger.pageY);
30527 var x = Math.pow(coords[0] - coords[2], 2);
30528 var y = Math.pow(coords[1] - coords[3], 2);
30530 this.endDistance = Math.sqrt(x + y);
30532 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30534 if(!this.zoomable()){
30535 this.scale = this.startScale;
30543 onTouchEnd : function(e)
30545 this.pinching = false;
30546 this.dragable = false;
30550 process : function(file, crop)
30553 this.maskEl.mask(this.loadingText);
30556 this.xhr = new XMLHttpRequest();
30558 file.xhr = this.xhr;
30560 this.xhr.open(this.method, this.url, true);
30563 "Accept": "application/json",
30564 "Cache-Control": "no-cache",
30565 "X-Requested-With": "XMLHttpRequest"
30568 for (var headerName in headers) {
30569 var headerValue = headers[headerName];
30571 this.xhr.setRequestHeader(headerName, headerValue);
30577 this.xhr.onload = function()
30579 _this.xhrOnLoad(_this.xhr);
30582 this.xhr.onerror = function()
30584 _this.xhrOnError(_this.xhr);
30587 var formData = new FormData();
30589 formData.append('returnHTML', 'NO');
30592 formData.append('crop', crop);
30595 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30596 formData.append(this.paramName, file, file.name);
30599 if(typeof(file.filename) != 'undefined'){
30600 formData.append('filename', file.filename);
30603 if(typeof(file.mimetype) != 'undefined'){
30604 formData.append('mimetype', file.mimetype);
30607 if(this.fireEvent('arrange', this, formData) != false){
30608 this.xhr.send(formData);
30612 xhrOnLoad : function(xhr)
30615 this.maskEl.unmask();
30618 if (xhr.readyState !== 4) {
30619 this.fireEvent('exception', this, xhr);
30623 var response = Roo.decode(xhr.responseText);
30625 if(!response.success){
30626 this.fireEvent('exception', this, xhr);
30630 var response = Roo.decode(xhr.responseText);
30632 this.fireEvent('upload', this, response);
30636 xhrOnError : function()
30639 this.maskEl.unmask();
30642 Roo.log('xhr on error');
30644 var response = Roo.decode(xhr.responseText);
30650 prepare : function(file)
30653 this.maskEl.mask(this.loadingText);
30659 if(typeof(file) === 'string'){
30660 this.loadCanvas(file);
30664 if(!file || !this.urlAPI){
30669 this.cropType = file.type;
30673 if(this.fireEvent('prepare', this, this.file) != false){
30675 var reader = new FileReader();
30677 reader.onload = function (e) {
30678 if (e.target.error) {
30679 Roo.log(e.target.error);
30683 var buffer = e.target.result,
30684 dataView = new DataView(buffer),
30686 maxOffset = dataView.byteLength - 4,
30690 if (dataView.getUint16(0) === 0xffd8) {
30691 while (offset < maxOffset) {
30692 markerBytes = dataView.getUint16(offset);
30694 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30695 markerLength = dataView.getUint16(offset + 2) + 2;
30696 if (offset + markerLength > dataView.byteLength) {
30697 Roo.log('Invalid meta data: Invalid segment size.');
30701 if(markerBytes == 0xffe1){
30702 _this.parseExifData(
30709 offset += markerLength;
30719 var url = _this.urlAPI.createObjectURL(_this.file);
30721 _this.loadCanvas(url);
30726 reader.readAsArrayBuffer(this.file);
30732 parseExifData : function(dataView, offset, length)
30734 var tiffOffset = offset + 10,
30738 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30739 // No Exif data, might be XMP data instead
30743 // Check for the ASCII code for "Exif" (0x45786966):
30744 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30745 // No Exif data, might be XMP data instead
30748 if (tiffOffset + 8 > dataView.byteLength) {
30749 Roo.log('Invalid Exif data: Invalid segment size.');
30752 // Check for the two null bytes:
30753 if (dataView.getUint16(offset + 8) !== 0x0000) {
30754 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30757 // Check the byte alignment:
30758 switch (dataView.getUint16(tiffOffset)) {
30760 littleEndian = true;
30763 littleEndian = false;
30766 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30769 // Check for the TIFF tag marker (0x002A):
30770 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30771 Roo.log('Invalid Exif data: Missing TIFF marker.');
30774 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30775 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30777 this.parseExifTags(
30780 tiffOffset + dirOffset,
30785 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30790 if (dirOffset + 6 > dataView.byteLength) {
30791 Roo.log('Invalid Exif data: Invalid directory offset.');
30794 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30795 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30796 if (dirEndOffset + 4 > dataView.byteLength) {
30797 Roo.log('Invalid Exif data: Invalid directory size.');
30800 for (i = 0; i < tagsNumber; i += 1) {
30804 dirOffset + 2 + 12 * i, // tag offset
30808 // Return the offset to the next directory:
30809 return dataView.getUint32(dirEndOffset, littleEndian);
30812 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30814 var tag = dataView.getUint16(offset, littleEndian);
30816 this.exif[tag] = this.getExifValue(
30820 dataView.getUint16(offset + 2, littleEndian), // tag type
30821 dataView.getUint32(offset + 4, littleEndian), // tag length
30826 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30828 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30837 Roo.log('Invalid Exif data: Invalid tag type.');
30841 tagSize = tagType.size * length;
30842 // Determine if the value is contained in the dataOffset bytes,
30843 // or if the value at the dataOffset is a pointer to the actual data:
30844 dataOffset = tagSize > 4 ?
30845 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30846 if (dataOffset + tagSize > dataView.byteLength) {
30847 Roo.log('Invalid Exif data: Invalid data offset.');
30850 if (length === 1) {
30851 return tagType.getValue(dataView, dataOffset, littleEndian);
30854 for (i = 0; i < length; i += 1) {
30855 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30858 if (tagType.ascii) {
30860 // Concatenate the chars:
30861 for (i = 0; i < values.length; i += 1) {
30863 // Ignore the terminating NULL byte(s):
30864 if (c === '\u0000') {
30876 Roo.apply(Roo.bootstrap.UploadCropbox, {
30878 'Orientation': 0x0112
30882 1: 0, //'top-left',
30884 3: 180, //'bottom-right',
30885 // 4: 'bottom-left',
30887 6: 90, //'right-top',
30888 // 7: 'right-bottom',
30889 8: 270 //'left-bottom'
30893 // byte, 8-bit unsigned int:
30895 getValue: function (dataView, dataOffset) {
30896 return dataView.getUint8(dataOffset);
30900 // ascii, 8-bit byte:
30902 getValue: function (dataView, dataOffset) {
30903 return String.fromCharCode(dataView.getUint8(dataOffset));
30908 // short, 16 bit int:
30910 getValue: function (dataView, dataOffset, littleEndian) {
30911 return dataView.getUint16(dataOffset, littleEndian);
30915 // long, 32 bit int:
30917 getValue: function (dataView, dataOffset, littleEndian) {
30918 return dataView.getUint32(dataOffset, littleEndian);
30922 // rational = two long values, first is numerator, second is denominator:
30924 getValue: function (dataView, dataOffset, littleEndian) {
30925 return dataView.getUint32(dataOffset, littleEndian) /
30926 dataView.getUint32(dataOffset + 4, littleEndian);
30930 // slong, 32 bit signed int:
30932 getValue: function (dataView, dataOffset, littleEndian) {
30933 return dataView.getInt32(dataOffset, littleEndian);
30937 // srational, two slongs, first is numerator, second is denominator:
30939 getValue: function (dataView, dataOffset, littleEndian) {
30940 return dataView.getInt32(dataOffset, littleEndian) /
30941 dataView.getInt32(dataOffset + 4, littleEndian);
30951 cls : 'btn-group roo-upload-cropbox-rotate-left',
30952 action : 'rotate-left',
30956 cls : 'btn btn-default',
30957 html : '<i class="fa fa-undo"></i>'
30963 cls : 'btn-group roo-upload-cropbox-picture',
30964 action : 'picture',
30968 cls : 'btn btn-default',
30969 html : '<i class="fa fa-picture-o"></i>'
30975 cls : 'btn-group roo-upload-cropbox-rotate-right',
30976 action : 'rotate-right',
30980 cls : 'btn btn-default',
30981 html : '<i class="fa fa-repeat"></i>'
30989 cls : 'btn-group roo-upload-cropbox-rotate-left',
30990 action : 'rotate-left',
30994 cls : 'btn btn-default',
30995 html : '<i class="fa fa-undo"></i>'
31001 cls : 'btn-group roo-upload-cropbox-download',
31002 action : 'download',
31006 cls : 'btn btn-default',
31007 html : '<i class="fa fa-download"></i>'
31013 cls : 'btn-group roo-upload-cropbox-crop',
31018 cls : 'btn btn-default',
31019 html : '<i class="fa fa-crop"></i>'
31025 cls : 'btn-group roo-upload-cropbox-trash',
31030 cls : 'btn btn-default',
31031 html : '<i class="fa fa-trash"></i>'
31037 cls : 'btn-group roo-upload-cropbox-rotate-right',
31038 action : 'rotate-right',
31042 cls : 'btn btn-default',
31043 html : '<i class="fa fa-repeat"></i>'
31051 cls : 'btn-group roo-upload-cropbox-rotate-left',
31052 action : 'rotate-left',
31056 cls : 'btn btn-default',
31057 html : '<i class="fa fa-undo"></i>'
31063 cls : 'btn-group roo-upload-cropbox-rotate-right',
31064 action : 'rotate-right',
31068 cls : 'btn btn-default',
31069 html : '<i class="fa fa-repeat"></i>'
31082 * @class Roo.bootstrap.DocumentManager
31083 * @extends Roo.bootstrap.Component
31084 * Bootstrap DocumentManager class
31085 * @cfg {String} paramName default 'imageUpload'
31086 * @cfg {String} toolTipName default 'filename'
31087 * @cfg {String} method default POST
31088 * @cfg {String} url action url
31089 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31090 * @cfg {Boolean} multiple multiple upload default true
31091 * @cfg {Number} thumbSize default 300
31092 * @cfg {String} fieldLabel
31093 * @cfg {Number} labelWidth default 4
31094 * @cfg {String} labelAlign (left|top) default left
31095 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31096 * @cfg {Number} labellg set the width of label (1-12)
31097 * @cfg {Number} labelmd set the width of label (1-12)
31098 * @cfg {Number} labelsm set the width of label (1-12)
31099 * @cfg {Number} labelxs set the width of label (1-12)
31102 * Create a new DocumentManager
31103 * @param {Object} config The config object
31106 Roo.bootstrap.DocumentManager = function(config){
31107 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31110 this.delegates = [];
31115 * Fire when initial the DocumentManager
31116 * @param {Roo.bootstrap.DocumentManager} this
31121 * inspect selected file
31122 * @param {Roo.bootstrap.DocumentManager} this
31123 * @param {File} file
31128 * Fire when xhr load exception
31129 * @param {Roo.bootstrap.DocumentManager} this
31130 * @param {XMLHttpRequest} xhr
31132 "exception" : true,
31134 * @event afterupload
31135 * Fire when xhr load exception
31136 * @param {Roo.bootstrap.DocumentManager} this
31137 * @param {XMLHttpRequest} xhr
31139 "afterupload" : true,
31142 * prepare the form data
31143 * @param {Roo.bootstrap.DocumentManager} this
31144 * @param {Object} formData
31149 * Fire when remove the file
31150 * @param {Roo.bootstrap.DocumentManager} this
31151 * @param {Object} file
31156 * Fire after refresh the file
31157 * @param {Roo.bootstrap.DocumentManager} this
31162 * Fire after click the image
31163 * @param {Roo.bootstrap.DocumentManager} this
31164 * @param {Object} file
31169 * Fire when upload a image and editable set to true
31170 * @param {Roo.bootstrap.DocumentManager} this
31171 * @param {Object} file
31175 * @event beforeselectfile
31176 * Fire before select file
31177 * @param {Roo.bootstrap.DocumentManager} this
31179 "beforeselectfile" : true,
31182 * Fire before process file
31183 * @param {Roo.bootstrap.DocumentManager} this
31184 * @param {Object} file
31188 * @event previewrendered
31189 * Fire when preview rendered
31190 * @param {Roo.bootstrap.DocumentManager} this
31191 * @param {Object} file
31193 "previewrendered" : true,
31196 "previewResize" : true
31201 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31210 paramName : 'imageUpload',
31211 toolTipName : 'filename',
31214 labelAlign : 'left',
31224 getAutoCreate : function()
31226 var managerWidget = {
31228 cls : 'roo-document-manager',
31232 cls : 'roo-document-manager-selector',
31237 cls : 'roo-document-manager-uploader',
31241 cls : 'roo-document-manager-upload-btn',
31242 html : '<i class="fa fa-plus"></i>'
31253 cls : 'column col-md-12',
31258 if(this.fieldLabel.length){
31263 cls : 'column col-md-12',
31264 html : this.fieldLabel
31268 cls : 'column col-md-12',
31273 if(this.labelAlign == 'left'){
31278 html : this.fieldLabel
31287 if(this.labelWidth > 12){
31288 content[0].style = "width: " + this.labelWidth + 'px';
31291 if(this.labelWidth < 13 && this.labelmd == 0){
31292 this.labelmd = this.labelWidth;
31295 if(this.labellg > 0){
31296 content[0].cls += ' col-lg-' + this.labellg;
31297 content[1].cls += ' col-lg-' + (12 - this.labellg);
31300 if(this.labelmd > 0){
31301 content[0].cls += ' col-md-' + this.labelmd;
31302 content[1].cls += ' col-md-' + (12 - this.labelmd);
31305 if(this.labelsm > 0){
31306 content[0].cls += ' col-sm-' + this.labelsm;
31307 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31310 if(this.labelxs > 0){
31311 content[0].cls += ' col-xs-' + this.labelxs;
31312 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31320 cls : 'row clearfix',
31328 initEvents : function()
31330 this.managerEl = this.el.select('.roo-document-manager', true).first();
31331 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31333 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31334 this.selectorEl.hide();
31337 this.selectorEl.attr('multiple', 'multiple');
31340 this.selectorEl.on('change', this.onFileSelected, this);
31342 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31343 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31345 this.uploader.on('click', this.onUploaderClick, this);
31347 this.renderProgressDialog();
31351 window.addEventListener("resize", function() { _this.refresh(); } );
31353 this.fireEvent('initial', this);
31356 renderProgressDialog : function()
31360 this.progressDialog = new Roo.bootstrap.Modal({
31361 cls : 'roo-document-manager-progress-dialog',
31362 allow_close : false,
31373 btnclick : function() {
31374 _this.uploadCancel();
31380 this.progressDialog.render(Roo.get(document.body));
31382 this.progress = new Roo.bootstrap.Progress({
31383 cls : 'roo-document-manager-progress',
31388 this.progress.render(this.progressDialog.getChildContainer());
31390 this.progressBar = new Roo.bootstrap.ProgressBar({
31391 cls : 'roo-document-manager-progress-bar',
31394 aria_valuemax : 12,
31398 this.progressBar.render(this.progress.getChildContainer());
31401 onUploaderClick : function(e)
31403 e.preventDefault();
31405 if(this.fireEvent('beforeselectfile', this) != false){
31406 this.selectorEl.dom.click();
31411 onFileSelected : function(e)
31413 e.preventDefault();
31415 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31419 Roo.each(this.selectorEl.dom.files, function(file){
31420 if(this.fireEvent('inspect', this, file) != false){
31421 this.files.push(file);
31431 this.selectorEl.dom.value = '';
31433 if(!this.files || !this.files.length){
31437 if(this.boxes > 0 && this.files.length > this.boxes){
31438 this.files = this.files.slice(0, this.boxes);
31441 this.uploader.show();
31443 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31444 this.uploader.hide();
31453 Roo.each(this.files, function(file){
31455 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31456 var f = this.renderPreview(file);
31461 if(file.type.indexOf('image') != -1){
31462 this.delegates.push(
31464 _this.process(file);
31465 }).createDelegate(this)
31473 _this.process(file);
31474 }).createDelegate(this)
31479 this.files = files;
31481 this.delegates = this.delegates.concat(docs);
31483 if(!this.delegates.length){
31488 this.progressBar.aria_valuemax = this.delegates.length;
31495 arrange : function()
31497 if(!this.delegates.length){
31498 this.progressDialog.hide();
31503 var delegate = this.delegates.shift();
31505 this.progressDialog.show();
31507 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31509 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31514 refresh : function()
31516 this.uploader.show();
31518 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31519 this.uploader.hide();
31522 Roo.isTouch ? this.closable(false) : this.closable(true);
31524 this.fireEvent('refresh', this);
31527 onRemove : function(e, el, o)
31529 e.preventDefault();
31531 this.fireEvent('remove', this, o);
31535 remove : function(o)
31539 Roo.each(this.files, function(file){
31540 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31549 this.files = files;
31556 Roo.each(this.files, function(file){
31561 file.target.remove();
31570 onClick : function(e, el, o)
31572 e.preventDefault();
31574 this.fireEvent('click', this, o);
31578 closable : function(closable)
31580 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31582 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31594 xhrOnLoad : function(xhr)
31596 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31600 if (xhr.readyState !== 4) {
31602 this.fireEvent('exception', this, xhr);
31606 var response = Roo.decode(xhr.responseText);
31608 if(!response.success){
31610 this.fireEvent('exception', this, xhr);
31614 var file = this.renderPreview(response.data);
31616 this.files.push(file);
31620 this.fireEvent('afterupload', this, xhr);
31624 xhrOnError : function(xhr)
31626 Roo.log('xhr on error');
31628 var response = Roo.decode(xhr.responseText);
31635 process : function(file)
31637 if(this.fireEvent('process', this, file) !== false){
31638 if(this.editable && file.type.indexOf('image') != -1){
31639 this.fireEvent('edit', this, file);
31643 this.uploadStart(file, false);
31650 uploadStart : function(file, crop)
31652 this.xhr = new XMLHttpRequest();
31654 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31659 file.xhr = this.xhr;
31661 this.managerEl.createChild({
31663 cls : 'roo-document-manager-loading',
31667 tooltip : file.name,
31668 cls : 'roo-document-manager-thumb',
31669 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31675 this.xhr.open(this.method, this.url, true);
31678 "Accept": "application/json",
31679 "Cache-Control": "no-cache",
31680 "X-Requested-With": "XMLHttpRequest"
31683 for (var headerName in headers) {
31684 var headerValue = headers[headerName];
31686 this.xhr.setRequestHeader(headerName, headerValue);
31692 this.xhr.onload = function()
31694 _this.xhrOnLoad(_this.xhr);
31697 this.xhr.onerror = function()
31699 _this.xhrOnError(_this.xhr);
31702 var formData = new FormData();
31704 formData.append('returnHTML', 'NO');
31707 formData.append('crop', crop);
31710 formData.append(this.paramName, file, file.name);
31717 if(this.fireEvent('prepare', this, formData, options) != false){
31719 if(options.manually){
31723 this.xhr.send(formData);
31727 this.uploadCancel();
31730 uploadCancel : function()
31736 this.delegates = [];
31738 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31745 renderPreview : function(file)
31747 if(typeof(file.target) != 'undefined' && file.target){
31751 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31753 var previewEl = this.managerEl.createChild({
31755 cls : 'roo-document-manager-preview',
31759 tooltip : file[this.toolTipName],
31760 cls : 'roo-document-manager-thumb',
31761 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31766 html : '<i class="fa fa-times-circle"></i>'
31771 var close = previewEl.select('button.close', true).first();
31773 close.on('click', this.onRemove, this, file);
31775 file.target = previewEl;
31777 var image = previewEl.select('img', true).first();
31781 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31783 image.on('click', this.onClick, this, file);
31785 this.fireEvent('previewrendered', this, file);
31791 onPreviewLoad : function(file, image)
31793 if(typeof(file.target) == 'undefined' || !file.target){
31797 var width = image.dom.naturalWidth || image.dom.width;
31798 var height = image.dom.naturalHeight || image.dom.height;
31800 if(!this.previewResize) {
31804 if(width > height){
31805 file.target.addClass('wide');
31809 file.target.addClass('tall');
31814 uploadFromSource : function(file, crop)
31816 this.xhr = new XMLHttpRequest();
31818 this.managerEl.createChild({
31820 cls : 'roo-document-manager-loading',
31824 tooltip : file.name,
31825 cls : 'roo-document-manager-thumb',
31826 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31832 this.xhr.open(this.method, this.url, true);
31835 "Accept": "application/json",
31836 "Cache-Control": "no-cache",
31837 "X-Requested-With": "XMLHttpRequest"
31840 for (var headerName in headers) {
31841 var headerValue = headers[headerName];
31843 this.xhr.setRequestHeader(headerName, headerValue);
31849 this.xhr.onload = function()
31851 _this.xhrOnLoad(_this.xhr);
31854 this.xhr.onerror = function()
31856 _this.xhrOnError(_this.xhr);
31859 var formData = new FormData();
31861 formData.append('returnHTML', 'NO');
31863 formData.append('crop', crop);
31865 if(typeof(file.filename) != 'undefined'){
31866 formData.append('filename', file.filename);
31869 if(typeof(file.mimetype) != 'undefined'){
31870 formData.append('mimetype', file.mimetype);
31875 if(this.fireEvent('prepare', this, formData) != false){
31876 this.xhr.send(formData);
31886 * @class Roo.bootstrap.DocumentViewer
31887 * @extends Roo.bootstrap.Component
31888 * Bootstrap DocumentViewer class
31889 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31890 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31893 * Create a new DocumentViewer
31894 * @param {Object} config The config object
31897 Roo.bootstrap.DocumentViewer = function(config){
31898 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31903 * Fire after initEvent
31904 * @param {Roo.bootstrap.DocumentViewer} this
31910 * @param {Roo.bootstrap.DocumentViewer} this
31915 * Fire after download button
31916 * @param {Roo.bootstrap.DocumentViewer} this
31921 * Fire after trash button
31922 * @param {Roo.bootstrap.DocumentViewer} this
31929 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31931 showDownload : true,
31935 getAutoCreate : function()
31939 cls : 'roo-document-viewer',
31943 cls : 'roo-document-viewer-body',
31947 cls : 'roo-document-viewer-thumb',
31951 cls : 'roo-document-viewer-image'
31959 cls : 'roo-document-viewer-footer',
31962 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31966 cls : 'btn-group roo-document-viewer-download',
31970 cls : 'btn btn-default',
31971 html : '<i class="fa fa-download"></i>'
31977 cls : 'btn-group roo-document-viewer-trash',
31981 cls : 'btn btn-default',
31982 html : '<i class="fa fa-trash"></i>'
31995 initEvents : function()
31997 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31998 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32000 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32001 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32003 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32004 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32006 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32007 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32009 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32010 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32012 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32013 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32015 this.bodyEl.on('click', this.onClick, this);
32016 this.downloadBtn.on('click', this.onDownload, this);
32017 this.trashBtn.on('click', this.onTrash, this);
32019 this.downloadBtn.hide();
32020 this.trashBtn.hide();
32022 if(this.showDownload){
32023 this.downloadBtn.show();
32026 if(this.showTrash){
32027 this.trashBtn.show();
32030 if(!this.showDownload && !this.showTrash) {
32031 this.footerEl.hide();
32036 initial : function()
32038 this.fireEvent('initial', this);
32042 onClick : function(e)
32044 e.preventDefault();
32046 this.fireEvent('click', this);
32049 onDownload : function(e)
32051 e.preventDefault();
32053 this.fireEvent('download', this);
32056 onTrash : function(e)
32058 e.preventDefault();
32060 this.fireEvent('trash', this);
32072 * @class Roo.bootstrap.NavProgressBar
32073 * @extends Roo.bootstrap.Component
32074 * Bootstrap NavProgressBar class
32077 * Create a new nav progress bar
32078 * @param {Object} config The config object
32081 Roo.bootstrap.NavProgressBar = function(config){
32082 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32084 this.bullets = this.bullets || [];
32086 // Roo.bootstrap.NavProgressBar.register(this);
32090 * Fires when the active item changes
32091 * @param {Roo.bootstrap.NavProgressBar} this
32092 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32093 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32100 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32105 getAutoCreate : function()
32107 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32111 cls : 'roo-navigation-bar-group',
32115 cls : 'roo-navigation-top-bar'
32119 cls : 'roo-navigation-bullets-bar',
32123 cls : 'roo-navigation-bar'
32130 cls : 'roo-navigation-bottom-bar'
32140 initEvents: function()
32145 onRender : function(ct, position)
32147 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32149 if(this.bullets.length){
32150 Roo.each(this.bullets, function(b){
32159 addItem : function(cfg)
32161 var item = new Roo.bootstrap.NavProgressItem(cfg);
32163 item.parentId = this.id;
32164 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32167 var top = new Roo.bootstrap.Element({
32169 cls : 'roo-navigation-bar-text'
32172 var bottom = new Roo.bootstrap.Element({
32174 cls : 'roo-navigation-bar-text'
32177 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32178 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32180 var topText = new Roo.bootstrap.Element({
32182 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32185 var bottomText = new Roo.bootstrap.Element({
32187 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32190 topText.onRender(top.el, null);
32191 bottomText.onRender(bottom.el, null);
32194 item.bottomEl = bottom;
32197 this.barItems.push(item);
32202 getActive : function()
32204 var active = false;
32206 Roo.each(this.barItems, function(v){
32208 if (!v.isActive()) {
32220 setActiveItem : function(item)
32224 Roo.each(this.barItems, function(v){
32225 if (v.rid == item.rid) {
32229 if (v.isActive()) {
32230 v.setActive(false);
32235 item.setActive(true);
32237 this.fireEvent('changed', this, item, prev);
32240 getBarItem: function(rid)
32244 Roo.each(this.barItems, function(e) {
32245 if (e.rid != rid) {
32256 indexOfItem : function(item)
32260 Roo.each(this.barItems, function(v, i){
32262 if (v.rid != item.rid) {
32273 setActiveNext : function()
32275 var i = this.indexOfItem(this.getActive());
32277 if (i > this.barItems.length) {
32281 this.setActiveItem(this.barItems[i+1]);
32284 setActivePrev : function()
32286 var i = this.indexOfItem(this.getActive());
32292 this.setActiveItem(this.barItems[i-1]);
32295 format : function()
32297 if(!this.barItems.length){
32301 var width = 100 / this.barItems.length;
32303 Roo.each(this.barItems, function(i){
32304 i.el.setStyle('width', width + '%');
32305 i.topEl.el.setStyle('width', width + '%');
32306 i.bottomEl.el.setStyle('width', width + '%');
32315 * Nav Progress Item
32320 * @class Roo.bootstrap.NavProgressItem
32321 * @extends Roo.bootstrap.Component
32322 * Bootstrap NavProgressItem class
32323 * @cfg {String} rid the reference id
32324 * @cfg {Boolean} active (true|false) Is item active default false
32325 * @cfg {Boolean} disabled (true|false) Is item active default false
32326 * @cfg {String} html
32327 * @cfg {String} position (top|bottom) text position default bottom
32328 * @cfg {String} icon show icon instead of number
32331 * Create a new NavProgressItem
32332 * @param {Object} config The config object
32334 Roo.bootstrap.NavProgressItem = function(config){
32335 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32340 * The raw click event for the entire grid.
32341 * @param {Roo.bootstrap.NavProgressItem} this
32342 * @param {Roo.EventObject} e
32349 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32355 position : 'bottom',
32358 getAutoCreate : function()
32360 var iconCls = 'roo-navigation-bar-item-icon';
32362 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32366 cls: 'roo-navigation-bar-item',
32376 cfg.cls += ' active';
32379 cfg.cls += ' disabled';
32385 disable : function()
32387 this.setDisabled(true);
32390 enable : function()
32392 this.setDisabled(false);
32395 initEvents: function()
32397 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32399 this.iconEl.on('click', this.onClick, this);
32402 onClick : function(e)
32404 e.preventDefault();
32410 if(this.fireEvent('click', this, e) === false){
32414 this.parent().setActiveItem(this);
32417 isActive: function ()
32419 return this.active;
32422 setActive : function(state)
32424 if(this.active == state){
32428 this.active = state;
32431 this.el.addClass('active');
32435 this.el.removeClass('active');
32440 setDisabled : function(state)
32442 if(this.disabled == state){
32446 this.disabled = state;
32449 this.el.addClass('disabled');
32453 this.el.removeClass('disabled');
32456 tooltipEl : function()
32458 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32471 * @class Roo.bootstrap.FieldLabel
32472 * @extends Roo.bootstrap.Component
32473 * Bootstrap FieldLabel class
32474 * @cfg {String} html contents of the element
32475 * @cfg {String} tag tag of the element default label
32476 * @cfg {String} cls class of the element
32477 * @cfg {String} target label target
32478 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32479 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32480 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32481 * @cfg {String} iconTooltip default "This field is required"
32482 * @cfg {String} indicatorpos (left|right) default left
32485 * Create a new FieldLabel
32486 * @param {Object} config The config object
32489 Roo.bootstrap.FieldLabel = function(config){
32490 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32495 * Fires after the field has been marked as invalid.
32496 * @param {Roo.form.FieldLabel} this
32497 * @param {String} msg The validation message
32502 * Fires after the field has been validated with no errors.
32503 * @param {Roo.form.FieldLabel} this
32509 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32516 invalidClass : 'has-warning',
32517 validClass : 'has-success',
32518 iconTooltip : 'This field is required',
32519 indicatorpos : 'left',
32521 getAutoCreate : function(){
32524 if (!this.allowBlank) {
32530 cls : 'roo-bootstrap-field-label ' + this.cls,
32535 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32536 tooltip : this.iconTooltip
32545 if(this.indicatorpos == 'right'){
32548 cls : 'roo-bootstrap-field-label ' + this.cls,
32557 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32558 tooltip : this.iconTooltip
32567 initEvents: function()
32569 Roo.bootstrap.Element.superclass.initEvents.call(this);
32571 this.indicator = this.indicatorEl();
32573 if(this.indicator){
32574 this.indicator.removeClass('visible');
32575 this.indicator.addClass('invisible');
32578 Roo.bootstrap.FieldLabel.register(this);
32581 indicatorEl : function()
32583 var indicator = this.el.select('i.roo-required-indicator',true).first();
32594 * Mark this field as valid
32596 markValid : function()
32598 if(this.indicator){
32599 this.indicator.removeClass('visible');
32600 this.indicator.addClass('invisible');
32602 if (Roo.bootstrap.version == 3) {
32603 this.el.removeClass(this.invalidClass);
32604 this.el.addClass(this.validClass);
32606 this.el.removeClass('is-invalid');
32607 this.el.addClass('is-valid');
32611 this.fireEvent('valid', this);
32615 * Mark this field as invalid
32616 * @param {String} msg The validation message
32618 markInvalid : function(msg)
32620 if(this.indicator){
32621 this.indicator.removeClass('invisible');
32622 this.indicator.addClass('visible');
32624 if (Roo.bootstrap.version == 3) {
32625 this.el.removeClass(this.validClass);
32626 this.el.addClass(this.invalidClass);
32628 this.el.removeClass('is-valid');
32629 this.el.addClass('is-invalid');
32633 this.fireEvent('invalid', this, msg);
32639 Roo.apply(Roo.bootstrap.FieldLabel, {
32644 * register a FieldLabel Group
32645 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32647 register : function(label)
32649 if(this.groups.hasOwnProperty(label.target)){
32653 this.groups[label.target] = label;
32657 * fetch a FieldLabel Group based on the target
32658 * @param {string} target
32659 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32661 get: function(target) {
32662 if (typeof(this.groups[target]) == 'undefined') {
32666 return this.groups[target] ;
32675 * page DateSplitField.
32681 * @class Roo.bootstrap.DateSplitField
32682 * @extends Roo.bootstrap.Component
32683 * Bootstrap DateSplitField class
32684 * @cfg {string} fieldLabel - the label associated
32685 * @cfg {Number} labelWidth set the width of label (0-12)
32686 * @cfg {String} labelAlign (top|left)
32687 * @cfg {Boolean} dayAllowBlank (true|false) default false
32688 * @cfg {Boolean} monthAllowBlank (true|false) default false
32689 * @cfg {Boolean} yearAllowBlank (true|false) default false
32690 * @cfg {string} dayPlaceholder
32691 * @cfg {string} monthPlaceholder
32692 * @cfg {string} yearPlaceholder
32693 * @cfg {string} dayFormat default 'd'
32694 * @cfg {string} monthFormat default 'm'
32695 * @cfg {string} yearFormat default 'Y'
32696 * @cfg {Number} labellg set the width of label (1-12)
32697 * @cfg {Number} labelmd set the width of label (1-12)
32698 * @cfg {Number} labelsm set the width of label (1-12)
32699 * @cfg {Number} labelxs set the width of label (1-12)
32703 * Create a new DateSplitField
32704 * @param {Object} config The config object
32707 Roo.bootstrap.DateSplitField = function(config){
32708 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32714 * getting the data of years
32715 * @param {Roo.bootstrap.DateSplitField} this
32716 * @param {Object} years
32721 * getting the data of days
32722 * @param {Roo.bootstrap.DateSplitField} this
32723 * @param {Object} days
32728 * Fires after the field has been marked as invalid.
32729 * @param {Roo.form.Field} this
32730 * @param {String} msg The validation message
32735 * Fires after the field has been validated with no errors.
32736 * @param {Roo.form.Field} this
32742 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32745 labelAlign : 'top',
32747 dayAllowBlank : false,
32748 monthAllowBlank : false,
32749 yearAllowBlank : false,
32750 dayPlaceholder : '',
32751 monthPlaceholder : '',
32752 yearPlaceholder : '',
32756 isFormField : true,
32762 getAutoCreate : function()
32766 cls : 'row roo-date-split-field-group',
32771 cls : 'form-hidden-field roo-date-split-field-group-value',
32777 var labelCls = 'col-md-12';
32778 var contentCls = 'col-md-4';
32780 if(this.fieldLabel){
32784 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32788 html : this.fieldLabel
32793 if(this.labelAlign == 'left'){
32795 if(this.labelWidth > 12){
32796 label.style = "width: " + this.labelWidth + 'px';
32799 if(this.labelWidth < 13 && this.labelmd == 0){
32800 this.labelmd = this.labelWidth;
32803 if(this.labellg > 0){
32804 labelCls = ' col-lg-' + this.labellg;
32805 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32808 if(this.labelmd > 0){
32809 labelCls = ' col-md-' + this.labelmd;
32810 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32813 if(this.labelsm > 0){
32814 labelCls = ' col-sm-' + this.labelsm;
32815 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32818 if(this.labelxs > 0){
32819 labelCls = ' col-xs-' + this.labelxs;
32820 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32824 label.cls += ' ' + labelCls;
32826 cfg.cn.push(label);
32829 Roo.each(['day', 'month', 'year'], function(t){
32832 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32839 inputEl: function ()
32841 return this.el.select('.roo-date-split-field-group-value', true).first();
32844 onRender : function(ct, position)
32848 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32850 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32852 this.dayField = new Roo.bootstrap.ComboBox({
32853 allowBlank : this.dayAllowBlank,
32854 alwaysQuery : true,
32855 displayField : 'value',
32858 forceSelection : true,
32860 placeholder : this.dayPlaceholder,
32861 selectOnFocus : true,
32862 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32863 triggerAction : 'all',
32865 valueField : 'value',
32866 store : new Roo.data.SimpleStore({
32867 data : (function() {
32869 _this.fireEvent('days', _this, days);
32872 fields : [ 'value' ]
32875 select : function (_self, record, index)
32877 _this.setValue(_this.getValue());
32882 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32884 this.monthField = new Roo.bootstrap.MonthField({
32885 after : '<i class=\"fa fa-calendar\"></i>',
32886 allowBlank : this.monthAllowBlank,
32887 placeholder : this.monthPlaceholder,
32890 render : function (_self)
32892 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32893 e.preventDefault();
32897 select : function (_self, oldvalue, newvalue)
32899 _this.setValue(_this.getValue());
32904 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32906 this.yearField = new Roo.bootstrap.ComboBox({
32907 allowBlank : this.yearAllowBlank,
32908 alwaysQuery : true,
32909 displayField : 'value',
32912 forceSelection : true,
32914 placeholder : this.yearPlaceholder,
32915 selectOnFocus : true,
32916 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32917 triggerAction : 'all',
32919 valueField : 'value',
32920 store : new Roo.data.SimpleStore({
32921 data : (function() {
32923 _this.fireEvent('years', _this, years);
32926 fields : [ 'value' ]
32929 select : function (_self, record, index)
32931 _this.setValue(_this.getValue());
32936 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32939 setValue : function(v, format)
32941 this.inputEl.dom.value = v;
32943 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32945 var d = Date.parseDate(v, f);
32952 this.setDay(d.format(this.dayFormat));
32953 this.setMonth(d.format(this.monthFormat));
32954 this.setYear(d.format(this.yearFormat));
32961 setDay : function(v)
32963 this.dayField.setValue(v);
32964 this.inputEl.dom.value = this.getValue();
32969 setMonth : function(v)
32971 this.monthField.setValue(v, true);
32972 this.inputEl.dom.value = this.getValue();
32977 setYear : function(v)
32979 this.yearField.setValue(v);
32980 this.inputEl.dom.value = this.getValue();
32985 getDay : function()
32987 return this.dayField.getValue();
32990 getMonth : function()
32992 return this.monthField.getValue();
32995 getYear : function()
32997 return this.yearField.getValue();
33000 getValue : function()
33002 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33004 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33014 this.inputEl.dom.value = '';
33019 validate : function()
33021 var d = this.dayField.validate();
33022 var m = this.monthField.validate();
33023 var y = this.yearField.validate();
33028 (!this.dayAllowBlank && !d) ||
33029 (!this.monthAllowBlank && !m) ||
33030 (!this.yearAllowBlank && !y)
33035 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33044 this.markInvalid();
33049 markValid : function()
33052 var label = this.el.select('label', true).first();
33053 var icon = this.el.select('i.fa-star', true).first();
33059 this.fireEvent('valid', this);
33063 * Mark this field as invalid
33064 * @param {String} msg The validation message
33066 markInvalid : function(msg)
33069 var label = this.el.select('label', true).first();
33070 var icon = this.el.select('i.fa-star', true).first();
33072 if(label && !icon){
33073 this.el.select('.roo-date-split-field-label', true).createChild({
33075 cls : 'text-danger fa fa-lg fa-star',
33076 tooltip : 'This field is required',
33077 style : 'margin-right:5px;'
33081 this.fireEvent('invalid', this, msg);
33084 clearInvalid : function()
33086 var label = this.el.select('label', true).first();
33087 var icon = this.el.select('i.fa-star', true).first();
33093 this.fireEvent('valid', this);
33096 getName: function()
33106 * http://masonry.desandro.com
33108 * The idea is to render all the bricks based on vertical width...
33110 * The original code extends 'outlayer' - we might need to use that....
33116 * @class Roo.bootstrap.LayoutMasonry
33117 * @extends Roo.bootstrap.Component
33118 * Bootstrap Layout Masonry class
33121 * Create a new Element
33122 * @param {Object} config The config object
33125 Roo.bootstrap.LayoutMasonry = function(config){
33127 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33131 Roo.bootstrap.LayoutMasonry.register(this);
33137 * Fire after layout the items
33138 * @param {Roo.bootstrap.LayoutMasonry} this
33139 * @param {Roo.EventObject} e
33146 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33149 * @cfg {Boolean} isLayoutInstant = no animation?
33151 isLayoutInstant : false, // needed?
33154 * @cfg {Number} boxWidth width of the columns
33159 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33164 * @cfg {Number} padWidth padding below box..
33169 * @cfg {Number} gutter gutter width..
33174 * @cfg {Number} maxCols maximum number of columns
33180 * @cfg {Boolean} isAutoInitial defalut true
33182 isAutoInitial : true,
33187 * @cfg {Boolean} isHorizontal defalut false
33189 isHorizontal : false,
33191 currentSize : null,
33197 bricks: null, //CompositeElement
33201 _isLayoutInited : false,
33203 // isAlternative : false, // only use for vertical layout...
33206 * @cfg {Number} alternativePadWidth padding below box..
33208 alternativePadWidth : 50,
33210 selectedBrick : [],
33212 getAutoCreate : function(){
33214 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33218 cls: 'blog-masonary-wrapper ' + this.cls,
33220 cls : 'mas-boxes masonary'
33227 getChildContainer: function( )
33229 if (this.boxesEl) {
33230 return this.boxesEl;
33233 this.boxesEl = this.el.select('.mas-boxes').first();
33235 return this.boxesEl;
33239 initEvents : function()
33243 if(this.isAutoInitial){
33244 Roo.log('hook children rendered');
33245 this.on('childrenrendered', function() {
33246 Roo.log('children rendered');
33252 initial : function()
33254 this.selectedBrick = [];
33256 this.currentSize = this.el.getBox(true);
33258 Roo.EventManager.onWindowResize(this.resize, this);
33260 if(!this.isAutoInitial){
33268 //this.layout.defer(500,this);
33272 resize : function()
33274 var cs = this.el.getBox(true);
33277 this.currentSize.width == cs.width &&
33278 this.currentSize.x == cs.x &&
33279 this.currentSize.height == cs.height &&
33280 this.currentSize.y == cs.y
33282 Roo.log("no change in with or X or Y");
33286 this.currentSize = cs;
33292 layout : function()
33294 this._resetLayout();
33296 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33298 this.layoutItems( isInstant );
33300 this._isLayoutInited = true;
33302 this.fireEvent('layout', this);
33306 _resetLayout : function()
33308 if(this.isHorizontal){
33309 this.horizontalMeasureColumns();
33313 this.verticalMeasureColumns();
33317 verticalMeasureColumns : function()
33319 this.getContainerWidth();
33321 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33322 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33326 var boxWidth = this.boxWidth + this.padWidth;
33328 if(this.containerWidth < this.boxWidth){
33329 boxWidth = this.containerWidth
33332 var containerWidth = this.containerWidth;
33334 var cols = Math.floor(containerWidth / boxWidth);
33336 this.cols = Math.max( cols, 1 );
33338 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33340 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33342 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33344 this.colWidth = boxWidth + avail - this.padWidth;
33346 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33347 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33350 horizontalMeasureColumns : function()
33352 this.getContainerWidth();
33354 var boxWidth = this.boxWidth;
33356 if(this.containerWidth < boxWidth){
33357 boxWidth = this.containerWidth;
33360 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33362 this.el.setHeight(boxWidth);
33366 getContainerWidth : function()
33368 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33371 layoutItems : function( isInstant )
33373 Roo.log(this.bricks);
33375 var items = Roo.apply([], this.bricks);
33377 if(this.isHorizontal){
33378 this._horizontalLayoutItems( items , isInstant );
33382 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33383 // this._verticalAlternativeLayoutItems( items , isInstant );
33387 this._verticalLayoutItems( items , isInstant );
33391 _verticalLayoutItems : function ( items , isInstant)
33393 if ( !items || !items.length ) {
33398 ['xs', 'xs', 'xs', 'tall'],
33399 ['xs', 'xs', 'tall'],
33400 ['xs', 'xs', 'sm'],
33401 ['xs', 'xs', 'xs'],
33407 ['sm', 'xs', 'xs'],
33411 ['tall', 'xs', 'xs', 'xs'],
33412 ['tall', 'xs', 'xs'],
33424 Roo.each(items, function(item, k){
33426 switch (item.size) {
33427 // these layouts take up a full box,
33438 boxes.push([item]);
33461 var filterPattern = function(box, length)
33469 var pattern = box.slice(0, length);
33473 Roo.each(pattern, function(i){
33474 format.push(i.size);
33477 Roo.each(standard, function(s){
33479 if(String(s) != String(format)){
33488 if(!match && length == 1){
33493 filterPattern(box, length - 1);
33497 queue.push(pattern);
33499 box = box.slice(length, box.length);
33501 filterPattern(box, 4);
33507 Roo.each(boxes, function(box, k){
33513 if(box.length == 1){
33518 filterPattern(box, 4);
33522 this._processVerticalLayoutQueue( queue, isInstant );
33526 // _verticalAlternativeLayoutItems : function( items , isInstant )
33528 // if ( !items || !items.length ) {
33532 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33536 _horizontalLayoutItems : function ( items , isInstant)
33538 if ( !items || !items.length || items.length < 3) {
33544 var eItems = items.slice(0, 3);
33546 items = items.slice(3, items.length);
33549 ['xs', 'xs', 'xs', 'wide'],
33550 ['xs', 'xs', 'wide'],
33551 ['xs', 'xs', 'sm'],
33552 ['xs', 'xs', 'xs'],
33558 ['sm', 'xs', 'xs'],
33562 ['wide', 'xs', 'xs', 'xs'],
33563 ['wide', 'xs', 'xs'],
33576 Roo.each(items, function(item, k){
33578 switch (item.size) {
33589 boxes.push([item]);
33613 var filterPattern = function(box, length)
33621 var pattern = box.slice(0, length);
33625 Roo.each(pattern, function(i){
33626 format.push(i.size);
33629 Roo.each(standard, function(s){
33631 if(String(s) != String(format)){
33640 if(!match && length == 1){
33645 filterPattern(box, length - 1);
33649 queue.push(pattern);
33651 box = box.slice(length, box.length);
33653 filterPattern(box, 4);
33659 Roo.each(boxes, function(box, k){
33665 if(box.length == 1){
33670 filterPattern(box, 4);
33677 var pos = this.el.getBox(true);
33681 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33683 var hit_end = false;
33685 Roo.each(queue, function(box){
33689 Roo.each(box, function(b){
33691 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33701 Roo.each(box, function(b){
33703 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33706 mx = Math.max(mx, b.x);
33710 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33714 Roo.each(box, function(b){
33716 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33730 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33733 /** Sets position of item in DOM
33734 * @param {Element} item
33735 * @param {Number} x - horizontal position
33736 * @param {Number} y - vertical position
33737 * @param {Boolean} isInstant - disables transitions
33739 _processVerticalLayoutQueue : function( queue, isInstant )
33741 var pos = this.el.getBox(true);
33746 for (var i = 0; i < this.cols; i++){
33750 Roo.each(queue, function(box, k){
33752 var col = k % this.cols;
33754 Roo.each(box, function(b,kk){
33756 b.el.position('absolute');
33758 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33759 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33761 if(b.size == 'md-left' || b.size == 'md-right'){
33762 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33763 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33766 b.el.setWidth(width);
33767 b.el.setHeight(height);
33769 b.el.select('iframe',true).setSize(width,height);
33773 for (var i = 0; i < this.cols; i++){
33775 if(maxY[i] < maxY[col]){
33780 col = Math.min(col, i);
33784 x = pos.x + col * (this.colWidth + this.padWidth);
33788 var positions = [];
33790 switch (box.length){
33792 positions = this.getVerticalOneBoxColPositions(x, y, box);
33795 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33798 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33801 positions = this.getVerticalFourBoxColPositions(x, y, box);
33807 Roo.each(box, function(b,kk){
33809 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33811 var sz = b.el.getSize();
33813 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33821 for (var i = 0; i < this.cols; i++){
33822 mY = Math.max(mY, maxY[i]);
33825 this.el.setHeight(mY - pos.y);
33829 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33831 // var pos = this.el.getBox(true);
33834 // var maxX = pos.right;
33836 // var maxHeight = 0;
33838 // Roo.each(items, function(item, k){
33842 // item.el.position('absolute');
33844 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33846 // item.el.setWidth(width);
33848 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33850 // item.el.setHeight(height);
33853 // item.el.setXY([x, y], isInstant ? false : true);
33855 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33858 // y = y + height + this.alternativePadWidth;
33860 // maxHeight = maxHeight + height + this.alternativePadWidth;
33864 // this.el.setHeight(maxHeight);
33868 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33870 var pos = this.el.getBox(true);
33875 var maxX = pos.right;
33877 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33879 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33881 Roo.each(queue, function(box, k){
33883 Roo.each(box, function(b, kk){
33885 b.el.position('absolute');
33887 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33888 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33890 if(b.size == 'md-left' || b.size == 'md-right'){
33891 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33892 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33895 b.el.setWidth(width);
33896 b.el.setHeight(height);
33904 var positions = [];
33906 switch (box.length){
33908 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33911 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33914 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33917 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33923 Roo.each(box, function(b,kk){
33925 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33927 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33935 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33937 Roo.each(eItems, function(b,k){
33939 b.size = (k == 0) ? 'sm' : 'xs';
33940 b.x = (k == 0) ? 2 : 1;
33941 b.y = (k == 0) ? 2 : 1;
33943 b.el.position('absolute');
33945 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33947 b.el.setWidth(width);
33949 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33951 b.el.setHeight(height);
33955 var positions = [];
33958 x : maxX - this.unitWidth * 2 - this.gutter,
33963 x : maxX - this.unitWidth,
33964 y : minY + (this.unitWidth + this.gutter) * 2
33968 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33972 Roo.each(eItems, function(b,k){
33974 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33980 getVerticalOneBoxColPositions : function(x, y, box)
33984 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33986 if(box[0].size == 'md-left'){
33990 if(box[0].size == 'md-right'){
33995 x : x + (this.unitWidth + this.gutter) * rand,
34002 getVerticalTwoBoxColPositions : function(x, y, box)
34006 if(box[0].size == 'xs'){
34010 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34014 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34028 x : x + (this.unitWidth + this.gutter) * 2,
34029 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34036 getVerticalThreeBoxColPositions : function(x, y, box)
34040 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34048 x : x + (this.unitWidth + this.gutter) * 1,
34053 x : x + (this.unitWidth + this.gutter) * 2,
34061 if(box[0].size == 'xs' && box[1].size == 'xs'){
34070 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34074 x : x + (this.unitWidth + this.gutter) * 1,
34088 x : x + (this.unitWidth + this.gutter) * 2,
34093 x : x + (this.unitWidth + this.gutter) * 2,
34094 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34101 getVerticalFourBoxColPositions : function(x, y, box)
34105 if(box[0].size == 'xs'){
34114 y : y + (this.unitHeight + this.gutter) * 1
34119 y : y + (this.unitHeight + this.gutter) * 2
34123 x : x + (this.unitWidth + this.gutter) * 1,
34137 x : x + (this.unitWidth + this.gutter) * 2,
34142 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34143 y : y + (this.unitHeight + this.gutter) * 1
34147 x : x + (this.unitWidth + this.gutter) * 2,
34148 y : y + (this.unitWidth + this.gutter) * 2
34155 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34159 if(box[0].size == 'md-left'){
34161 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34168 if(box[0].size == 'md-right'){
34170 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34171 y : minY + (this.unitWidth + this.gutter) * 1
34177 var rand = Math.floor(Math.random() * (4 - box[0].y));
34180 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34181 y : minY + (this.unitWidth + this.gutter) * rand
34188 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34192 if(box[0].size == 'xs'){
34195 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34200 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34201 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34209 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34214 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34215 y : minY + (this.unitWidth + this.gutter) * 2
34222 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34226 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34229 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34234 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34235 y : minY + (this.unitWidth + this.gutter) * 1
34239 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34240 y : minY + (this.unitWidth + this.gutter) * 2
34247 if(box[0].size == 'xs' && box[1].size == 'xs'){
34250 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34255 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34260 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34261 y : minY + (this.unitWidth + this.gutter) * 1
34269 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34274 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34275 y : minY + (this.unitWidth + this.gutter) * 2
34279 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34280 y : minY + (this.unitWidth + this.gutter) * 2
34287 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34291 if(box[0].size == 'xs'){
34294 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34299 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34304 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),
34309 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34310 y : minY + (this.unitWidth + this.gutter) * 1
34318 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34323 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34324 y : minY + (this.unitWidth + this.gutter) * 2
34328 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34329 y : minY + (this.unitWidth + this.gutter) * 2
34333 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),
34334 y : minY + (this.unitWidth + this.gutter) * 2
34342 * remove a Masonry Brick
34343 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34345 removeBrick : function(brick_id)
34351 for (var i = 0; i<this.bricks.length; i++) {
34352 if (this.bricks[i].id == brick_id) {
34353 this.bricks.splice(i,1);
34354 this.el.dom.removeChild(Roo.get(brick_id).dom);
34361 * adds a Masonry Brick
34362 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34364 addBrick : function(cfg)
34366 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34367 //this.register(cn);
34368 cn.parentId = this.id;
34369 cn.render(this.el);
34374 * register a Masonry Brick
34375 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34378 register : function(brick)
34380 this.bricks.push(brick);
34381 brick.masonryId = this.id;
34385 * clear all the Masonry Brick
34387 clearAll : function()
34390 //this.getChildContainer().dom.innerHTML = "";
34391 this.el.dom.innerHTML = '';
34394 getSelected : function()
34396 if (!this.selectedBrick) {
34400 return this.selectedBrick;
34404 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34408 * register a Masonry Layout
34409 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34412 register : function(layout)
34414 this.groups[layout.id] = layout;
34417 * fetch a Masonry Layout based on the masonry layout ID
34418 * @param {string} the masonry layout to add
34419 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34422 get: function(layout_id) {
34423 if (typeof(this.groups[layout_id]) == 'undefined') {
34426 return this.groups[layout_id] ;
34438 * http://masonry.desandro.com
34440 * The idea is to render all the bricks based on vertical width...
34442 * The original code extends 'outlayer' - we might need to use that....
34448 * @class Roo.bootstrap.LayoutMasonryAuto
34449 * @extends Roo.bootstrap.Component
34450 * Bootstrap Layout Masonry class
34453 * Create a new Element
34454 * @param {Object} config The config object
34457 Roo.bootstrap.LayoutMasonryAuto = function(config){
34458 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34461 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34464 * @cfg {Boolean} isFitWidth - resize the width..
34466 isFitWidth : false, // options..
34468 * @cfg {Boolean} isOriginLeft = left align?
34470 isOriginLeft : true,
34472 * @cfg {Boolean} isOriginTop = top align?
34474 isOriginTop : false,
34476 * @cfg {Boolean} isLayoutInstant = no animation?
34478 isLayoutInstant : false, // needed?
34480 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34482 isResizingContainer : true,
34484 * @cfg {Number} columnWidth width of the columns
34490 * @cfg {Number} maxCols maximum number of columns
34495 * @cfg {Number} padHeight padding below box..
34501 * @cfg {Boolean} isAutoInitial defalut true
34504 isAutoInitial : true,
34510 initialColumnWidth : 0,
34511 currentSize : null,
34513 colYs : null, // array.
34520 bricks: null, //CompositeElement
34521 cols : 0, // array?
34522 // element : null, // wrapped now this.el
34523 _isLayoutInited : null,
34526 getAutoCreate : function(){
34530 cls: 'blog-masonary-wrapper ' + this.cls,
34532 cls : 'mas-boxes masonary'
34539 getChildContainer: function( )
34541 if (this.boxesEl) {
34542 return this.boxesEl;
34545 this.boxesEl = this.el.select('.mas-boxes').first();
34547 return this.boxesEl;
34551 initEvents : function()
34555 if(this.isAutoInitial){
34556 Roo.log('hook children rendered');
34557 this.on('childrenrendered', function() {
34558 Roo.log('children rendered');
34565 initial : function()
34567 this.reloadItems();
34569 this.currentSize = this.el.getBox(true);
34571 /// was window resize... - let's see if this works..
34572 Roo.EventManager.onWindowResize(this.resize, this);
34574 if(!this.isAutoInitial){
34579 this.layout.defer(500,this);
34582 reloadItems: function()
34584 this.bricks = this.el.select('.masonry-brick', true);
34586 this.bricks.each(function(b) {
34587 //Roo.log(b.getSize());
34588 if (!b.attr('originalwidth')) {
34589 b.attr('originalwidth', b.getSize().width);
34594 Roo.log(this.bricks.elements.length);
34597 resize : function()
34600 var cs = this.el.getBox(true);
34602 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34603 Roo.log("no change in with or X");
34606 this.currentSize = cs;
34610 layout : function()
34613 this._resetLayout();
34614 //this._manageStamps();
34616 // don't animate first layout
34617 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34618 this.layoutItems( isInstant );
34620 // flag for initalized
34621 this._isLayoutInited = true;
34624 layoutItems : function( isInstant )
34626 //var items = this._getItemsForLayout( this.items );
34627 // original code supports filtering layout items.. we just ignore it..
34629 this._layoutItems( this.bricks , isInstant );
34631 this._postLayout();
34633 _layoutItems : function ( items , isInstant)
34635 //this.fireEvent( 'layout', this, items );
34638 if ( !items || !items.elements.length ) {
34639 // no items, emit event with empty array
34644 items.each(function(item) {
34645 Roo.log("layout item");
34647 // get x/y object from method
34648 var position = this._getItemLayoutPosition( item );
34650 position.item = item;
34651 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34652 queue.push( position );
34655 this._processLayoutQueue( queue );
34657 /** Sets position of item in DOM
34658 * @param {Element} item
34659 * @param {Number} x - horizontal position
34660 * @param {Number} y - vertical position
34661 * @param {Boolean} isInstant - disables transitions
34663 _processLayoutQueue : function( queue )
34665 for ( var i=0, len = queue.length; i < len; i++ ) {
34666 var obj = queue[i];
34667 obj.item.position('absolute');
34668 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34674 * Any logic you want to do after each layout,
34675 * i.e. size the container
34677 _postLayout : function()
34679 this.resizeContainer();
34682 resizeContainer : function()
34684 if ( !this.isResizingContainer ) {
34687 var size = this._getContainerSize();
34689 this.el.setSize(size.width,size.height);
34690 this.boxesEl.setSize(size.width,size.height);
34696 _resetLayout : function()
34698 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34699 this.colWidth = this.el.getWidth();
34700 //this.gutter = this.el.getWidth();
34702 this.measureColumns();
34708 this.colYs.push( 0 );
34714 measureColumns : function()
34716 this.getContainerWidth();
34717 // if columnWidth is 0, default to outerWidth of first item
34718 if ( !this.columnWidth ) {
34719 var firstItem = this.bricks.first();
34720 Roo.log(firstItem);
34721 this.columnWidth = this.containerWidth;
34722 if (firstItem && firstItem.attr('originalwidth') ) {
34723 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34725 // columnWidth fall back to item of first element
34726 Roo.log("set column width?");
34727 this.initialColumnWidth = this.columnWidth ;
34729 // if first elem has no width, default to size of container
34734 if (this.initialColumnWidth) {
34735 this.columnWidth = this.initialColumnWidth;
34740 // column width is fixed at the top - however if container width get's smaller we should
34743 // this bit calcs how man columns..
34745 var columnWidth = this.columnWidth += this.gutter;
34747 // calculate columns
34748 var containerWidth = this.containerWidth + this.gutter;
34750 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34751 // fix rounding errors, typically with gutters
34752 var excess = columnWidth - containerWidth % columnWidth;
34755 // if overshoot is less than a pixel, round up, otherwise floor it
34756 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34757 cols = Math[ mathMethod ]( cols );
34758 this.cols = Math.max( cols, 1 );
34759 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34761 // padding positioning..
34762 var totalColWidth = this.cols * this.columnWidth;
34763 var padavail = this.containerWidth - totalColWidth;
34764 // so for 2 columns - we need 3 'pads'
34766 var padNeeded = (1+this.cols) * this.padWidth;
34768 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34770 this.columnWidth += padExtra
34771 //this.padWidth = Math.floor(padavail / ( this.cols));
34773 // adjust colum width so that padding is fixed??
34775 // we have 3 columns ... total = width * 3
34776 // we have X left over... that should be used by
34778 //if (this.expandC) {
34786 getContainerWidth : function()
34788 /* // container is parent if fit width
34789 var container = this.isFitWidth ? this.element.parentNode : this.element;
34790 // check that this.size and size are there
34791 // IE8 triggers resize on body size change, so they might not be
34793 var size = getSize( container ); //FIXME
34794 this.containerWidth = size && size.innerWidth; //FIXME
34797 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34801 _getItemLayoutPosition : function( item ) // what is item?
34803 // we resize the item to our columnWidth..
34805 item.setWidth(this.columnWidth);
34806 item.autoBoxAdjust = false;
34808 var sz = item.getSize();
34810 // how many columns does this brick span
34811 var remainder = this.containerWidth % this.columnWidth;
34813 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34814 // round if off by 1 pixel, otherwise use ceil
34815 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34816 colSpan = Math.min( colSpan, this.cols );
34818 // normally this should be '1' as we dont' currently allow multi width columns..
34820 var colGroup = this._getColGroup( colSpan );
34821 // get the minimum Y value from the columns
34822 var minimumY = Math.min.apply( Math, colGroup );
34823 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34825 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34827 // position the brick
34829 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34830 y: this.currentSize.y + minimumY + this.padHeight
34834 // apply setHeight to necessary columns
34835 var setHeight = minimumY + sz.height + this.padHeight;
34836 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34838 var setSpan = this.cols + 1 - colGroup.length;
34839 for ( var i = 0; i < setSpan; i++ ) {
34840 this.colYs[ shortColIndex + i ] = setHeight ;
34847 * @param {Number} colSpan - number of columns the element spans
34848 * @returns {Array} colGroup
34850 _getColGroup : function( colSpan )
34852 if ( colSpan < 2 ) {
34853 // if brick spans only one column, use all the column Ys
34858 // how many different places could this brick fit horizontally
34859 var groupCount = this.cols + 1 - colSpan;
34860 // for each group potential horizontal position
34861 for ( var i = 0; i < groupCount; i++ ) {
34862 // make an array of colY values for that one group
34863 var groupColYs = this.colYs.slice( i, i + colSpan );
34864 // and get the max value of the array
34865 colGroup[i] = Math.max.apply( Math, groupColYs );
34870 _manageStamp : function( stamp )
34872 var stampSize = stamp.getSize();
34873 var offset = stamp.getBox();
34874 // get the columns that this stamp affects
34875 var firstX = this.isOriginLeft ? offset.x : offset.right;
34876 var lastX = firstX + stampSize.width;
34877 var firstCol = Math.floor( firstX / this.columnWidth );
34878 firstCol = Math.max( 0, firstCol );
34880 var lastCol = Math.floor( lastX / this.columnWidth );
34881 // lastCol should not go over if multiple of columnWidth #425
34882 lastCol -= lastX % this.columnWidth ? 0 : 1;
34883 lastCol = Math.min( this.cols - 1, lastCol );
34885 // set colYs to bottom of the stamp
34886 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34889 for ( var i = firstCol; i <= lastCol; i++ ) {
34890 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34895 _getContainerSize : function()
34897 this.maxY = Math.max.apply( Math, this.colYs );
34902 if ( this.isFitWidth ) {
34903 size.width = this._getContainerFitWidth();
34909 _getContainerFitWidth : function()
34911 var unusedCols = 0;
34912 // count unused columns
34915 if ( this.colYs[i] !== 0 ) {
34920 // fit container to columns that have been used
34921 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34924 needsResizeLayout : function()
34926 var previousWidth = this.containerWidth;
34927 this.getContainerWidth();
34928 return previousWidth !== this.containerWidth;
34943 * @class Roo.bootstrap.MasonryBrick
34944 * @extends Roo.bootstrap.Component
34945 * Bootstrap MasonryBrick class
34948 * Create a new MasonryBrick
34949 * @param {Object} config The config object
34952 Roo.bootstrap.MasonryBrick = function(config){
34954 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34956 Roo.bootstrap.MasonryBrick.register(this);
34962 * When a MasonryBrick is clcik
34963 * @param {Roo.bootstrap.MasonryBrick} this
34964 * @param {Roo.EventObject} e
34970 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34973 * @cfg {String} title
34977 * @cfg {String} html
34981 * @cfg {String} bgimage
34985 * @cfg {String} videourl
34989 * @cfg {String} cls
34993 * @cfg {String} href
34997 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35002 * @cfg {String} placetitle (center|bottom)
35007 * @cfg {Boolean} isFitContainer defalut true
35009 isFitContainer : true,
35012 * @cfg {Boolean} preventDefault defalut false
35014 preventDefault : false,
35017 * @cfg {Boolean} inverse defalut false
35019 maskInverse : false,
35021 getAutoCreate : function()
35023 if(!this.isFitContainer){
35024 return this.getSplitAutoCreate();
35027 var cls = 'masonry-brick masonry-brick-full';
35029 if(this.href.length){
35030 cls += ' masonry-brick-link';
35033 if(this.bgimage.length){
35034 cls += ' masonry-brick-image';
35037 if(this.maskInverse){
35038 cls += ' mask-inverse';
35041 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35042 cls += ' enable-mask';
35046 cls += ' masonry-' + this.size + '-brick';
35049 if(this.placetitle.length){
35051 switch (this.placetitle) {
35053 cls += ' masonry-center-title';
35056 cls += ' masonry-bottom-title';
35063 if(!this.html.length && !this.bgimage.length){
35064 cls += ' masonry-center-title';
35067 if(!this.html.length && this.bgimage.length){
35068 cls += ' masonry-bottom-title';
35073 cls += ' ' + this.cls;
35077 tag: (this.href.length) ? 'a' : 'div',
35082 cls: 'masonry-brick-mask'
35086 cls: 'masonry-brick-paragraph',
35092 if(this.href.length){
35093 cfg.href = this.href;
35096 var cn = cfg.cn[1].cn;
35098 if(this.title.length){
35101 cls: 'masonry-brick-title',
35106 if(this.html.length){
35109 cls: 'masonry-brick-text',
35114 if (!this.title.length && !this.html.length) {
35115 cfg.cn[1].cls += ' hide';
35118 if(this.bgimage.length){
35121 cls: 'masonry-brick-image-view',
35126 if(this.videourl.length){
35127 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35128 // youtube support only?
35131 cls: 'masonry-brick-image-view',
35134 allowfullscreen : true
35142 getSplitAutoCreate : function()
35144 var cls = 'masonry-brick masonry-brick-split';
35146 if(this.href.length){
35147 cls += ' masonry-brick-link';
35150 if(this.bgimage.length){
35151 cls += ' masonry-brick-image';
35155 cls += ' masonry-' + this.size + '-brick';
35158 switch (this.placetitle) {
35160 cls += ' masonry-center-title';
35163 cls += ' masonry-bottom-title';
35166 if(!this.bgimage.length){
35167 cls += ' masonry-center-title';
35170 if(this.bgimage.length){
35171 cls += ' masonry-bottom-title';
35177 cls += ' ' + this.cls;
35181 tag: (this.href.length) ? 'a' : 'div',
35186 cls: 'masonry-brick-split-head',
35190 cls: 'masonry-brick-paragraph',
35197 cls: 'masonry-brick-split-body',
35203 if(this.href.length){
35204 cfg.href = this.href;
35207 if(this.title.length){
35208 cfg.cn[0].cn[0].cn.push({
35210 cls: 'masonry-brick-title',
35215 if(this.html.length){
35216 cfg.cn[1].cn.push({
35218 cls: 'masonry-brick-text',
35223 if(this.bgimage.length){
35224 cfg.cn[0].cn.push({
35226 cls: 'masonry-brick-image-view',
35231 if(this.videourl.length){
35232 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35233 // youtube support only?
35234 cfg.cn[0].cn.cn.push({
35236 cls: 'masonry-brick-image-view',
35239 allowfullscreen : true
35246 initEvents: function()
35248 switch (this.size) {
35281 this.el.on('touchstart', this.onTouchStart, this);
35282 this.el.on('touchmove', this.onTouchMove, this);
35283 this.el.on('touchend', this.onTouchEnd, this);
35284 this.el.on('contextmenu', this.onContextMenu, this);
35286 this.el.on('mouseenter' ,this.enter, this);
35287 this.el.on('mouseleave', this.leave, this);
35288 this.el.on('click', this.onClick, this);
35291 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35292 this.parent().bricks.push(this);
35297 onClick: function(e, el)
35299 var time = this.endTimer - this.startTimer;
35300 // Roo.log(e.preventDefault());
35303 e.preventDefault();
35308 if(!this.preventDefault){
35312 e.preventDefault();
35314 if (this.activeClass != '') {
35315 this.selectBrick();
35318 this.fireEvent('click', this, e);
35321 enter: function(e, el)
35323 e.preventDefault();
35325 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35329 if(this.bgimage.length && this.html.length){
35330 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35334 leave: function(e, el)
35336 e.preventDefault();
35338 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35342 if(this.bgimage.length && this.html.length){
35343 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35347 onTouchStart: function(e, el)
35349 // e.preventDefault();
35351 this.touchmoved = false;
35353 if(!this.isFitContainer){
35357 if(!this.bgimage.length || !this.html.length){
35361 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35363 this.timer = new Date().getTime();
35367 onTouchMove: function(e, el)
35369 this.touchmoved = true;
35372 onContextMenu : function(e,el)
35374 e.preventDefault();
35375 e.stopPropagation();
35379 onTouchEnd: function(e, el)
35381 // e.preventDefault();
35383 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35390 if(!this.bgimage.length || !this.html.length){
35392 if(this.href.length){
35393 window.location.href = this.href;
35399 if(!this.isFitContainer){
35403 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35405 window.location.href = this.href;
35408 //selection on single brick only
35409 selectBrick : function() {
35411 if (!this.parentId) {
35415 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35416 var index = m.selectedBrick.indexOf(this.id);
35419 m.selectedBrick.splice(index,1);
35420 this.el.removeClass(this.activeClass);
35424 for(var i = 0; i < m.selectedBrick.length; i++) {
35425 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35426 b.el.removeClass(b.activeClass);
35429 m.selectedBrick = [];
35431 m.selectedBrick.push(this.id);
35432 this.el.addClass(this.activeClass);
35436 isSelected : function(){
35437 return this.el.hasClass(this.activeClass);
35442 Roo.apply(Roo.bootstrap.MasonryBrick, {
35445 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35447 * register a Masonry Brick
35448 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35451 register : function(brick)
35453 //this.groups[brick.id] = brick;
35454 this.groups.add(brick.id, brick);
35457 * fetch a masonry brick based on the masonry brick ID
35458 * @param {string} the masonry brick to add
35459 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35462 get: function(brick_id)
35464 // if (typeof(this.groups[brick_id]) == 'undefined') {
35467 // return this.groups[brick_id] ;
35469 if(this.groups.key(brick_id)) {
35470 return this.groups.key(brick_id);
35488 * @class Roo.bootstrap.Brick
35489 * @extends Roo.bootstrap.Component
35490 * Bootstrap Brick class
35493 * Create a new Brick
35494 * @param {Object} config The config object
35497 Roo.bootstrap.Brick = function(config){
35498 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35504 * When a Brick is click
35505 * @param {Roo.bootstrap.Brick} this
35506 * @param {Roo.EventObject} e
35512 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35515 * @cfg {String} title
35519 * @cfg {String} html
35523 * @cfg {String} bgimage
35527 * @cfg {String} cls
35531 * @cfg {String} href
35535 * @cfg {String} video
35539 * @cfg {Boolean} square
35543 getAutoCreate : function()
35545 var cls = 'roo-brick';
35547 if(this.href.length){
35548 cls += ' roo-brick-link';
35551 if(this.bgimage.length){
35552 cls += ' roo-brick-image';
35555 if(!this.html.length && !this.bgimage.length){
35556 cls += ' roo-brick-center-title';
35559 if(!this.html.length && this.bgimage.length){
35560 cls += ' roo-brick-bottom-title';
35564 cls += ' ' + this.cls;
35568 tag: (this.href.length) ? 'a' : 'div',
35573 cls: 'roo-brick-paragraph',
35579 if(this.href.length){
35580 cfg.href = this.href;
35583 var cn = cfg.cn[0].cn;
35585 if(this.title.length){
35588 cls: 'roo-brick-title',
35593 if(this.html.length){
35596 cls: 'roo-brick-text',
35603 if(this.bgimage.length){
35606 cls: 'roo-brick-image-view',
35614 initEvents: function()
35616 if(this.title.length || this.html.length){
35617 this.el.on('mouseenter' ,this.enter, this);
35618 this.el.on('mouseleave', this.leave, this);
35621 Roo.EventManager.onWindowResize(this.resize, this);
35623 if(this.bgimage.length){
35624 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35625 this.imageEl.on('load', this.onImageLoad, this);
35632 onImageLoad : function()
35637 resize : function()
35639 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35641 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35643 if(this.bgimage.length){
35644 var image = this.el.select('.roo-brick-image-view', true).first();
35646 image.setWidth(paragraph.getWidth());
35649 image.setHeight(paragraph.getWidth());
35652 this.el.setHeight(image.getHeight());
35653 paragraph.setHeight(image.getHeight());
35659 enter: function(e, el)
35661 e.preventDefault();
35663 if(this.bgimage.length){
35664 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35665 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35669 leave: function(e, el)
35671 e.preventDefault();
35673 if(this.bgimage.length){
35674 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35675 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35690 * @class Roo.bootstrap.NumberField
35691 * @extends Roo.bootstrap.Input
35692 * Bootstrap NumberField class
35698 * Create a new NumberField
35699 * @param {Object} config The config object
35702 Roo.bootstrap.NumberField = function(config){
35703 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35706 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35709 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35711 allowDecimals : true,
35713 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35715 decimalSeparator : ".",
35717 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35719 decimalPrecision : 2,
35721 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35723 allowNegative : true,
35726 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35730 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35732 minValue : Number.NEGATIVE_INFINITY,
35734 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35736 maxValue : Number.MAX_VALUE,
35738 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35740 minText : "The minimum value for this field is {0}",
35742 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35744 maxText : "The maximum value for this field is {0}",
35746 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35747 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35749 nanText : "{0} is not a valid number",
35751 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35753 thousandsDelimiter : false,
35755 * @cfg {String} valueAlign alignment of value
35757 valueAlign : "left",
35759 getAutoCreate : function()
35761 var hiddenInput = {
35765 cls: 'hidden-number-input'
35769 hiddenInput.name = this.name;
35774 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35776 this.name = hiddenInput.name;
35778 if(cfg.cn.length > 0) {
35779 cfg.cn.push(hiddenInput);
35786 initEvents : function()
35788 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35790 var allowed = "0123456789";
35792 if(this.allowDecimals){
35793 allowed += this.decimalSeparator;
35796 if(this.allowNegative){
35800 if(this.thousandsDelimiter) {
35804 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35806 var keyPress = function(e){
35808 var k = e.getKey();
35810 var c = e.getCharCode();
35813 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35814 allowed.indexOf(String.fromCharCode(c)) === -1
35820 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35824 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35829 this.el.on("keypress", keyPress, this);
35832 validateValue : function(value)
35835 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35839 var num = this.parseValue(value);
35842 this.markInvalid(String.format(this.nanText, value));
35846 if(num < this.minValue){
35847 this.markInvalid(String.format(this.minText, this.minValue));
35851 if(num > this.maxValue){
35852 this.markInvalid(String.format(this.maxText, this.maxValue));
35859 getValue : function()
35861 var v = this.hiddenEl().getValue();
35863 return this.fixPrecision(this.parseValue(v));
35866 parseValue : function(value)
35868 if(this.thousandsDelimiter) {
35870 r = new RegExp(",", "g");
35871 value = value.replace(r, "");
35874 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35875 return isNaN(value) ? '' : value;
35878 fixPrecision : function(value)
35880 if(this.thousandsDelimiter) {
35882 r = new RegExp(",", "g");
35883 value = value.replace(r, "");
35886 var nan = isNaN(value);
35888 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35889 return nan ? '' : value;
35891 return parseFloat(value).toFixed(this.decimalPrecision);
35894 setValue : function(v)
35896 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35902 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35904 this.inputEl().dom.value = (v == '') ? '' :
35905 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35907 if(!this.allowZero && v === '0') {
35908 this.hiddenEl().dom.value = '';
35909 this.inputEl().dom.value = '';
35916 decimalPrecisionFcn : function(v)
35918 return Math.floor(v);
35921 beforeBlur : function()
35923 var v = this.parseValue(this.getRawValue());
35925 if(v || v === 0 || v === ''){
35930 hiddenEl : function()
35932 return this.el.select('input.hidden-number-input',true).first();
35944 * @class Roo.bootstrap.DocumentSlider
35945 * @extends Roo.bootstrap.Component
35946 * Bootstrap DocumentSlider class
35949 * Create a new DocumentViewer
35950 * @param {Object} config The config object
35953 Roo.bootstrap.DocumentSlider = function(config){
35954 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35961 * Fire after initEvent
35962 * @param {Roo.bootstrap.DocumentSlider} this
35967 * Fire after update
35968 * @param {Roo.bootstrap.DocumentSlider} this
35974 * @param {Roo.bootstrap.DocumentSlider} this
35980 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35986 getAutoCreate : function()
35990 cls : 'roo-document-slider',
35994 cls : 'roo-document-slider-header',
35998 cls : 'roo-document-slider-header-title'
36004 cls : 'roo-document-slider-body',
36008 cls : 'roo-document-slider-prev',
36012 cls : 'fa fa-chevron-left'
36018 cls : 'roo-document-slider-thumb',
36022 cls : 'roo-document-slider-image'
36028 cls : 'roo-document-slider-next',
36032 cls : 'fa fa-chevron-right'
36044 initEvents : function()
36046 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36047 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36049 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36050 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36052 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36053 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36055 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36056 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36058 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36059 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36061 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36062 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36064 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36065 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36067 this.thumbEl.on('click', this.onClick, this);
36069 this.prevIndicator.on('click', this.prev, this);
36071 this.nextIndicator.on('click', this.next, this);
36075 initial : function()
36077 if(this.files.length){
36078 this.indicator = 1;
36082 this.fireEvent('initial', this);
36085 update : function()
36087 this.imageEl.attr('src', this.files[this.indicator - 1]);
36089 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36091 this.prevIndicator.show();
36093 if(this.indicator == 1){
36094 this.prevIndicator.hide();
36097 this.nextIndicator.show();
36099 if(this.indicator == this.files.length){
36100 this.nextIndicator.hide();
36103 this.thumbEl.scrollTo('top');
36105 this.fireEvent('update', this);
36108 onClick : function(e)
36110 e.preventDefault();
36112 this.fireEvent('click', this);
36117 e.preventDefault();
36119 this.indicator = Math.max(1, this.indicator - 1);
36126 e.preventDefault();
36128 this.indicator = Math.min(this.files.length, this.indicator + 1);
36142 * @class Roo.bootstrap.RadioSet
36143 * @extends Roo.bootstrap.Input
36144 * Bootstrap RadioSet class
36145 * @cfg {String} indicatorpos (left|right) default left
36146 * @cfg {Boolean} inline (true|false) inline the element (default true)
36147 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36149 * Create a new RadioSet
36150 * @param {Object} config The config object
36153 Roo.bootstrap.RadioSet = function(config){
36155 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36159 Roo.bootstrap.RadioSet.register(this);
36164 * Fires when the element is checked or unchecked.
36165 * @param {Roo.bootstrap.RadioSet} this This radio
36166 * @param {Roo.bootstrap.Radio} item The checked item
36171 * Fires when the element is click.
36172 * @param {Roo.bootstrap.RadioSet} this This radio set
36173 * @param {Roo.bootstrap.Radio} item The checked item
36174 * @param {Roo.EventObject} e The event object
36181 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36189 indicatorpos : 'left',
36191 getAutoCreate : function()
36195 cls : 'roo-radio-set-label',
36199 html : this.fieldLabel
36203 if (Roo.bootstrap.version == 3) {
36206 if(this.indicatorpos == 'left'){
36209 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36210 tooltip : 'This field is required'
36215 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36216 tooltip : 'This field is required'
36222 cls : 'roo-radio-set-items'
36225 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36227 if (align === 'left' && this.fieldLabel.length) {
36230 cls : "roo-radio-set-right",
36236 if(this.labelWidth > 12){
36237 label.style = "width: " + this.labelWidth + 'px';
36240 if(this.labelWidth < 13 && this.labelmd == 0){
36241 this.labelmd = this.labelWidth;
36244 if(this.labellg > 0){
36245 label.cls += ' col-lg-' + this.labellg;
36246 items.cls += ' col-lg-' + (12 - this.labellg);
36249 if(this.labelmd > 0){
36250 label.cls += ' col-md-' + this.labelmd;
36251 items.cls += ' col-md-' + (12 - this.labelmd);
36254 if(this.labelsm > 0){
36255 label.cls += ' col-sm-' + this.labelsm;
36256 items.cls += ' col-sm-' + (12 - this.labelsm);
36259 if(this.labelxs > 0){
36260 label.cls += ' col-xs-' + this.labelxs;
36261 items.cls += ' col-xs-' + (12 - this.labelxs);
36267 cls : 'roo-radio-set',
36271 cls : 'roo-radio-set-input',
36274 value : this.value ? this.value : ''
36281 if(this.weight.length){
36282 cfg.cls += ' roo-radio-' + this.weight;
36286 cfg.cls += ' roo-radio-set-inline';
36290 ['xs','sm','md','lg'].map(function(size){
36291 if (settings[size]) {
36292 cfg.cls += ' col-' + size + '-' + settings[size];
36300 initEvents : function()
36302 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36303 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36305 if(!this.fieldLabel.length){
36306 this.labelEl.hide();
36309 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36310 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36312 this.indicator = this.indicatorEl();
36314 if(this.indicator){
36315 this.indicator.addClass('invisible');
36318 this.originalValue = this.getValue();
36322 inputEl: function ()
36324 return this.el.select('.roo-radio-set-input', true).first();
36327 getChildContainer : function()
36329 return this.itemsEl;
36332 register : function(item)
36334 this.radioes.push(item);
36338 validate : function()
36340 if(this.getVisibilityEl().hasClass('hidden')){
36346 Roo.each(this.radioes, function(i){
36355 if(this.allowBlank) {
36359 if(this.disabled || valid){
36364 this.markInvalid();
36369 markValid : function()
36371 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36372 this.indicatorEl().removeClass('visible');
36373 this.indicatorEl().addClass('invisible');
36377 if (Roo.bootstrap.version == 3) {
36378 this.el.removeClass([this.invalidClass, this.validClass]);
36379 this.el.addClass(this.validClass);
36381 this.el.removeClass(['is-invalid','is-valid']);
36382 this.el.addClass(['is-valid']);
36384 this.fireEvent('valid', this);
36387 markInvalid : function(msg)
36389 if(this.allowBlank || this.disabled){
36393 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36394 this.indicatorEl().removeClass('invisible');
36395 this.indicatorEl().addClass('visible');
36397 if (Roo.bootstrap.version == 3) {
36398 this.el.removeClass([this.invalidClass, this.validClass]);
36399 this.el.addClass(this.invalidClass);
36401 this.el.removeClass(['is-invalid','is-valid']);
36402 this.el.addClass(['is-invalid']);
36405 this.fireEvent('invalid', this, msg);
36409 setValue : function(v, suppressEvent)
36411 if(this.value === v){
36418 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36421 Roo.each(this.radioes, function(i){
36423 i.el.removeClass('checked');
36426 Roo.each(this.radioes, function(i){
36428 if(i.value === v || i.value.toString() === v.toString()){
36430 i.el.addClass('checked');
36432 if(suppressEvent !== true){
36433 this.fireEvent('check', this, i);
36444 clearInvalid : function(){
36446 if(!this.el || this.preventMark){
36450 this.el.removeClass([this.invalidClass]);
36452 this.fireEvent('valid', this);
36457 Roo.apply(Roo.bootstrap.RadioSet, {
36461 register : function(set)
36463 this.groups[set.name] = set;
36466 get: function(name)
36468 if (typeof(this.groups[name]) == 'undefined') {
36472 return this.groups[name] ;
36478 * Ext JS Library 1.1.1
36479 * Copyright(c) 2006-2007, Ext JS, LLC.
36481 * Originally Released Under LGPL - original licence link has changed is not relivant.
36484 * <script type="text/javascript">
36489 * @class Roo.bootstrap.SplitBar
36490 * @extends Roo.util.Observable
36491 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36495 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36496 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36497 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36498 split.minSize = 100;
36499 split.maxSize = 600;
36500 split.animate = true;
36501 split.on('moved', splitterMoved);
36504 * Create a new SplitBar
36505 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36506 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36507 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36508 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36509 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36510 position of the SplitBar).
36512 Roo.bootstrap.SplitBar = function(cfg){
36517 // dragElement : elm
36518 // resizingElement: el,
36520 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36521 // placement : Roo.bootstrap.SplitBar.LEFT ,
36522 // existingProxy ???
36525 this.el = Roo.get(cfg.dragElement, true);
36526 this.el.dom.unselectable = "on";
36528 this.resizingEl = Roo.get(cfg.resizingElement, true);
36532 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36533 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36536 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36539 * The minimum size of the resizing element. (Defaults to 0)
36545 * The maximum size of the resizing element. (Defaults to 2000)
36548 this.maxSize = 2000;
36551 * Whether to animate the transition to the new size
36554 this.animate = false;
36557 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36560 this.useShim = false;
36565 if(!cfg.existingProxy){
36567 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36569 this.proxy = Roo.get(cfg.existingProxy).dom;
36572 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36575 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36578 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36581 this.dragSpecs = {};
36584 * @private The adapter to use to positon and resize elements
36586 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36587 this.adapter.init(this);
36589 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36591 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36592 this.el.addClass("roo-splitbar-h");
36595 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36596 this.el.addClass("roo-splitbar-v");
36602 * Fires when the splitter is moved (alias for {@link #event-moved})
36603 * @param {Roo.bootstrap.SplitBar} this
36604 * @param {Number} newSize the new width or height
36609 * Fires when the splitter is moved
36610 * @param {Roo.bootstrap.SplitBar} this
36611 * @param {Number} newSize the new width or height
36615 * @event beforeresize
36616 * Fires before the splitter is dragged
36617 * @param {Roo.bootstrap.SplitBar} this
36619 "beforeresize" : true,
36621 "beforeapply" : true
36624 Roo.util.Observable.call(this);
36627 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36628 onStartProxyDrag : function(x, y){
36629 this.fireEvent("beforeresize", this);
36631 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36633 o.enableDisplayMode("block");
36634 // all splitbars share the same overlay
36635 Roo.bootstrap.SplitBar.prototype.overlay = o;
36637 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36638 this.overlay.show();
36639 Roo.get(this.proxy).setDisplayed("block");
36640 var size = this.adapter.getElementSize(this);
36641 this.activeMinSize = this.getMinimumSize();;
36642 this.activeMaxSize = this.getMaximumSize();;
36643 var c1 = size - this.activeMinSize;
36644 var c2 = Math.max(this.activeMaxSize - size, 0);
36645 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36646 this.dd.resetConstraints();
36647 this.dd.setXConstraint(
36648 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36649 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36651 this.dd.setYConstraint(0, 0);
36653 this.dd.resetConstraints();
36654 this.dd.setXConstraint(0, 0);
36655 this.dd.setYConstraint(
36656 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36657 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36660 this.dragSpecs.startSize = size;
36661 this.dragSpecs.startPoint = [x, y];
36662 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36666 * @private Called after the drag operation by the DDProxy
36668 onEndProxyDrag : function(e){
36669 Roo.get(this.proxy).setDisplayed(false);
36670 var endPoint = Roo.lib.Event.getXY(e);
36672 this.overlay.hide();
36675 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36676 newSize = this.dragSpecs.startSize +
36677 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36678 endPoint[0] - this.dragSpecs.startPoint[0] :
36679 this.dragSpecs.startPoint[0] - endPoint[0]
36682 newSize = this.dragSpecs.startSize +
36683 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36684 endPoint[1] - this.dragSpecs.startPoint[1] :
36685 this.dragSpecs.startPoint[1] - endPoint[1]
36688 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36689 if(newSize != this.dragSpecs.startSize){
36690 if(this.fireEvent('beforeapply', this, newSize) !== false){
36691 this.adapter.setElementSize(this, newSize);
36692 this.fireEvent("moved", this, newSize);
36693 this.fireEvent("resize", this, newSize);
36699 * Get the adapter this SplitBar uses
36700 * @return The adapter object
36702 getAdapter : function(){
36703 return this.adapter;
36707 * Set the adapter this SplitBar uses
36708 * @param {Object} adapter A SplitBar adapter object
36710 setAdapter : function(adapter){
36711 this.adapter = adapter;
36712 this.adapter.init(this);
36716 * Gets the minimum size for the resizing element
36717 * @return {Number} The minimum size
36719 getMinimumSize : function(){
36720 return this.minSize;
36724 * Sets the minimum size for the resizing element
36725 * @param {Number} minSize The minimum size
36727 setMinimumSize : function(minSize){
36728 this.minSize = minSize;
36732 * Gets the maximum size for the resizing element
36733 * @return {Number} The maximum size
36735 getMaximumSize : function(){
36736 return this.maxSize;
36740 * Sets the maximum size for the resizing element
36741 * @param {Number} maxSize The maximum size
36743 setMaximumSize : function(maxSize){
36744 this.maxSize = maxSize;
36748 * Sets the initialize size for the resizing element
36749 * @param {Number} size The initial size
36751 setCurrentSize : function(size){
36752 var oldAnimate = this.animate;
36753 this.animate = false;
36754 this.adapter.setElementSize(this, size);
36755 this.animate = oldAnimate;
36759 * Destroy this splitbar.
36760 * @param {Boolean} removeEl True to remove the element
36762 destroy : function(removeEl){
36764 this.shim.remove();
36767 this.proxy.parentNode.removeChild(this.proxy);
36775 * @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.
36777 Roo.bootstrap.SplitBar.createProxy = function(dir){
36778 var proxy = new Roo.Element(document.createElement("div"));
36779 proxy.unselectable();
36780 var cls = 'roo-splitbar-proxy';
36781 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36782 document.body.appendChild(proxy.dom);
36787 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36788 * Default Adapter. It assumes the splitter and resizing element are not positioned
36789 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36791 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36794 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36795 // do nothing for now
36796 init : function(s){
36800 * Called before drag operations to get the current size of the resizing element.
36801 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36803 getElementSize : function(s){
36804 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36805 return s.resizingEl.getWidth();
36807 return s.resizingEl.getHeight();
36812 * Called after drag operations to set the size of the resizing element.
36813 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36814 * @param {Number} newSize The new size to set
36815 * @param {Function} onComplete A function to be invoked when resizing is complete
36817 setElementSize : function(s, newSize, onComplete){
36818 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36820 s.resizingEl.setWidth(newSize);
36822 onComplete(s, newSize);
36825 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36830 s.resizingEl.setHeight(newSize);
36832 onComplete(s, newSize);
36835 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36842 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36843 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36844 * Adapter that moves the splitter element to align with the resized sizing element.
36845 * Used with an absolute positioned SplitBar.
36846 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36847 * document.body, make sure you assign an id to the body element.
36849 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36850 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36851 this.container = Roo.get(container);
36854 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36855 init : function(s){
36856 this.basic.init(s);
36859 getElementSize : function(s){
36860 return this.basic.getElementSize(s);
36863 setElementSize : function(s, newSize, onComplete){
36864 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36867 moveSplitter : function(s){
36868 var yes = Roo.bootstrap.SplitBar;
36869 switch(s.placement){
36871 s.el.setX(s.resizingEl.getRight());
36874 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36877 s.el.setY(s.resizingEl.getBottom());
36880 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36887 * Orientation constant - Create a vertical SplitBar
36891 Roo.bootstrap.SplitBar.VERTICAL = 1;
36894 * Orientation constant - Create a horizontal SplitBar
36898 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36901 * Placement constant - The resizing element is to the left of the splitter element
36905 Roo.bootstrap.SplitBar.LEFT = 1;
36908 * Placement constant - The resizing element is to the right of the splitter element
36912 Roo.bootstrap.SplitBar.RIGHT = 2;
36915 * Placement constant - The resizing element is positioned above the splitter element
36919 Roo.bootstrap.SplitBar.TOP = 3;
36922 * Placement constant - The resizing element is positioned under splitter element
36926 Roo.bootstrap.SplitBar.BOTTOM = 4;
36927 Roo.namespace("Roo.bootstrap.layout");/*
36929 * Ext JS Library 1.1.1
36930 * Copyright(c) 2006-2007, Ext JS, LLC.
36932 * Originally Released Under LGPL - original licence link has changed is not relivant.
36935 * <script type="text/javascript">
36939 * @class Roo.bootstrap.layout.Manager
36940 * @extends Roo.bootstrap.Component
36941 * Base class for layout managers.
36943 Roo.bootstrap.layout.Manager = function(config)
36945 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36951 /** false to disable window resize monitoring @type Boolean */
36952 this.monitorWindowResize = true;
36957 * Fires when a layout is performed.
36958 * @param {Roo.LayoutManager} this
36962 * @event regionresized
36963 * Fires when the user resizes a region.
36964 * @param {Roo.LayoutRegion} region The resized region
36965 * @param {Number} newSize The new size (width for east/west, height for north/south)
36967 "regionresized" : true,
36969 * @event regioncollapsed
36970 * Fires when a region is collapsed.
36971 * @param {Roo.LayoutRegion} region The collapsed region
36973 "regioncollapsed" : true,
36975 * @event regionexpanded
36976 * Fires when a region is expanded.
36977 * @param {Roo.LayoutRegion} region The expanded region
36979 "regionexpanded" : true
36981 this.updating = false;
36984 this.el = Roo.get(config.el);
36990 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36995 monitorWindowResize : true,
37001 onRender : function(ct, position)
37004 this.el = Roo.get(ct);
37007 //this.fireEvent('render',this);
37011 initEvents: function()
37015 // ie scrollbar fix
37016 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37017 document.body.scroll = "no";
37018 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37019 this.el.position('relative');
37021 this.id = this.el.id;
37022 this.el.addClass("roo-layout-container");
37023 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37024 if(this.el.dom != document.body ) {
37025 this.el.on('resize', this.layout,this);
37026 this.el.on('show', this.layout,this);
37032 * Returns true if this layout is currently being updated
37033 * @return {Boolean}
37035 isUpdating : function(){
37036 return this.updating;
37040 * Suspend the LayoutManager from doing auto-layouts while
37041 * making multiple add or remove calls
37043 beginUpdate : function(){
37044 this.updating = true;
37048 * Restore auto-layouts and optionally disable the manager from performing a layout
37049 * @param {Boolean} noLayout true to disable a layout update
37051 endUpdate : function(noLayout){
37052 this.updating = false;
37058 layout: function(){
37062 onRegionResized : function(region, newSize){
37063 this.fireEvent("regionresized", region, newSize);
37067 onRegionCollapsed : function(region){
37068 this.fireEvent("regioncollapsed", region);
37071 onRegionExpanded : function(region){
37072 this.fireEvent("regionexpanded", region);
37076 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37077 * performs box-model adjustments.
37078 * @return {Object} The size as an object {width: (the width), height: (the height)}
37080 getViewSize : function()
37083 if(this.el.dom != document.body){
37084 size = this.el.getSize();
37086 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37088 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37089 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37094 * Returns the Element this layout is bound to.
37095 * @return {Roo.Element}
37097 getEl : function(){
37102 * Returns the specified region.
37103 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37104 * @return {Roo.LayoutRegion}
37106 getRegion : function(target){
37107 return this.regions[target.toLowerCase()];
37110 onWindowResize : function(){
37111 if(this.monitorWindowResize){
37118 * Ext JS Library 1.1.1
37119 * Copyright(c) 2006-2007, Ext JS, LLC.
37121 * Originally Released Under LGPL - original licence link has changed is not relivant.
37124 * <script type="text/javascript">
37127 * @class Roo.bootstrap.layout.Border
37128 * @extends Roo.bootstrap.layout.Manager
37129 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37130 * please see: examples/bootstrap/nested.html<br><br>
37132 <b>The container the layout is rendered into can be either the body element or any other element.
37133 If it is not the body element, the container needs to either be an absolute positioned element,
37134 or you will need to add "position:relative" to the css of the container. You will also need to specify
37135 the container size if it is not the body element.</b>
37138 * Create a new Border
37139 * @param {Object} config Configuration options
37141 Roo.bootstrap.layout.Border = function(config){
37142 config = config || {};
37143 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37147 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37148 if(config[region]){
37149 config[region].region = region;
37150 this.addRegion(config[region]);
37156 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37158 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37160 parent : false, // this might point to a 'nest' or a ???
37163 * Creates and adds a new region if it doesn't already exist.
37164 * @param {String} target The target region key (north, south, east, west or center).
37165 * @param {Object} config The regions config object
37166 * @return {BorderLayoutRegion} The new region
37168 addRegion : function(config)
37170 if(!this.regions[config.region]){
37171 var r = this.factory(config);
37172 this.bindRegion(r);
37174 return this.regions[config.region];
37178 bindRegion : function(r){
37179 this.regions[r.config.region] = r;
37181 r.on("visibilitychange", this.layout, this);
37182 r.on("paneladded", this.layout, this);
37183 r.on("panelremoved", this.layout, this);
37184 r.on("invalidated", this.layout, this);
37185 r.on("resized", this.onRegionResized, this);
37186 r.on("collapsed", this.onRegionCollapsed, this);
37187 r.on("expanded", this.onRegionExpanded, this);
37191 * Performs a layout update.
37193 layout : function()
37195 if(this.updating) {
37199 // render all the rebions if they have not been done alreayd?
37200 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37201 if(this.regions[region] && !this.regions[region].bodyEl){
37202 this.regions[region].onRender(this.el)
37206 var size = this.getViewSize();
37207 var w = size.width;
37208 var h = size.height;
37213 //var x = 0, y = 0;
37215 var rs = this.regions;
37216 var north = rs["north"];
37217 var south = rs["south"];
37218 var west = rs["west"];
37219 var east = rs["east"];
37220 var center = rs["center"];
37221 //if(this.hideOnLayout){ // not supported anymore
37222 //c.el.setStyle("display", "none");
37224 if(north && north.isVisible()){
37225 var b = north.getBox();
37226 var m = north.getMargins();
37227 b.width = w - (m.left+m.right);
37230 centerY = b.height + b.y + m.bottom;
37231 centerH -= centerY;
37232 north.updateBox(this.safeBox(b));
37234 if(south && south.isVisible()){
37235 var b = south.getBox();
37236 var m = south.getMargins();
37237 b.width = w - (m.left+m.right);
37239 var totalHeight = (b.height + m.top + m.bottom);
37240 b.y = h - totalHeight + m.top;
37241 centerH -= totalHeight;
37242 south.updateBox(this.safeBox(b));
37244 if(west && west.isVisible()){
37245 var b = west.getBox();
37246 var m = west.getMargins();
37247 b.height = centerH - (m.top+m.bottom);
37249 b.y = centerY + m.top;
37250 var totalWidth = (b.width + m.left + m.right);
37251 centerX += totalWidth;
37252 centerW -= totalWidth;
37253 west.updateBox(this.safeBox(b));
37255 if(east && east.isVisible()){
37256 var b = east.getBox();
37257 var m = east.getMargins();
37258 b.height = centerH - (m.top+m.bottom);
37259 var totalWidth = (b.width + m.left + m.right);
37260 b.x = w - totalWidth + m.left;
37261 b.y = centerY + m.top;
37262 centerW -= totalWidth;
37263 east.updateBox(this.safeBox(b));
37266 var m = center.getMargins();
37268 x: centerX + m.left,
37269 y: centerY + m.top,
37270 width: centerW - (m.left+m.right),
37271 height: centerH - (m.top+m.bottom)
37273 //if(this.hideOnLayout){
37274 //center.el.setStyle("display", "block");
37276 center.updateBox(this.safeBox(centerBox));
37279 this.fireEvent("layout", this);
37283 safeBox : function(box){
37284 box.width = Math.max(0, box.width);
37285 box.height = Math.max(0, box.height);
37290 * Adds a ContentPanel (or subclass) to this layout.
37291 * @param {String} target The target region key (north, south, east, west or center).
37292 * @param {Roo.ContentPanel} panel The panel to add
37293 * @return {Roo.ContentPanel} The added panel
37295 add : function(target, panel){
37297 target = target.toLowerCase();
37298 return this.regions[target].add(panel);
37302 * Remove a ContentPanel (or subclass) to this layout.
37303 * @param {String} target The target region key (north, south, east, west or center).
37304 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37305 * @return {Roo.ContentPanel} The removed panel
37307 remove : function(target, panel){
37308 target = target.toLowerCase();
37309 return this.regions[target].remove(panel);
37313 * Searches all regions for a panel with the specified id
37314 * @param {String} panelId
37315 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37317 findPanel : function(panelId){
37318 var rs = this.regions;
37319 for(var target in rs){
37320 if(typeof rs[target] != "function"){
37321 var p = rs[target].getPanel(panelId);
37331 * Searches all regions for a panel with the specified id and activates (shows) it.
37332 * @param {String/ContentPanel} panelId The panels id or the panel itself
37333 * @return {Roo.ContentPanel} The shown panel or null
37335 showPanel : function(panelId) {
37336 var rs = this.regions;
37337 for(var target in rs){
37338 var r = rs[target];
37339 if(typeof r != "function"){
37340 if(r.hasPanel(panelId)){
37341 return r.showPanel(panelId);
37349 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37350 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37353 restoreState : function(provider){
37355 provider = Roo.state.Manager;
37357 var sm = new Roo.LayoutStateManager();
37358 sm.init(this, provider);
37364 * Adds a xtype elements to the layout.
37368 xtype : 'ContentPanel',
37375 xtype : 'NestedLayoutPanel',
37381 items : [ ... list of content panels or nested layout panels.. ]
37385 * @param {Object} cfg Xtype definition of item to add.
37387 addxtype : function(cfg)
37389 // basically accepts a pannel...
37390 // can accept a layout region..!?!?
37391 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37394 // theory? children can only be panels??
37396 //if (!cfg.xtype.match(/Panel$/)) {
37401 if (typeof(cfg.region) == 'undefined') {
37402 Roo.log("Failed to add Panel, region was not set");
37406 var region = cfg.region;
37412 xitems = cfg.items;
37417 if ( region == 'center') {
37418 Roo.log("Center: " + cfg.title);
37424 case 'Content': // ContentPanel (el, cfg)
37425 case 'Scroll': // ContentPanel (el, cfg)
37427 cfg.autoCreate = cfg.autoCreate || true;
37428 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37430 // var el = this.el.createChild();
37431 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37434 this.add(region, ret);
37438 case 'TreePanel': // our new panel!
37439 cfg.el = this.el.createChild();
37440 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37441 this.add(region, ret);
37446 // create a new Layout (which is a Border Layout...
37448 var clayout = cfg.layout;
37449 clayout.el = this.el.createChild();
37450 clayout.items = clayout.items || [];
37454 // replace this exitems with the clayout ones..
37455 xitems = clayout.items;
37457 // force background off if it's in center...
37458 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37459 cfg.background = false;
37461 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37464 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37465 //console.log('adding nested layout panel ' + cfg.toSource());
37466 this.add(region, ret);
37467 nb = {}; /// find first...
37472 // needs grid and region
37474 //var el = this.getRegion(region).el.createChild();
37476 *var el = this.el.createChild();
37477 // create the grid first...
37478 cfg.grid.container = el;
37479 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37482 if (region == 'center' && this.active ) {
37483 cfg.background = false;
37486 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37488 this.add(region, ret);
37490 if (cfg.background) {
37491 // render grid on panel activation (if panel background)
37492 ret.on('activate', function(gp) {
37493 if (!gp.grid.rendered) {
37494 // gp.grid.render(el);
37498 // cfg.grid.render(el);
37504 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37505 // it was the old xcomponent building that caused this before.
37506 // espeically if border is the top element in the tree.
37516 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37518 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37519 this.add(region, ret);
37523 throw "Can not add '" + cfg.xtype + "' to Border";
37529 this.beginUpdate();
37533 Roo.each(xitems, function(i) {
37534 region = nb && i.region ? i.region : false;
37536 var add = ret.addxtype(i);
37539 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37540 if (!i.background) {
37541 abn[region] = nb[region] ;
37548 // make the last non-background panel active..
37549 //if (nb) { Roo.log(abn); }
37552 for(var r in abn) {
37553 region = this.getRegion(r);
37555 // tried using nb[r], but it does not work..
37557 region.showPanel(abn[r]);
37568 factory : function(cfg)
37571 var validRegions = Roo.bootstrap.layout.Border.regions;
37573 var target = cfg.region;
37576 var r = Roo.bootstrap.layout;
37580 return new r.North(cfg);
37582 return new r.South(cfg);
37584 return new r.East(cfg);
37586 return new r.West(cfg);
37588 return new r.Center(cfg);
37590 throw 'Layout region "'+target+'" not supported.';
37597 * Ext JS Library 1.1.1
37598 * Copyright(c) 2006-2007, Ext JS, LLC.
37600 * Originally Released Under LGPL - original licence link has changed is not relivant.
37603 * <script type="text/javascript">
37607 * @class Roo.bootstrap.layout.Basic
37608 * @extends Roo.util.Observable
37609 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37610 * and does not have a titlebar, tabs or any other features. All it does is size and position
37611 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37612 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37613 * @cfg {string} region the region that it inhabits..
37614 * @cfg {bool} skipConfig skip config?
37618 Roo.bootstrap.layout.Basic = function(config){
37620 this.mgr = config.mgr;
37622 this.position = config.region;
37624 var skipConfig = config.skipConfig;
37628 * @scope Roo.BasicLayoutRegion
37632 * @event beforeremove
37633 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37634 * @param {Roo.LayoutRegion} this
37635 * @param {Roo.ContentPanel} panel The panel
37636 * @param {Object} e The cancel event object
37638 "beforeremove" : true,
37640 * @event invalidated
37641 * Fires when the layout for this region is changed.
37642 * @param {Roo.LayoutRegion} this
37644 "invalidated" : true,
37646 * @event visibilitychange
37647 * Fires when this region is shown or hidden
37648 * @param {Roo.LayoutRegion} this
37649 * @param {Boolean} visibility true or false
37651 "visibilitychange" : true,
37653 * @event paneladded
37654 * Fires when a panel is added.
37655 * @param {Roo.LayoutRegion} this
37656 * @param {Roo.ContentPanel} panel The panel
37658 "paneladded" : true,
37660 * @event panelremoved
37661 * Fires when a panel is removed.
37662 * @param {Roo.LayoutRegion} this
37663 * @param {Roo.ContentPanel} panel The panel
37665 "panelremoved" : true,
37667 * @event beforecollapse
37668 * Fires when this region before collapse.
37669 * @param {Roo.LayoutRegion} this
37671 "beforecollapse" : true,
37674 * Fires when this region is collapsed.
37675 * @param {Roo.LayoutRegion} this
37677 "collapsed" : true,
37680 * Fires when this region is expanded.
37681 * @param {Roo.LayoutRegion} this
37686 * Fires when this region is slid into view.
37687 * @param {Roo.LayoutRegion} this
37689 "slideshow" : true,
37692 * Fires when this region slides out of view.
37693 * @param {Roo.LayoutRegion} this
37695 "slidehide" : true,
37697 * @event panelactivated
37698 * Fires when a panel is activated.
37699 * @param {Roo.LayoutRegion} this
37700 * @param {Roo.ContentPanel} panel The activated panel
37702 "panelactivated" : true,
37705 * Fires when the user resizes this region.
37706 * @param {Roo.LayoutRegion} this
37707 * @param {Number} newSize The new size (width for east/west, height for north/south)
37711 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37712 this.panels = new Roo.util.MixedCollection();
37713 this.panels.getKey = this.getPanelId.createDelegate(this);
37715 this.activePanel = null;
37716 // ensure listeners are added...
37718 if (config.listeners || config.events) {
37719 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37720 listeners : config.listeners || {},
37721 events : config.events || {}
37725 if(skipConfig !== true){
37726 this.applyConfig(config);
37730 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37732 getPanelId : function(p){
37736 applyConfig : function(config){
37737 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37738 this.config = config;
37743 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37744 * the width, for horizontal (north, south) the height.
37745 * @param {Number} newSize The new width or height
37747 resizeTo : function(newSize){
37748 var el = this.el ? this.el :
37749 (this.activePanel ? this.activePanel.getEl() : null);
37751 switch(this.position){
37754 el.setWidth(newSize);
37755 this.fireEvent("resized", this, newSize);
37759 el.setHeight(newSize);
37760 this.fireEvent("resized", this, newSize);
37766 getBox : function(){
37767 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37770 getMargins : function(){
37771 return this.margins;
37774 updateBox : function(box){
37776 var el = this.activePanel.getEl();
37777 el.dom.style.left = box.x + "px";
37778 el.dom.style.top = box.y + "px";
37779 this.activePanel.setSize(box.width, box.height);
37783 * Returns the container element for this region.
37784 * @return {Roo.Element}
37786 getEl : function(){
37787 return this.activePanel;
37791 * Returns true if this region is currently visible.
37792 * @return {Boolean}
37794 isVisible : function(){
37795 return this.activePanel ? true : false;
37798 setActivePanel : function(panel){
37799 panel = this.getPanel(panel);
37800 if(this.activePanel && this.activePanel != panel){
37801 this.activePanel.setActiveState(false);
37802 this.activePanel.getEl().setLeftTop(-10000,-10000);
37804 this.activePanel = panel;
37805 panel.setActiveState(true);
37807 panel.setSize(this.box.width, this.box.height);
37809 this.fireEvent("panelactivated", this, panel);
37810 this.fireEvent("invalidated");
37814 * Show the specified panel.
37815 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37816 * @return {Roo.ContentPanel} The shown panel or null
37818 showPanel : function(panel){
37819 panel = this.getPanel(panel);
37821 this.setActivePanel(panel);
37827 * Get the active panel for this region.
37828 * @return {Roo.ContentPanel} The active panel or null
37830 getActivePanel : function(){
37831 return this.activePanel;
37835 * Add the passed ContentPanel(s)
37836 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37837 * @return {Roo.ContentPanel} The panel added (if only one was added)
37839 add : function(panel){
37840 if(arguments.length > 1){
37841 for(var i = 0, len = arguments.length; i < len; i++) {
37842 this.add(arguments[i]);
37846 if(this.hasPanel(panel)){
37847 this.showPanel(panel);
37850 var el = panel.getEl();
37851 if(el.dom.parentNode != this.mgr.el.dom){
37852 this.mgr.el.dom.appendChild(el.dom);
37854 if(panel.setRegion){
37855 panel.setRegion(this);
37857 this.panels.add(panel);
37858 el.setStyle("position", "absolute");
37859 if(!panel.background){
37860 this.setActivePanel(panel);
37861 if(this.config.initialSize && this.panels.getCount()==1){
37862 this.resizeTo(this.config.initialSize);
37865 this.fireEvent("paneladded", this, panel);
37870 * Returns true if the panel is in this region.
37871 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37872 * @return {Boolean}
37874 hasPanel : function(panel){
37875 if(typeof panel == "object"){ // must be panel obj
37876 panel = panel.getId();
37878 return this.getPanel(panel) ? true : false;
37882 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37883 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37884 * @param {Boolean} preservePanel Overrides the config preservePanel option
37885 * @return {Roo.ContentPanel} The panel that was removed
37887 remove : function(panel, preservePanel){
37888 panel = this.getPanel(panel);
37893 this.fireEvent("beforeremove", this, panel, e);
37894 if(e.cancel === true){
37897 var panelId = panel.getId();
37898 this.panels.removeKey(panelId);
37903 * Returns the panel specified or null if it's not in this region.
37904 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37905 * @return {Roo.ContentPanel}
37907 getPanel : function(id){
37908 if(typeof id == "object"){ // must be panel obj
37911 return this.panels.get(id);
37915 * Returns this regions position (north/south/east/west/center).
37918 getPosition: function(){
37919 return this.position;
37923 * Ext JS Library 1.1.1
37924 * Copyright(c) 2006-2007, Ext JS, LLC.
37926 * Originally Released Under LGPL - original licence link has changed is not relivant.
37929 * <script type="text/javascript">
37933 * @class Roo.bootstrap.layout.Region
37934 * @extends Roo.bootstrap.layout.Basic
37935 * This class represents a region in a layout manager.
37937 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37938 * @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})
37939 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37940 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37941 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37942 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37943 * @cfg {String} title The title for the region (overrides panel titles)
37944 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37945 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37946 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37947 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37948 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37949 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37950 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37951 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37952 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37953 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37955 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37956 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37957 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37958 * @cfg {Number} width For East/West panels
37959 * @cfg {Number} height For North/South panels
37960 * @cfg {Boolean} split To show the splitter
37961 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37963 * @cfg {string} cls Extra CSS classes to add to region
37965 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37966 * @cfg {string} region the region that it inhabits..
37969 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37970 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37972 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37973 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37974 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37976 Roo.bootstrap.layout.Region = function(config)
37978 this.applyConfig(config);
37980 var mgr = config.mgr;
37981 var pos = config.region;
37982 config.skipConfig = true;
37983 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37986 this.onRender(mgr.el);
37989 this.visible = true;
37990 this.collapsed = false;
37991 this.unrendered_panels = [];
37994 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37996 position: '', // set by wrapper (eg. north/south etc..)
37997 unrendered_panels : null, // unrendered panels.
37999 tabPosition : false,
38001 mgr: false, // points to 'Border'
38004 createBody : function(){
38005 /** This region's body element
38006 * @type Roo.Element */
38007 this.bodyEl = this.el.createChild({
38009 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38013 onRender: function(ctr, pos)
38015 var dh = Roo.DomHelper;
38016 /** This region's container element
38017 * @type Roo.Element */
38018 this.el = dh.append(ctr.dom, {
38020 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38022 /** This region's title element
38023 * @type Roo.Element */
38025 this.titleEl = dh.append(this.el.dom, {
38027 unselectable: "on",
38028 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38030 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38031 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38035 this.titleEl.enableDisplayMode();
38036 /** This region's title text element
38037 * @type HTMLElement */
38038 this.titleTextEl = this.titleEl.dom.firstChild;
38039 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38041 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38042 this.closeBtn.enableDisplayMode();
38043 this.closeBtn.on("click", this.closeClicked, this);
38044 this.closeBtn.hide();
38046 this.createBody(this.config);
38047 if(this.config.hideWhenEmpty){
38049 this.on("paneladded", this.validateVisibility, this);
38050 this.on("panelremoved", this.validateVisibility, this);
38052 if(this.autoScroll){
38053 this.bodyEl.setStyle("overflow", "auto");
38055 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38057 //if(c.titlebar !== false){
38058 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38059 this.titleEl.hide();
38061 this.titleEl.show();
38062 if(this.config.title){
38063 this.titleTextEl.innerHTML = this.config.title;
38067 if(this.config.collapsed){
38068 this.collapse(true);
38070 if(this.config.hidden){
38074 if (this.unrendered_panels && this.unrendered_panels.length) {
38075 for (var i =0;i< this.unrendered_panels.length; i++) {
38076 this.add(this.unrendered_panels[i]);
38078 this.unrendered_panels = null;
38084 applyConfig : function(c)
38087 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38088 var dh = Roo.DomHelper;
38089 if(c.titlebar !== false){
38090 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38091 this.collapseBtn.on("click", this.collapse, this);
38092 this.collapseBtn.enableDisplayMode();
38094 if(c.showPin === true || this.showPin){
38095 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38096 this.stickBtn.enableDisplayMode();
38097 this.stickBtn.on("click", this.expand, this);
38098 this.stickBtn.hide();
38103 /** This region's collapsed element
38104 * @type Roo.Element */
38107 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38108 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38111 if(c.floatable !== false){
38112 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38113 this.collapsedEl.on("click", this.collapseClick, this);
38116 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38117 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38118 id: "message", unselectable: "on", style:{"float":"left"}});
38119 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38121 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38122 this.expandBtn.on("click", this.expand, this);
38126 if(this.collapseBtn){
38127 this.collapseBtn.setVisible(c.collapsible == true);
38130 this.cmargins = c.cmargins || this.cmargins ||
38131 (this.position == "west" || this.position == "east" ?
38132 {top: 0, left: 2, right:2, bottom: 0} :
38133 {top: 2, left: 0, right:0, bottom: 2});
38135 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38138 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38140 this.autoScroll = c.autoScroll || false;
38145 this.duration = c.duration || .30;
38146 this.slideDuration = c.slideDuration || .45;
38151 * Returns true if this region is currently visible.
38152 * @return {Boolean}
38154 isVisible : function(){
38155 return this.visible;
38159 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38160 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38162 //setCollapsedTitle : function(title){
38163 // title = title || " ";
38164 // if(this.collapsedTitleTextEl){
38165 // this.collapsedTitleTextEl.innerHTML = title;
38169 getBox : function(){
38171 // if(!this.collapsed){
38172 b = this.el.getBox(false, true);
38174 // b = this.collapsedEl.getBox(false, true);
38179 getMargins : function(){
38180 return this.margins;
38181 //return this.collapsed ? this.cmargins : this.margins;
38184 highlight : function(){
38185 this.el.addClass("x-layout-panel-dragover");
38188 unhighlight : function(){
38189 this.el.removeClass("x-layout-panel-dragover");
38192 updateBox : function(box)
38194 if (!this.bodyEl) {
38195 return; // not rendered yet..
38199 if(!this.collapsed){
38200 this.el.dom.style.left = box.x + "px";
38201 this.el.dom.style.top = box.y + "px";
38202 this.updateBody(box.width, box.height);
38204 this.collapsedEl.dom.style.left = box.x + "px";
38205 this.collapsedEl.dom.style.top = box.y + "px";
38206 this.collapsedEl.setSize(box.width, box.height);
38209 this.tabs.autoSizeTabs();
38213 updateBody : function(w, h)
38216 this.el.setWidth(w);
38217 w -= this.el.getBorderWidth("rl");
38218 if(this.config.adjustments){
38219 w += this.config.adjustments[0];
38222 if(h !== null && h > 0){
38223 this.el.setHeight(h);
38224 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38225 h -= this.el.getBorderWidth("tb");
38226 if(this.config.adjustments){
38227 h += this.config.adjustments[1];
38229 this.bodyEl.setHeight(h);
38231 h = this.tabs.syncHeight(h);
38234 if(this.panelSize){
38235 w = w !== null ? w : this.panelSize.width;
38236 h = h !== null ? h : this.panelSize.height;
38238 if(this.activePanel){
38239 var el = this.activePanel.getEl();
38240 w = w !== null ? w : el.getWidth();
38241 h = h !== null ? h : el.getHeight();
38242 this.panelSize = {width: w, height: h};
38243 this.activePanel.setSize(w, h);
38245 if(Roo.isIE && this.tabs){
38246 this.tabs.el.repaint();
38251 * Returns the container element for this region.
38252 * @return {Roo.Element}
38254 getEl : function(){
38259 * Hides this region.
38262 //if(!this.collapsed){
38263 this.el.dom.style.left = "-2000px";
38266 // this.collapsedEl.dom.style.left = "-2000px";
38267 // this.collapsedEl.hide();
38269 this.visible = false;
38270 this.fireEvent("visibilitychange", this, false);
38274 * Shows this region if it was previously hidden.
38277 //if(!this.collapsed){
38280 // this.collapsedEl.show();
38282 this.visible = true;
38283 this.fireEvent("visibilitychange", this, true);
38286 closeClicked : function(){
38287 if(this.activePanel){
38288 this.remove(this.activePanel);
38292 collapseClick : function(e){
38294 e.stopPropagation();
38297 e.stopPropagation();
38303 * Collapses this region.
38304 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38307 collapse : function(skipAnim, skipCheck = false){
38308 if(this.collapsed) {
38312 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38314 this.collapsed = true;
38316 this.split.el.hide();
38318 if(this.config.animate && skipAnim !== true){
38319 this.fireEvent("invalidated", this);
38320 this.animateCollapse();
38322 this.el.setLocation(-20000,-20000);
38324 this.collapsedEl.show();
38325 this.fireEvent("collapsed", this);
38326 this.fireEvent("invalidated", this);
38332 animateCollapse : function(){
38337 * Expands this region if it was previously collapsed.
38338 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38339 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38342 expand : function(e, skipAnim){
38344 e.stopPropagation();
38346 if(!this.collapsed || this.el.hasActiveFx()) {
38350 this.afterSlideIn();
38353 this.collapsed = false;
38354 if(this.config.animate && skipAnim !== true){
38355 this.animateExpand();
38359 this.split.el.show();
38361 this.collapsedEl.setLocation(-2000,-2000);
38362 this.collapsedEl.hide();
38363 this.fireEvent("invalidated", this);
38364 this.fireEvent("expanded", this);
38368 animateExpand : function(){
38372 initTabs : function()
38374 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38376 var ts = new Roo.bootstrap.panel.Tabs({
38377 el: this.bodyEl.dom,
38379 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38380 disableTooltips: this.config.disableTabTips,
38381 toolbar : this.config.toolbar
38384 if(this.config.hideTabs){
38385 ts.stripWrap.setDisplayed(false);
38388 ts.resizeTabs = this.config.resizeTabs === true;
38389 ts.minTabWidth = this.config.minTabWidth || 40;
38390 ts.maxTabWidth = this.config.maxTabWidth || 250;
38391 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38392 ts.monitorResize = false;
38393 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38394 ts.bodyEl.addClass('roo-layout-tabs-body');
38395 this.panels.each(this.initPanelAsTab, this);
38398 initPanelAsTab : function(panel){
38399 var ti = this.tabs.addTab(
38403 this.config.closeOnTab && panel.isClosable(),
38406 if(panel.tabTip !== undefined){
38407 ti.setTooltip(panel.tabTip);
38409 ti.on("activate", function(){
38410 this.setActivePanel(panel);
38413 if(this.config.closeOnTab){
38414 ti.on("beforeclose", function(t, e){
38416 this.remove(panel);
38420 panel.tabItem = ti;
38425 updatePanelTitle : function(panel, title)
38427 if(this.activePanel == panel){
38428 this.updateTitle(title);
38431 var ti = this.tabs.getTab(panel.getEl().id);
38433 if(panel.tabTip !== undefined){
38434 ti.setTooltip(panel.tabTip);
38439 updateTitle : function(title){
38440 if(this.titleTextEl && !this.config.title){
38441 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38445 setActivePanel : function(panel)
38447 panel = this.getPanel(panel);
38448 if(this.activePanel && this.activePanel != panel){
38449 if(this.activePanel.setActiveState(false) === false){
38453 this.activePanel = panel;
38454 panel.setActiveState(true);
38455 if(this.panelSize){
38456 panel.setSize(this.panelSize.width, this.panelSize.height);
38459 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38461 this.updateTitle(panel.getTitle());
38463 this.fireEvent("invalidated", this);
38465 this.fireEvent("panelactivated", this, panel);
38469 * Shows the specified panel.
38470 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38471 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38473 showPanel : function(panel)
38475 panel = this.getPanel(panel);
38478 var tab = this.tabs.getTab(panel.getEl().id);
38479 if(tab.isHidden()){
38480 this.tabs.unhideTab(tab.id);
38484 this.setActivePanel(panel);
38491 * Get the active panel for this region.
38492 * @return {Roo.ContentPanel} The active panel or null
38494 getActivePanel : function(){
38495 return this.activePanel;
38498 validateVisibility : function(){
38499 if(this.panels.getCount() < 1){
38500 this.updateTitle(" ");
38501 this.closeBtn.hide();
38504 if(!this.isVisible()){
38511 * Adds the passed ContentPanel(s) to this region.
38512 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38513 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38515 add : function(panel)
38517 if(arguments.length > 1){
38518 for(var i = 0, len = arguments.length; i < len; i++) {
38519 this.add(arguments[i]);
38524 // if we have not been rendered yet, then we can not really do much of this..
38525 if (!this.bodyEl) {
38526 this.unrendered_panels.push(panel);
38533 if(this.hasPanel(panel)){
38534 this.showPanel(panel);
38537 panel.setRegion(this);
38538 this.panels.add(panel);
38539 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38540 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38541 // and hide them... ???
38542 this.bodyEl.dom.appendChild(panel.getEl().dom);
38543 if(panel.background !== true){
38544 this.setActivePanel(panel);
38546 this.fireEvent("paneladded", this, panel);
38553 this.initPanelAsTab(panel);
38557 if(panel.background !== true){
38558 this.tabs.activate(panel.getEl().id);
38560 this.fireEvent("paneladded", this, panel);
38565 * Hides the tab for the specified panel.
38566 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38568 hidePanel : function(panel){
38569 if(this.tabs && (panel = this.getPanel(panel))){
38570 this.tabs.hideTab(panel.getEl().id);
38575 * Unhides the tab for a previously hidden panel.
38576 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38578 unhidePanel : function(panel){
38579 if(this.tabs && (panel = this.getPanel(panel))){
38580 this.tabs.unhideTab(panel.getEl().id);
38584 clearPanels : function(){
38585 while(this.panels.getCount() > 0){
38586 this.remove(this.panels.first());
38591 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38592 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38593 * @param {Boolean} preservePanel Overrides the config preservePanel option
38594 * @return {Roo.ContentPanel} The panel that was removed
38596 remove : function(panel, preservePanel)
38598 panel = this.getPanel(panel);
38603 this.fireEvent("beforeremove", this, panel, e);
38604 if(e.cancel === true){
38607 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38608 var panelId = panel.getId();
38609 this.panels.removeKey(panelId);
38611 document.body.appendChild(panel.getEl().dom);
38614 this.tabs.removeTab(panel.getEl().id);
38615 }else if (!preservePanel){
38616 this.bodyEl.dom.removeChild(panel.getEl().dom);
38618 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38619 var p = this.panels.first();
38620 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38621 tempEl.appendChild(p.getEl().dom);
38622 this.bodyEl.update("");
38623 this.bodyEl.dom.appendChild(p.getEl().dom);
38625 this.updateTitle(p.getTitle());
38627 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38628 this.setActivePanel(p);
38630 panel.setRegion(null);
38631 if(this.activePanel == panel){
38632 this.activePanel = null;
38634 if(this.config.autoDestroy !== false && preservePanel !== true){
38635 try{panel.destroy();}catch(e){}
38637 this.fireEvent("panelremoved", this, panel);
38642 * Returns the TabPanel component used by this region
38643 * @return {Roo.TabPanel}
38645 getTabs : function(){
38649 createTool : function(parentEl, className){
38650 var btn = Roo.DomHelper.append(parentEl, {
38652 cls: "x-layout-tools-button",
38655 cls: "roo-layout-tools-button-inner " + className,
38659 btn.addClassOnOver("roo-layout-tools-button-over");
38664 * Ext JS Library 1.1.1
38665 * Copyright(c) 2006-2007, Ext JS, LLC.
38667 * Originally Released Under LGPL - original licence link has changed is not relivant.
38670 * <script type="text/javascript">
38676 * @class Roo.SplitLayoutRegion
38677 * @extends Roo.LayoutRegion
38678 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38680 Roo.bootstrap.layout.Split = function(config){
38681 this.cursor = config.cursor;
38682 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38685 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38687 splitTip : "Drag to resize.",
38688 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38689 useSplitTips : false,
38691 applyConfig : function(config){
38692 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38695 onRender : function(ctr,pos) {
38697 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38698 if(!this.config.split){
38703 var splitEl = Roo.DomHelper.append(ctr.dom, {
38705 id: this.el.id + "-split",
38706 cls: "roo-layout-split roo-layout-split-"+this.position,
38709 /** The SplitBar for this region
38710 * @type Roo.SplitBar */
38711 // does not exist yet...
38712 Roo.log([this.position, this.orientation]);
38714 this.split = new Roo.bootstrap.SplitBar({
38715 dragElement : splitEl,
38716 resizingElement: this.el,
38717 orientation : this.orientation
38720 this.split.on("moved", this.onSplitMove, this);
38721 this.split.useShim = this.config.useShim === true;
38722 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38723 if(this.useSplitTips){
38724 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38726 //if(config.collapsible){
38727 // this.split.el.on("dblclick", this.collapse, this);
38730 if(typeof this.config.minSize != "undefined"){
38731 this.split.minSize = this.config.minSize;
38733 if(typeof this.config.maxSize != "undefined"){
38734 this.split.maxSize = this.config.maxSize;
38736 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38737 this.hideSplitter();
38742 getHMaxSize : function(){
38743 var cmax = this.config.maxSize || 10000;
38744 var center = this.mgr.getRegion("center");
38745 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38748 getVMaxSize : function(){
38749 var cmax = this.config.maxSize || 10000;
38750 var center = this.mgr.getRegion("center");
38751 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38754 onSplitMove : function(split, newSize){
38755 this.fireEvent("resized", this, newSize);
38759 * Returns the {@link Roo.SplitBar} for this region.
38760 * @return {Roo.SplitBar}
38762 getSplitBar : function(){
38767 this.hideSplitter();
38768 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38771 hideSplitter : function(){
38773 this.split.el.setLocation(-2000,-2000);
38774 this.split.el.hide();
38780 this.split.el.show();
38782 Roo.bootstrap.layout.Split.superclass.show.call(this);
38785 beforeSlide: function(){
38786 if(Roo.isGecko){// firefox overflow auto bug workaround
38787 this.bodyEl.clip();
38789 this.tabs.bodyEl.clip();
38791 if(this.activePanel){
38792 this.activePanel.getEl().clip();
38794 if(this.activePanel.beforeSlide){
38795 this.activePanel.beforeSlide();
38801 afterSlide : function(){
38802 if(Roo.isGecko){// firefox overflow auto bug workaround
38803 this.bodyEl.unclip();
38805 this.tabs.bodyEl.unclip();
38807 if(this.activePanel){
38808 this.activePanel.getEl().unclip();
38809 if(this.activePanel.afterSlide){
38810 this.activePanel.afterSlide();
38816 initAutoHide : function(){
38817 if(this.autoHide !== false){
38818 if(!this.autoHideHd){
38819 var st = new Roo.util.DelayedTask(this.slideIn, this);
38820 this.autoHideHd = {
38821 "mouseout": function(e){
38822 if(!e.within(this.el, true)){
38826 "mouseover" : function(e){
38832 this.el.on(this.autoHideHd);
38836 clearAutoHide : function(){
38837 if(this.autoHide !== false){
38838 this.el.un("mouseout", this.autoHideHd.mouseout);
38839 this.el.un("mouseover", this.autoHideHd.mouseover);
38843 clearMonitor : function(){
38844 Roo.get(document).un("click", this.slideInIf, this);
38847 // these names are backwards but not changed for compat
38848 slideOut : function(){
38849 if(this.isSlid || this.el.hasActiveFx()){
38852 this.isSlid = true;
38853 if(this.collapseBtn){
38854 this.collapseBtn.hide();
38856 this.closeBtnState = this.closeBtn.getStyle('display');
38857 this.closeBtn.hide();
38859 this.stickBtn.show();
38862 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38863 this.beforeSlide();
38864 this.el.setStyle("z-index", 10001);
38865 this.el.slideIn(this.getSlideAnchor(), {
38866 callback: function(){
38868 this.initAutoHide();
38869 Roo.get(document).on("click", this.slideInIf, this);
38870 this.fireEvent("slideshow", this);
38877 afterSlideIn : function(){
38878 this.clearAutoHide();
38879 this.isSlid = false;
38880 this.clearMonitor();
38881 this.el.setStyle("z-index", "");
38882 if(this.collapseBtn){
38883 this.collapseBtn.show();
38885 this.closeBtn.setStyle('display', this.closeBtnState);
38887 this.stickBtn.hide();
38889 this.fireEvent("slidehide", this);
38892 slideIn : function(cb){
38893 if(!this.isSlid || this.el.hasActiveFx()){
38897 this.isSlid = false;
38898 this.beforeSlide();
38899 this.el.slideOut(this.getSlideAnchor(), {
38900 callback: function(){
38901 this.el.setLeftTop(-10000, -10000);
38903 this.afterSlideIn();
38911 slideInIf : function(e){
38912 if(!e.within(this.el)){
38917 animateCollapse : function(){
38918 this.beforeSlide();
38919 this.el.setStyle("z-index", 20000);
38920 var anchor = this.getSlideAnchor();
38921 this.el.slideOut(anchor, {
38922 callback : function(){
38923 this.el.setStyle("z-index", "");
38924 this.collapsedEl.slideIn(anchor, {duration:.3});
38926 this.el.setLocation(-10000,-10000);
38928 this.fireEvent("collapsed", this);
38935 animateExpand : function(){
38936 this.beforeSlide();
38937 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38938 this.el.setStyle("z-index", 20000);
38939 this.collapsedEl.hide({
38942 this.el.slideIn(this.getSlideAnchor(), {
38943 callback : function(){
38944 this.el.setStyle("z-index", "");
38947 this.split.el.show();
38949 this.fireEvent("invalidated", this);
38950 this.fireEvent("expanded", this);
38978 getAnchor : function(){
38979 return this.anchors[this.position];
38982 getCollapseAnchor : function(){
38983 return this.canchors[this.position];
38986 getSlideAnchor : function(){
38987 return this.sanchors[this.position];
38990 getAlignAdj : function(){
38991 var cm = this.cmargins;
38992 switch(this.position){
39008 getExpandAdj : function(){
39009 var c = this.collapsedEl, cm = this.cmargins;
39010 switch(this.position){
39012 return [-(cm.right+c.getWidth()+cm.left), 0];
39015 return [cm.right+c.getWidth()+cm.left, 0];
39018 return [0, -(cm.top+cm.bottom+c.getHeight())];
39021 return [0, cm.top+cm.bottom+c.getHeight()];
39027 * Ext JS Library 1.1.1
39028 * Copyright(c) 2006-2007, Ext JS, LLC.
39030 * Originally Released Under LGPL - original licence link has changed is not relivant.
39033 * <script type="text/javascript">
39036 * These classes are private internal classes
39038 Roo.bootstrap.layout.Center = function(config){
39039 config.region = "center";
39040 Roo.bootstrap.layout.Region.call(this, config);
39041 this.visible = true;
39042 this.minWidth = config.minWidth || 20;
39043 this.minHeight = config.minHeight || 20;
39046 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39048 // center panel can't be hidden
39052 // center panel can't be hidden
39055 getMinWidth: function(){
39056 return this.minWidth;
39059 getMinHeight: function(){
39060 return this.minHeight;
39074 Roo.bootstrap.layout.North = function(config)
39076 config.region = 'north';
39077 config.cursor = 'n-resize';
39079 Roo.bootstrap.layout.Split.call(this, config);
39083 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39084 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39085 this.split.el.addClass("roo-layout-split-v");
39087 var size = config.initialSize || config.height;
39088 if(typeof size != "undefined"){
39089 this.el.setHeight(size);
39092 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39094 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39098 getBox : function(){
39099 if(this.collapsed){
39100 return this.collapsedEl.getBox();
39102 var box = this.el.getBox();
39104 box.height += this.split.el.getHeight();
39109 updateBox : function(box){
39110 if(this.split && !this.collapsed){
39111 box.height -= this.split.el.getHeight();
39112 this.split.el.setLeft(box.x);
39113 this.split.el.setTop(box.y+box.height);
39114 this.split.el.setWidth(box.width);
39116 if(this.collapsed){
39117 this.updateBody(box.width, null);
39119 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39127 Roo.bootstrap.layout.South = function(config){
39128 config.region = 'south';
39129 config.cursor = 's-resize';
39130 Roo.bootstrap.layout.Split.call(this, config);
39132 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39133 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39134 this.split.el.addClass("roo-layout-split-v");
39136 var size = config.initialSize || config.height;
39137 if(typeof size != "undefined"){
39138 this.el.setHeight(size);
39142 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39143 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39144 getBox : function(){
39145 if(this.collapsed){
39146 return this.collapsedEl.getBox();
39148 var box = this.el.getBox();
39150 var sh = this.split.el.getHeight();
39157 updateBox : function(box){
39158 if(this.split && !this.collapsed){
39159 var sh = this.split.el.getHeight();
39162 this.split.el.setLeft(box.x);
39163 this.split.el.setTop(box.y-sh);
39164 this.split.el.setWidth(box.width);
39166 if(this.collapsed){
39167 this.updateBody(box.width, null);
39169 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39173 Roo.bootstrap.layout.East = function(config){
39174 config.region = "east";
39175 config.cursor = "e-resize";
39176 Roo.bootstrap.layout.Split.call(this, config);
39178 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39179 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39180 this.split.el.addClass("roo-layout-split-h");
39182 var size = config.initialSize || config.width;
39183 if(typeof size != "undefined"){
39184 this.el.setWidth(size);
39187 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39188 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39189 getBox : function(){
39190 if(this.collapsed){
39191 return this.collapsedEl.getBox();
39193 var box = this.el.getBox();
39195 var sw = this.split.el.getWidth();
39202 updateBox : function(box){
39203 if(this.split && !this.collapsed){
39204 var sw = this.split.el.getWidth();
39206 this.split.el.setLeft(box.x);
39207 this.split.el.setTop(box.y);
39208 this.split.el.setHeight(box.height);
39211 if(this.collapsed){
39212 this.updateBody(null, box.height);
39214 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39218 Roo.bootstrap.layout.West = function(config){
39219 config.region = "west";
39220 config.cursor = "w-resize";
39222 Roo.bootstrap.layout.Split.call(this, config);
39224 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39225 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39226 this.split.el.addClass("roo-layout-split-h");
39230 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39231 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39233 onRender: function(ctr, pos)
39235 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39236 var size = this.config.initialSize || this.config.width;
39237 if(typeof size != "undefined"){
39238 this.el.setWidth(size);
39242 getBox : function(){
39243 if(this.collapsed){
39244 return this.collapsedEl.getBox();
39246 var box = this.el.getBox();
39248 box.width += this.split.el.getWidth();
39253 updateBox : function(box){
39254 if(this.split && !this.collapsed){
39255 var sw = this.split.el.getWidth();
39257 this.split.el.setLeft(box.x+box.width);
39258 this.split.el.setTop(box.y);
39259 this.split.el.setHeight(box.height);
39261 if(this.collapsed){
39262 this.updateBody(null, box.height);
39264 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39266 });Roo.namespace("Roo.bootstrap.panel");/*
39268 * Ext JS Library 1.1.1
39269 * Copyright(c) 2006-2007, Ext JS, LLC.
39271 * Originally Released Under LGPL - original licence link has changed is not relivant.
39274 * <script type="text/javascript">
39277 * @class Roo.ContentPanel
39278 * @extends Roo.util.Observable
39279 * A basic ContentPanel element.
39280 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39281 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39282 * @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
39283 * @cfg {Boolean} closable True if the panel can be closed/removed
39284 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39285 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39286 * @cfg {Toolbar} toolbar A toolbar for this panel
39287 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39288 * @cfg {String} title The title for this panel
39289 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39290 * @cfg {String} url Calls {@link #setUrl} with this value
39291 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39292 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39293 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39294 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39295 * @cfg {Boolean} badges render the badges
39296 * @cfg {String} cls extra classes to use
39297 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39300 * Create a new ContentPanel.
39301 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39302 * @param {String/Object} config A string to set only the title or a config object
39303 * @param {String} content (optional) Set the HTML content for this panel
39304 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39306 Roo.bootstrap.panel.Content = function( config){
39308 this.tpl = config.tpl || false;
39310 var el = config.el;
39311 var content = config.content;
39313 if(config.autoCreate){ // xtype is available if this is called from factory
39316 this.el = Roo.get(el);
39317 if(!this.el && config && config.autoCreate){
39318 if(typeof config.autoCreate == "object"){
39319 if(!config.autoCreate.id){
39320 config.autoCreate.id = config.id||el;
39322 this.el = Roo.DomHelper.append(document.body,
39323 config.autoCreate, true);
39327 cls: (config.cls || '') +
39328 (config.background ? ' bg-' + config.background : '') +
39329 " roo-layout-inactive-content",
39333 elcfg.html = config.html;
39337 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39340 this.closable = false;
39341 this.loaded = false;
39342 this.active = false;
39345 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39347 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39349 this.wrapEl = this.el; //this.el.wrap();
39351 if (config.toolbar.items) {
39352 ti = config.toolbar.items ;
39353 delete config.toolbar.items ;
39357 this.toolbar.render(this.wrapEl, 'before');
39358 for(var i =0;i < ti.length;i++) {
39359 // Roo.log(['add child', items[i]]);
39360 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39362 this.toolbar.items = nitems;
39363 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39364 delete config.toolbar;
39368 // xtype created footer. - not sure if will work as we normally have to render first..
39369 if (this.footer && !this.footer.el && this.footer.xtype) {
39370 if (!this.wrapEl) {
39371 this.wrapEl = this.el.wrap();
39374 this.footer.container = this.wrapEl.createChild();
39376 this.footer = Roo.factory(this.footer, Roo);
39381 if(typeof config == "string"){
39382 this.title = config;
39384 Roo.apply(this, config);
39388 this.resizeEl = Roo.get(this.resizeEl, true);
39390 this.resizeEl = this.el;
39392 // handle view.xtype
39400 * Fires when this panel is activated.
39401 * @param {Roo.ContentPanel} this
39405 * @event deactivate
39406 * Fires when this panel is activated.
39407 * @param {Roo.ContentPanel} this
39409 "deactivate" : true,
39413 * Fires when this panel is resized if fitToFrame is true.
39414 * @param {Roo.ContentPanel} this
39415 * @param {Number} width The width after any component adjustments
39416 * @param {Number} height The height after any component adjustments
39422 * Fires when this tab is created
39423 * @param {Roo.ContentPanel} this
39434 if(this.autoScroll){
39435 this.resizeEl.setStyle("overflow", "auto");
39437 // fix randome scrolling
39438 //this.el.on('scroll', function() {
39439 // Roo.log('fix random scolling');
39440 // this.scrollTo('top',0);
39443 content = content || this.content;
39445 this.setContent(content);
39447 if(config && config.url){
39448 this.setUrl(this.url, this.params, this.loadOnce);
39453 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39455 if (this.view && typeof(this.view.xtype) != 'undefined') {
39456 this.view.el = this.el.appendChild(document.createElement("div"));
39457 this.view = Roo.factory(this.view);
39458 this.view.render && this.view.render(false, '');
39462 this.fireEvent('render', this);
39465 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39472 setRegion : function(region){
39473 this.region = region;
39474 this.setActiveClass(region && !this.background);
39478 setActiveClass: function(state)
39481 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39482 this.el.setStyle('position','relative');
39484 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39485 this.el.setStyle('position', 'absolute');
39490 * Returns the toolbar for this Panel if one was configured.
39491 * @return {Roo.Toolbar}
39493 getToolbar : function(){
39494 return this.toolbar;
39497 setActiveState : function(active)
39499 this.active = active;
39500 this.setActiveClass(active);
39502 if(this.fireEvent("deactivate", this) === false){
39507 this.fireEvent("activate", this);
39511 * Updates this panel's element
39512 * @param {String} content The new content
39513 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39515 setContent : function(content, loadScripts){
39516 this.el.update(content, loadScripts);
39519 ignoreResize : function(w, h){
39520 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39523 this.lastSize = {width: w, height: h};
39528 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39529 * @return {Roo.UpdateManager} The UpdateManager
39531 getUpdateManager : function(){
39532 return this.el.getUpdateManager();
39535 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39536 * @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:
39539 url: "your-url.php",
39540 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39541 callback: yourFunction,
39542 scope: yourObject, //(optional scope)
39545 text: "Loading...",
39550 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39551 * 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.
39552 * @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}
39553 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39554 * @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.
39555 * @return {Roo.ContentPanel} this
39558 var um = this.el.getUpdateManager();
39559 um.update.apply(um, arguments);
39565 * 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.
39566 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39567 * @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)
39568 * @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)
39569 * @return {Roo.UpdateManager} The UpdateManager
39571 setUrl : function(url, params, loadOnce){
39572 if(this.refreshDelegate){
39573 this.removeListener("activate", this.refreshDelegate);
39575 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39576 this.on("activate", this.refreshDelegate);
39577 return this.el.getUpdateManager();
39580 _handleRefresh : function(url, params, loadOnce){
39581 if(!loadOnce || !this.loaded){
39582 var updater = this.el.getUpdateManager();
39583 updater.update(url, params, this._setLoaded.createDelegate(this));
39587 _setLoaded : function(){
39588 this.loaded = true;
39592 * Returns this panel's id
39595 getId : function(){
39600 * Returns this panel's element - used by regiosn to add.
39601 * @return {Roo.Element}
39603 getEl : function(){
39604 return this.wrapEl || this.el;
39609 adjustForComponents : function(width, height)
39611 //Roo.log('adjustForComponents ');
39612 if(this.resizeEl != this.el){
39613 width -= this.el.getFrameWidth('lr');
39614 height -= this.el.getFrameWidth('tb');
39617 var te = this.toolbar.getEl();
39618 te.setWidth(width);
39619 height -= te.getHeight();
39622 var te = this.footer.getEl();
39623 te.setWidth(width);
39624 height -= te.getHeight();
39628 if(this.adjustments){
39629 width += this.adjustments[0];
39630 height += this.adjustments[1];
39632 return {"width": width, "height": height};
39635 setSize : function(width, height){
39636 if(this.fitToFrame && !this.ignoreResize(width, height)){
39637 if(this.fitContainer && this.resizeEl != this.el){
39638 this.el.setSize(width, height);
39640 var size = this.adjustForComponents(width, height);
39641 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39642 this.fireEvent('resize', this, size.width, size.height);
39647 * Returns this panel's title
39650 getTitle : function(){
39652 if (typeof(this.title) != 'object') {
39657 for (var k in this.title) {
39658 if (!this.title.hasOwnProperty(k)) {
39662 if (k.indexOf('-') >= 0) {
39663 var s = k.split('-');
39664 for (var i = 0; i<s.length; i++) {
39665 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39668 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39675 * Set this panel's title
39676 * @param {String} title
39678 setTitle : function(title){
39679 this.title = title;
39681 this.region.updatePanelTitle(this, title);
39686 * Returns true is this panel was configured to be closable
39687 * @return {Boolean}
39689 isClosable : function(){
39690 return this.closable;
39693 beforeSlide : function(){
39695 this.resizeEl.clip();
39698 afterSlide : function(){
39700 this.resizeEl.unclip();
39704 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39705 * Will fail silently if the {@link #setUrl} method has not been called.
39706 * This does not activate the panel, just updates its content.
39708 refresh : function(){
39709 if(this.refreshDelegate){
39710 this.loaded = false;
39711 this.refreshDelegate();
39716 * Destroys this panel
39718 destroy : function(){
39719 this.el.removeAllListeners();
39720 var tempEl = document.createElement("span");
39721 tempEl.appendChild(this.el.dom);
39722 tempEl.innerHTML = "";
39728 * form - if the content panel contains a form - this is a reference to it.
39729 * @type {Roo.form.Form}
39733 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39734 * This contains a reference to it.
39740 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39750 * @param {Object} cfg Xtype definition of item to add.
39754 getChildContainer: function () {
39755 return this.getEl();
39760 var ret = new Roo.factory(cfg);
39765 if (cfg.xtype.match(/^Form$/)) {
39768 //if (this.footer) {
39769 // el = this.footer.container.insertSibling(false, 'before');
39771 el = this.el.createChild();
39774 this.form = new Roo.form.Form(cfg);
39777 if ( this.form.allItems.length) {
39778 this.form.render(el.dom);
39782 // should only have one of theses..
39783 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39784 // views.. should not be just added - used named prop 'view''
39786 cfg.el = this.el.appendChild(document.createElement("div"));
39789 var ret = new Roo.factory(cfg);
39791 ret.render && ret.render(false, ''); // render blank..
39801 * @class Roo.bootstrap.panel.Grid
39802 * @extends Roo.bootstrap.panel.Content
39804 * Create a new GridPanel.
39805 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39806 * @param {Object} config A the config object
39812 Roo.bootstrap.panel.Grid = function(config)
39816 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39817 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39819 config.el = this.wrapper;
39820 //this.el = this.wrapper;
39822 if (config.container) {
39823 // ctor'ed from a Border/panel.grid
39826 this.wrapper.setStyle("overflow", "hidden");
39827 this.wrapper.addClass('roo-grid-container');
39832 if(config.toolbar){
39833 var tool_el = this.wrapper.createChild();
39834 this.toolbar = Roo.factory(config.toolbar);
39836 if (config.toolbar.items) {
39837 ti = config.toolbar.items ;
39838 delete config.toolbar.items ;
39842 this.toolbar.render(tool_el);
39843 for(var i =0;i < ti.length;i++) {
39844 // Roo.log(['add child', items[i]]);
39845 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39847 this.toolbar.items = nitems;
39849 delete config.toolbar;
39852 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39853 config.grid.scrollBody = true;;
39854 config.grid.monitorWindowResize = false; // turn off autosizing
39855 config.grid.autoHeight = false;
39856 config.grid.autoWidth = false;
39858 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39860 if (config.background) {
39861 // render grid on panel activation (if panel background)
39862 this.on('activate', function(gp) {
39863 if (!gp.grid.rendered) {
39864 gp.grid.render(this.wrapper);
39865 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39870 this.grid.render(this.wrapper);
39871 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39874 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39875 // ??? needed ??? config.el = this.wrapper;
39880 // xtype created footer. - not sure if will work as we normally have to render first..
39881 if (this.footer && !this.footer.el && this.footer.xtype) {
39883 var ctr = this.grid.getView().getFooterPanel(true);
39884 this.footer.dataSource = this.grid.dataSource;
39885 this.footer = Roo.factory(this.footer, Roo);
39886 this.footer.render(ctr);
39896 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39897 getId : function(){
39898 return this.grid.id;
39902 * Returns the grid for this panel
39903 * @return {Roo.bootstrap.Table}
39905 getGrid : function(){
39909 setSize : function(width, height){
39910 if(!this.ignoreResize(width, height)){
39911 var grid = this.grid;
39912 var size = this.adjustForComponents(width, height);
39913 // tfoot is not a footer?
39916 var gridel = grid.getGridEl();
39917 gridel.setSize(size.width, size.height);
39919 var tbd = grid.getGridEl().select('tbody', true).first();
39920 var thd = grid.getGridEl().select('thead',true).first();
39921 var tbf= grid.getGridEl().select('tfoot', true).first();
39924 size.height -= thd.getHeight();
39927 size.height -= thd.getHeight();
39930 tbd.setSize(size.width, size.height );
39931 // this is for the account management tab -seems to work there.
39932 var thd = grid.getGridEl().select('thead',true).first();
39934 // tbd.setSize(size.width, size.height - thd.getHeight());
39943 beforeSlide : function(){
39944 this.grid.getView().scroller.clip();
39947 afterSlide : function(){
39948 this.grid.getView().scroller.unclip();
39951 destroy : function(){
39952 this.grid.destroy();
39954 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39959 * @class Roo.bootstrap.panel.Nest
39960 * @extends Roo.bootstrap.panel.Content
39962 * Create a new Panel, that can contain a layout.Border.
39965 * @param {Roo.BorderLayout} layout The layout for this panel
39966 * @param {String/Object} config A string to set only the title or a config object
39968 Roo.bootstrap.panel.Nest = function(config)
39970 // construct with only one argument..
39971 /* FIXME - implement nicer consturctors
39972 if (layout.layout) {
39974 layout = config.layout;
39975 delete config.layout;
39977 if (layout.xtype && !layout.getEl) {
39978 // then layout needs constructing..
39979 layout = Roo.factory(layout, Roo);
39983 config.el = config.layout.getEl();
39985 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39987 config.layout.monitorWindowResize = false; // turn off autosizing
39988 this.layout = config.layout;
39989 this.layout.getEl().addClass("roo-layout-nested-layout");
39990 this.layout.parent = this;
39997 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39999 setSize : function(width, height){
40000 if(!this.ignoreResize(width, height)){
40001 var size = this.adjustForComponents(width, height);
40002 var el = this.layout.getEl();
40003 if (size.height < 1) {
40004 el.setWidth(size.width);
40006 el.setSize(size.width, size.height);
40008 var touch = el.dom.offsetWidth;
40009 this.layout.layout();
40010 // ie requires a double layout on the first pass
40011 if(Roo.isIE && !this.initialized){
40012 this.initialized = true;
40013 this.layout.layout();
40018 // activate all subpanels if not currently active..
40020 setActiveState : function(active){
40021 this.active = active;
40022 this.setActiveClass(active);
40025 this.fireEvent("deactivate", this);
40029 this.fireEvent("activate", this);
40030 // not sure if this should happen before or after..
40031 if (!this.layout) {
40032 return; // should not happen..
40035 for (var r in this.layout.regions) {
40036 reg = this.layout.getRegion(r);
40037 if (reg.getActivePanel()) {
40038 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40039 reg.setActivePanel(reg.getActivePanel());
40042 if (!reg.panels.length) {
40045 reg.showPanel(reg.getPanel(0));
40054 * Returns the nested BorderLayout for this panel
40055 * @return {Roo.BorderLayout}
40057 getLayout : function(){
40058 return this.layout;
40062 * Adds a xtype elements to the layout of the nested panel
40066 xtype : 'ContentPanel',
40073 xtype : 'NestedLayoutPanel',
40079 items : [ ... list of content panels or nested layout panels.. ]
40083 * @param {Object} cfg Xtype definition of item to add.
40085 addxtype : function(cfg) {
40086 return this.layout.addxtype(cfg);
40091 * Ext JS Library 1.1.1
40092 * Copyright(c) 2006-2007, Ext JS, LLC.
40094 * Originally Released Under LGPL - original licence link has changed is not relivant.
40097 * <script type="text/javascript">
40100 * @class Roo.TabPanel
40101 * @extends Roo.util.Observable
40102 * A lightweight tab container.
40106 // basic tabs 1, built from existing content
40107 var tabs = new Roo.TabPanel("tabs1");
40108 tabs.addTab("script", "View Script");
40109 tabs.addTab("markup", "View Markup");
40110 tabs.activate("script");
40112 // more advanced tabs, built from javascript
40113 var jtabs = new Roo.TabPanel("jtabs");
40114 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40116 // set up the UpdateManager
40117 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40118 var updater = tab2.getUpdateManager();
40119 updater.setDefaultUrl("ajax1.htm");
40120 tab2.on('activate', updater.refresh, updater, true);
40122 // Use setUrl for Ajax loading
40123 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40124 tab3.setUrl("ajax2.htm", null, true);
40127 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40130 jtabs.activate("jtabs-1");
40133 * Create a new TabPanel.
40134 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40135 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40137 Roo.bootstrap.panel.Tabs = function(config){
40139 * The container element for this TabPanel.
40140 * @type Roo.Element
40142 this.el = Roo.get(config.el);
40145 if(typeof config == "boolean"){
40146 this.tabPosition = config ? "bottom" : "top";
40148 Roo.apply(this, config);
40152 if(this.tabPosition == "bottom"){
40153 // if tabs are at the bottom = create the body first.
40154 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40155 this.el.addClass("roo-tabs-bottom");
40157 // next create the tabs holders
40159 if (this.tabPosition == "west"){
40161 var reg = this.region; // fake it..
40163 if (!reg.mgr.parent) {
40166 reg = reg.mgr.parent.region;
40168 Roo.log("got nest?");
40170 if (reg.mgr.getRegion('west')) {
40171 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40172 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40173 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40174 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40175 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40183 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40184 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40185 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40186 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40191 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40194 // finally - if tabs are at the top, then create the body last..
40195 if(this.tabPosition != "bottom"){
40196 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40197 * @type Roo.Element
40199 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40200 this.el.addClass("roo-tabs-top");
40204 this.bodyEl.setStyle("position", "relative");
40206 this.active = null;
40207 this.activateDelegate = this.activate.createDelegate(this);
40212 * Fires when the active tab changes
40213 * @param {Roo.TabPanel} this
40214 * @param {Roo.TabPanelItem} activePanel The new active tab
40218 * @event beforetabchange
40219 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40220 * @param {Roo.TabPanel} this
40221 * @param {Object} e Set cancel to true on this object to cancel the tab change
40222 * @param {Roo.TabPanelItem} tab The tab being changed to
40224 "beforetabchange" : true
40227 Roo.EventManager.onWindowResize(this.onResize, this);
40228 this.cpad = this.el.getPadding("lr");
40229 this.hiddenCount = 0;
40232 // toolbar on the tabbar support...
40233 if (this.toolbar) {
40234 alert("no toolbar support yet");
40235 this.toolbar = false;
40237 var tcfg = this.toolbar;
40238 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40239 this.toolbar = new Roo.Toolbar(tcfg);
40240 if (Roo.isSafari) {
40241 var tbl = tcfg.container.child('table', true);
40242 tbl.setAttribute('width', '100%');
40250 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40253 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40255 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40257 tabPosition : "top",
40259 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40261 currentTabWidth : 0,
40263 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40267 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40271 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40273 preferredTabWidth : 175,
40275 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40277 resizeTabs : false,
40279 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40281 monitorResize : true,
40283 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40285 toolbar : false, // set by caller..
40287 region : false, /// set by caller
40289 disableTooltips : true, // not used yet...
40292 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40293 * @param {String} id The id of the div to use <b>or create</b>
40294 * @param {String} text The text for the tab
40295 * @param {String} content (optional) Content to put in the TabPanelItem body
40296 * @param {Boolean} closable (optional) True to create a close icon on the tab
40297 * @return {Roo.TabPanelItem} The created TabPanelItem
40299 addTab : function(id, text, content, closable, tpl)
40301 var item = new Roo.bootstrap.panel.TabItem({
40305 closable : closable,
40308 this.addTabItem(item);
40310 item.setContent(content);
40316 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40317 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40318 * @return {Roo.TabPanelItem}
40320 getTab : function(id){
40321 return this.items[id];
40325 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40326 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40328 hideTab : function(id){
40329 var t = this.items[id];
40332 this.hiddenCount++;
40333 this.autoSizeTabs();
40338 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40339 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40341 unhideTab : function(id){
40342 var t = this.items[id];
40344 t.setHidden(false);
40345 this.hiddenCount--;
40346 this.autoSizeTabs();
40351 * Adds an existing {@link Roo.TabPanelItem}.
40352 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40354 addTabItem : function(item)
40356 this.items[item.id] = item;
40357 this.items.push(item);
40358 this.autoSizeTabs();
40359 // if(this.resizeTabs){
40360 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40361 // this.autoSizeTabs();
40363 // item.autoSize();
40368 * Removes a {@link Roo.TabPanelItem}.
40369 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40371 removeTab : function(id){
40372 var items = this.items;
40373 var tab = items[id];
40374 if(!tab) { return; }
40375 var index = items.indexOf(tab);
40376 if(this.active == tab && items.length > 1){
40377 var newTab = this.getNextAvailable(index);
40382 this.stripEl.dom.removeChild(tab.pnode.dom);
40383 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40384 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40386 items.splice(index, 1);
40387 delete this.items[tab.id];
40388 tab.fireEvent("close", tab);
40389 tab.purgeListeners();
40390 this.autoSizeTabs();
40393 getNextAvailable : function(start){
40394 var items = this.items;
40396 // look for a next tab that will slide over to
40397 // replace the one being removed
40398 while(index < items.length){
40399 var item = items[++index];
40400 if(item && !item.isHidden()){
40404 // if one isn't found select the previous tab (on the left)
40407 var item = items[--index];
40408 if(item && !item.isHidden()){
40416 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40417 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40419 disableTab : function(id){
40420 var tab = this.items[id];
40421 if(tab && this.active != tab){
40427 * Enables a {@link Roo.TabPanelItem} that is disabled.
40428 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40430 enableTab : function(id){
40431 var tab = this.items[id];
40436 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40437 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40438 * @return {Roo.TabPanelItem} The TabPanelItem.
40440 activate : function(id)
40442 //Roo.log('activite:' + id);
40444 var tab = this.items[id];
40448 if(tab == this.active || tab.disabled){
40452 this.fireEvent("beforetabchange", this, e, tab);
40453 if(e.cancel !== true && !tab.disabled){
40455 this.active.hide();
40457 this.active = this.items[id];
40458 this.active.show();
40459 this.fireEvent("tabchange", this, this.active);
40465 * Gets the active {@link Roo.TabPanelItem}.
40466 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40468 getActiveTab : function(){
40469 return this.active;
40473 * Updates the tab body element to fit the height of the container element
40474 * for overflow scrolling
40475 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40477 syncHeight : function(targetHeight){
40478 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40479 var bm = this.bodyEl.getMargins();
40480 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40481 this.bodyEl.setHeight(newHeight);
40485 onResize : function(){
40486 if(this.monitorResize){
40487 this.autoSizeTabs();
40492 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40494 beginUpdate : function(){
40495 this.updating = true;
40499 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40501 endUpdate : function(){
40502 this.updating = false;
40503 this.autoSizeTabs();
40507 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40509 autoSizeTabs : function()
40511 var count = this.items.length;
40512 var vcount = count - this.hiddenCount;
40515 this.stripEl.hide();
40517 this.stripEl.show();
40520 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40525 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40526 var availWidth = Math.floor(w / vcount);
40527 var b = this.stripBody;
40528 if(b.getWidth() > w){
40529 var tabs = this.items;
40530 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40531 if(availWidth < this.minTabWidth){
40532 /*if(!this.sleft){ // incomplete scrolling code
40533 this.createScrollButtons();
40536 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40539 if(this.currentTabWidth < this.preferredTabWidth){
40540 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40546 * Returns the number of tabs in this TabPanel.
40549 getCount : function(){
40550 return this.items.length;
40554 * Resizes all the tabs to the passed width
40555 * @param {Number} The new width
40557 setTabWidth : function(width){
40558 this.currentTabWidth = width;
40559 for(var i = 0, len = this.items.length; i < len; i++) {
40560 if(!this.items[i].isHidden()) {
40561 this.items[i].setWidth(width);
40567 * Destroys this TabPanel
40568 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40570 destroy : function(removeEl){
40571 Roo.EventManager.removeResizeListener(this.onResize, this);
40572 for(var i = 0, len = this.items.length; i < len; i++){
40573 this.items[i].purgeListeners();
40575 if(removeEl === true){
40576 this.el.update("");
40581 createStrip : function(container)
40583 var strip = document.createElement("nav");
40584 strip.className = Roo.bootstrap.version == 4 ?
40585 "navbar-light bg-light" :
40586 "navbar navbar-default"; //"x-tabs-wrap";
40587 container.appendChild(strip);
40591 createStripList : function(strip)
40593 // div wrapper for retard IE
40594 // returns the "tr" element.
40595 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40596 //'<div class="x-tabs-strip-wrap">'+
40597 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40598 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40599 return strip.firstChild; //.firstChild.firstChild.firstChild;
40601 createBody : function(container)
40603 var body = document.createElement("div");
40604 Roo.id(body, "tab-body");
40605 //Roo.fly(body).addClass("x-tabs-body");
40606 Roo.fly(body).addClass("tab-content");
40607 container.appendChild(body);
40610 createItemBody :function(bodyEl, id){
40611 var body = Roo.getDom(id);
40613 body = document.createElement("div");
40616 //Roo.fly(body).addClass("x-tabs-item-body");
40617 Roo.fly(body).addClass("tab-pane");
40618 bodyEl.insertBefore(body, bodyEl.firstChild);
40622 createStripElements : function(stripEl, text, closable, tpl)
40624 var td = document.createElement("li"); // was td..
40625 td.className = 'nav-item';
40627 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40630 stripEl.appendChild(td);
40632 td.className = "x-tabs-closable";
40633 if(!this.closeTpl){
40634 this.closeTpl = new Roo.Template(
40635 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40636 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40637 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40640 var el = this.closeTpl.overwrite(td, {"text": text});
40641 var close = el.getElementsByTagName("div")[0];
40642 var inner = el.getElementsByTagName("em")[0];
40643 return {"el": el, "close": close, "inner": inner};
40646 // not sure what this is..
40647 // if(!this.tabTpl){
40648 //this.tabTpl = new Roo.Template(
40649 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40650 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40652 // this.tabTpl = new Roo.Template(
40653 // '<a href="#">' +
40654 // '<span unselectable="on"' +
40655 // (this.disableTooltips ? '' : ' title="{text}"') +
40656 // ' >{text}</span></a>'
40662 var template = tpl || this.tabTpl || false;
40665 template = new Roo.Template(
40666 Roo.bootstrap.version == 4 ?
40668 '<a class="nav-link" href="#" unselectable="on"' +
40669 (this.disableTooltips ? '' : ' title="{text}"') +
40672 '<a class="nav-link" href="#">' +
40673 '<span unselectable="on"' +
40674 (this.disableTooltips ? '' : ' title="{text}"') +
40675 ' >{text}</span></a>'
40680 switch (typeof(template)) {
40684 template = new Roo.Template(template);
40690 var el = template.overwrite(td, {"text": text});
40692 var inner = el.getElementsByTagName("span")[0];
40694 return {"el": el, "inner": inner};
40702 * @class Roo.TabPanelItem
40703 * @extends Roo.util.Observable
40704 * Represents an individual item (tab plus body) in a TabPanel.
40705 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40706 * @param {String} id The id of this TabPanelItem
40707 * @param {String} text The text for the tab of this TabPanelItem
40708 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40710 Roo.bootstrap.panel.TabItem = function(config){
40712 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40713 * @type Roo.TabPanel
40715 this.tabPanel = config.panel;
40717 * The id for this TabPanelItem
40720 this.id = config.id;
40722 this.disabled = false;
40724 this.text = config.text;
40726 this.loaded = false;
40727 this.closable = config.closable;
40730 * The body element for this TabPanelItem.
40731 * @type Roo.Element
40733 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40734 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40735 this.bodyEl.setStyle("display", "block");
40736 this.bodyEl.setStyle("zoom", "1");
40737 //this.hideAction();
40739 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40741 this.el = Roo.get(els.el);
40742 this.inner = Roo.get(els.inner, true);
40743 this.textEl = Roo.bootstrap.version == 4 ?
40744 this.el : Roo.get(this.el.dom.firstChild, true);
40746 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40747 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40750 // this.el.on("mousedown", this.onTabMouseDown, this);
40751 this.el.on("click", this.onTabClick, this);
40753 if(config.closable){
40754 var c = Roo.get(els.close, true);
40755 c.dom.title = this.closeText;
40756 c.addClassOnOver("close-over");
40757 c.on("click", this.closeClick, this);
40763 * Fires when this tab becomes the active tab.
40764 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40765 * @param {Roo.TabPanelItem} this
40769 * @event beforeclose
40770 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40771 * @param {Roo.TabPanelItem} this
40772 * @param {Object} e Set cancel to true on this object to cancel the close.
40774 "beforeclose": true,
40777 * Fires when this tab is closed.
40778 * @param {Roo.TabPanelItem} this
40782 * @event deactivate
40783 * Fires when this tab is no longer the active tab.
40784 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40785 * @param {Roo.TabPanelItem} this
40787 "deactivate" : true
40789 this.hidden = false;
40791 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40794 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40796 purgeListeners : function(){
40797 Roo.util.Observable.prototype.purgeListeners.call(this);
40798 this.el.removeAllListeners();
40801 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40804 this.status_node.addClass("active");
40807 this.tabPanel.stripWrap.repaint();
40809 this.fireEvent("activate", this.tabPanel, this);
40813 * Returns true if this tab is the active tab.
40814 * @return {Boolean}
40816 isActive : function(){
40817 return this.tabPanel.getActiveTab() == this;
40821 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40824 this.status_node.removeClass("active");
40826 this.fireEvent("deactivate", this.tabPanel, this);
40829 hideAction : function(){
40830 this.bodyEl.hide();
40831 this.bodyEl.setStyle("position", "absolute");
40832 this.bodyEl.setLeft("-20000px");
40833 this.bodyEl.setTop("-20000px");
40836 showAction : function(){
40837 this.bodyEl.setStyle("position", "relative");
40838 this.bodyEl.setTop("");
40839 this.bodyEl.setLeft("");
40840 this.bodyEl.show();
40844 * Set the tooltip for the tab.
40845 * @param {String} tooltip The tab's tooltip
40847 setTooltip : function(text){
40848 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40849 this.textEl.dom.qtip = text;
40850 this.textEl.dom.removeAttribute('title');
40852 this.textEl.dom.title = text;
40856 onTabClick : function(e){
40857 e.preventDefault();
40858 this.tabPanel.activate(this.id);
40861 onTabMouseDown : function(e){
40862 e.preventDefault();
40863 this.tabPanel.activate(this.id);
40866 getWidth : function(){
40867 return this.inner.getWidth();
40870 setWidth : function(width){
40871 var iwidth = width - this.linode.getPadding("lr");
40872 this.inner.setWidth(iwidth);
40873 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40874 this.linode.setWidth(width);
40878 * Show or hide the tab
40879 * @param {Boolean} hidden True to hide or false to show.
40881 setHidden : function(hidden){
40882 this.hidden = hidden;
40883 this.linode.setStyle("display", hidden ? "none" : "");
40887 * Returns true if this tab is "hidden"
40888 * @return {Boolean}
40890 isHidden : function(){
40891 return this.hidden;
40895 * Returns the text for this tab
40898 getText : function(){
40902 autoSize : function(){
40903 //this.el.beginMeasure();
40904 this.textEl.setWidth(1);
40906 * #2804 [new] Tabs in Roojs
40907 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40909 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40910 //this.el.endMeasure();
40914 * Sets the text for the tab (Note: this also sets the tooltip text)
40915 * @param {String} text The tab's text and tooltip
40917 setText : function(text){
40919 this.textEl.update(text);
40920 this.setTooltip(text);
40921 //if(!this.tabPanel.resizeTabs){
40922 // this.autoSize();
40926 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40928 activate : function(){
40929 this.tabPanel.activate(this.id);
40933 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40935 disable : function(){
40936 if(this.tabPanel.active != this){
40937 this.disabled = true;
40938 this.status_node.addClass("disabled");
40943 * Enables this TabPanelItem if it was previously disabled.
40945 enable : function(){
40946 this.disabled = false;
40947 this.status_node.removeClass("disabled");
40951 * Sets the content for this TabPanelItem.
40952 * @param {String} content The content
40953 * @param {Boolean} loadScripts true to look for and load scripts
40955 setContent : function(content, loadScripts){
40956 this.bodyEl.update(content, loadScripts);
40960 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40961 * @return {Roo.UpdateManager} The UpdateManager
40963 getUpdateManager : function(){
40964 return this.bodyEl.getUpdateManager();
40968 * Set a URL to be used to load the content for this TabPanelItem.
40969 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40970 * @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)
40971 * @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)
40972 * @return {Roo.UpdateManager} The UpdateManager
40974 setUrl : function(url, params, loadOnce){
40975 if(this.refreshDelegate){
40976 this.un('activate', this.refreshDelegate);
40978 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40979 this.on("activate", this.refreshDelegate);
40980 return this.bodyEl.getUpdateManager();
40984 _handleRefresh : function(url, params, loadOnce){
40985 if(!loadOnce || !this.loaded){
40986 var updater = this.bodyEl.getUpdateManager();
40987 updater.update(url, params, this._setLoaded.createDelegate(this));
40992 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40993 * Will fail silently if the setUrl method has not been called.
40994 * This does not activate the panel, just updates its content.
40996 refresh : function(){
40997 if(this.refreshDelegate){
40998 this.loaded = false;
40999 this.refreshDelegate();
41004 _setLoaded : function(){
41005 this.loaded = true;
41009 closeClick : function(e){
41012 this.fireEvent("beforeclose", this, o);
41013 if(o.cancel !== true){
41014 this.tabPanel.removeTab(this.id);
41018 * The text displayed in the tooltip for the close icon.
41021 closeText : "Close this tab"
41024 * This script refer to:
41025 * Title: International Telephone Input
41026 * Author: Jack O'Connor
41027 * Code version: v12.1.12
41028 * Availability: https://github.com/jackocnr/intl-tel-input.git
41031 Roo.bootstrap.PhoneInputData = function() {
41034 "Afghanistan (افغانستان)",
41039 "Albania (Shqipëri)",
41044 "Algeria (الجزائر)",
41069 "Antigua and Barbuda",
41079 "Armenia (Հայաստան)",
41095 "Austria (Österreich)",
41100 "Azerbaijan (Azərbaycan)",
41110 "Bahrain (البحرين)",
41115 "Bangladesh (বাংলাদেশ)",
41125 "Belarus (Беларусь)",
41130 "Belgium (België)",
41160 "Bosnia and Herzegovina (Босна и Херцеговина)",
41175 "British Indian Ocean Territory",
41180 "British Virgin Islands",
41190 "Bulgaria (България)",
41200 "Burundi (Uburundi)",
41205 "Cambodia (កម្ពុជា)",
41210 "Cameroon (Cameroun)",
41219 ["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"]
41222 "Cape Verde (Kabu Verdi)",
41227 "Caribbean Netherlands",
41238 "Central African Republic (République centrafricaine)",
41258 "Christmas Island",
41264 "Cocos (Keeling) Islands",
41275 "Comoros (جزر القمر)",
41280 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41285 "Congo (Republic) (Congo-Brazzaville)",
41305 "Croatia (Hrvatska)",
41326 "Czech Republic (Česká republika)",
41331 "Denmark (Danmark)",
41346 "Dominican Republic (República Dominicana)",
41350 ["809", "829", "849"]
41368 "Equatorial Guinea (Guinea Ecuatorial)",
41388 "Falkland Islands (Islas Malvinas)",
41393 "Faroe Islands (Føroyar)",
41414 "French Guiana (Guyane française)",
41419 "French Polynesia (Polynésie française)",
41434 "Georgia (საქართველო)",
41439 "Germany (Deutschland)",
41459 "Greenland (Kalaallit Nunaat)",
41496 "Guinea-Bissau (Guiné Bissau)",
41521 "Hungary (Magyarország)",
41526 "Iceland (Ísland)",
41546 "Iraq (العراق)",
41562 "Israel (ישראל)",
41589 "Jordan (الأردن)",
41594 "Kazakhstan (Казахстан)",
41615 "Kuwait (الكويت)",
41620 "Kyrgyzstan (Кыргызстан)",
41630 "Latvia (Latvija)",
41635 "Lebanon (لبنان)",
41650 "Libya (ليبيا)",
41660 "Lithuania (Lietuva)",
41675 "Macedonia (FYROM) (Македонија)",
41680 "Madagascar (Madagasikara)",
41710 "Marshall Islands",
41720 "Mauritania (موريتانيا)",
41725 "Mauritius (Moris)",
41746 "Moldova (Republica Moldova)",
41756 "Mongolia (Монгол)",
41761 "Montenegro (Crna Gora)",
41771 "Morocco (المغرب)",
41777 "Mozambique (Moçambique)",
41782 "Myanmar (Burma) (မြန်မာ)",
41787 "Namibia (Namibië)",
41802 "Netherlands (Nederland)",
41807 "New Caledonia (Nouvelle-Calédonie)",
41842 "North Korea (조선 민주주의 인민 공화국)",
41847 "Northern Mariana Islands",
41863 "Pakistan (پاکستان)",
41873 "Palestine (فلسطين)",
41883 "Papua New Guinea",
41925 "Réunion (La Réunion)",
41931 "Romania (România)",
41947 "Saint Barthélemy",
41958 "Saint Kitts and Nevis",
41968 "Saint Martin (Saint-Martin (partie française))",
41974 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41979 "Saint Vincent and the Grenadines",
41994 "São Tomé and Príncipe (São Tomé e Príncipe)",
41999 "Saudi Arabia (المملكة العربية السعودية)",
42004 "Senegal (Sénégal)",
42034 "Slovakia (Slovensko)",
42039 "Slovenia (Slovenija)",
42049 "Somalia (Soomaaliya)",
42059 "South Korea (대한민국)",
42064 "South Sudan (جنوب السودان)",
42074 "Sri Lanka (ශ්රී ලංකාව)",
42079 "Sudan (السودان)",
42089 "Svalbard and Jan Mayen",
42100 "Sweden (Sverige)",
42105 "Switzerland (Schweiz)",
42110 "Syria (سوريا)",
42155 "Trinidad and Tobago",
42160 "Tunisia (تونس)",
42165 "Turkey (Türkiye)",
42175 "Turks and Caicos Islands",
42185 "U.S. Virgin Islands",
42195 "Ukraine (Україна)",
42200 "United Arab Emirates (الإمارات العربية المتحدة)",
42222 "Uzbekistan (Oʻzbekiston)",
42232 "Vatican City (Città del Vaticano)",
42243 "Vietnam (Việt Nam)",
42248 "Wallis and Futuna (Wallis-et-Futuna)",
42253 "Western Sahara (الصحراء الغربية)",
42259 "Yemen (اليمن)",
42283 * This script refer to:
42284 * Title: International Telephone Input
42285 * Author: Jack O'Connor
42286 * Code version: v12.1.12
42287 * Availability: https://github.com/jackocnr/intl-tel-input.git
42291 * @class Roo.bootstrap.PhoneInput
42292 * @extends Roo.bootstrap.TriggerField
42293 * An input with International dial-code selection
42295 * @cfg {String} defaultDialCode default '+852'
42296 * @cfg {Array} preferedCountries default []
42299 * Create a new PhoneInput.
42300 * @param {Object} config Configuration options
42303 Roo.bootstrap.PhoneInput = function(config) {
42304 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42307 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42309 listWidth: undefined,
42311 selectedClass: 'active',
42313 invalidClass : "has-warning",
42315 validClass: 'has-success',
42317 allowed: '0123456789',
42322 * @cfg {String} defaultDialCode The default dial code when initializing the input
42324 defaultDialCode: '+852',
42327 * @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
42329 preferedCountries: false,
42331 getAutoCreate : function()
42333 var data = Roo.bootstrap.PhoneInputData();
42334 var align = this.labelAlign || this.parentLabelAlign();
42337 this.allCountries = [];
42338 this.dialCodeMapping = [];
42340 for (var i = 0; i < data.length; i++) {
42342 this.allCountries[i] = {
42346 priority: c[3] || 0,
42347 areaCodes: c[4] || null
42349 this.dialCodeMapping[c[2]] = {
42352 priority: c[3] || 0,
42353 areaCodes: c[4] || null
42365 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42366 maxlength: this.max_length,
42367 cls : 'form-control tel-input',
42368 autocomplete: 'new-password'
42371 var hiddenInput = {
42374 cls: 'hidden-tel-input'
42378 hiddenInput.name = this.name;
42381 if (this.disabled) {
42382 input.disabled = true;
42385 var flag_container = {
42402 cls: this.hasFeedback ? 'has-feedback' : '',
42408 cls: 'dial-code-holder',
42415 cls: 'roo-select2-container input-group',
42422 if (this.fieldLabel.length) {
42425 tooltip: 'This field is required'
42431 cls: 'control-label',
42437 html: this.fieldLabel
42440 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42446 if(this.indicatorpos == 'right') {
42447 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42454 if(align == 'left') {
42462 if(this.labelWidth > 12){
42463 label.style = "width: " + this.labelWidth + 'px';
42465 if(this.labelWidth < 13 && this.labelmd == 0){
42466 this.labelmd = this.labelWidth;
42468 if(this.labellg > 0){
42469 label.cls += ' col-lg-' + this.labellg;
42470 input.cls += ' col-lg-' + (12 - this.labellg);
42472 if(this.labelmd > 0){
42473 label.cls += ' col-md-' + this.labelmd;
42474 container.cls += ' col-md-' + (12 - this.labelmd);
42476 if(this.labelsm > 0){
42477 label.cls += ' col-sm-' + this.labelsm;
42478 container.cls += ' col-sm-' + (12 - this.labelsm);
42480 if(this.labelxs > 0){
42481 label.cls += ' col-xs-' + this.labelxs;
42482 container.cls += ' col-xs-' + (12 - this.labelxs);
42492 var settings = this;
42494 ['xs','sm','md','lg'].map(function(size){
42495 if (settings[size]) {
42496 cfg.cls += ' col-' + size + '-' + settings[size];
42500 this.store = new Roo.data.Store({
42501 proxy : new Roo.data.MemoryProxy({}),
42502 reader : new Roo.data.JsonReader({
42513 'name' : 'dialCode',
42517 'name' : 'priority',
42521 'name' : 'areaCodes',
42528 if(!this.preferedCountries) {
42529 this.preferedCountries = [
42536 var p = this.preferedCountries.reverse();
42539 for (var i = 0; i < p.length; i++) {
42540 for (var j = 0; j < this.allCountries.length; j++) {
42541 if(this.allCountries[j].iso2 == p[i]) {
42542 var t = this.allCountries[j];
42543 this.allCountries.splice(j,1);
42544 this.allCountries.unshift(t);
42550 this.store.proxy.data = {
42552 data: this.allCountries
42558 initEvents : function()
42561 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42563 this.indicator = this.indicatorEl();
42564 this.flag = this.flagEl();
42565 this.dialCodeHolder = this.dialCodeHolderEl();
42567 this.trigger = this.el.select('div.flag-box',true).first();
42568 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42573 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42574 _this.list.setWidth(lw);
42577 this.list.on('mouseover', this.onViewOver, this);
42578 this.list.on('mousemove', this.onViewMove, this);
42579 this.inputEl().on("keyup", this.onKeyUp, this);
42580 this.inputEl().on("keypress", this.onKeyPress, this);
42582 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42584 this.view = new Roo.View(this.list, this.tpl, {
42585 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42588 this.view.on('click', this.onViewClick, this);
42589 this.setValue(this.defaultDialCode);
42592 onTriggerClick : function(e)
42594 Roo.log('trigger click');
42599 if(this.isExpanded()){
42601 this.hasFocus = false;
42603 this.store.load({});
42604 this.hasFocus = true;
42609 isExpanded : function()
42611 return this.list.isVisible();
42614 collapse : function()
42616 if(!this.isExpanded()){
42620 Roo.get(document).un('mousedown', this.collapseIf, this);
42621 Roo.get(document).un('mousewheel', this.collapseIf, this);
42622 this.fireEvent('collapse', this);
42626 expand : function()
42630 if(this.isExpanded() || !this.hasFocus){
42634 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42635 this.list.setWidth(lw);
42638 this.restrictHeight();
42640 Roo.get(document).on('mousedown', this.collapseIf, this);
42641 Roo.get(document).on('mousewheel', this.collapseIf, this);
42643 this.fireEvent('expand', this);
42646 restrictHeight : function()
42648 this.list.alignTo(this.inputEl(), this.listAlign);
42649 this.list.alignTo(this.inputEl(), this.listAlign);
42652 onViewOver : function(e, t)
42654 if(this.inKeyMode){
42657 var item = this.view.findItemFromChild(t);
42660 var index = this.view.indexOf(item);
42661 this.select(index, false);
42666 onViewClick : function(view, doFocus, el, e)
42668 var index = this.view.getSelectedIndexes()[0];
42670 var r = this.store.getAt(index);
42673 this.onSelect(r, index);
42675 if(doFocus !== false && !this.blockFocus){
42676 this.inputEl().focus();
42680 onViewMove : function(e, t)
42682 this.inKeyMode = false;
42685 select : function(index, scrollIntoView)
42687 this.selectedIndex = index;
42688 this.view.select(index);
42689 if(scrollIntoView !== false){
42690 var el = this.view.getNode(index);
42692 this.list.scrollChildIntoView(el, false);
42697 createList : function()
42699 this.list = Roo.get(document.body).createChild({
42701 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42702 style: 'display:none'
42705 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42708 collapseIf : function(e)
42710 var in_combo = e.within(this.el);
42711 var in_list = e.within(this.list);
42712 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42714 if (in_combo || in_list || is_list) {
42720 onSelect : function(record, index)
42722 if(this.fireEvent('beforeselect', this, record, index) !== false){
42724 this.setFlagClass(record.data.iso2);
42725 this.setDialCode(record.data.dialCode);
42726 this.hasFocus = false;
42728 this.fireEvent('select', this, record, index);
42732 flagEl : function()
42734 var flag = this.el.select('div.flag',true).first();
42741 dialCodeHolderEl : function()
42743 var d = this.el.select('input.dial-code-holder',true).first();
42750 setDialCode : function(v)
42752 this.dialCodeHolder.dom.value = '+'+v;
42755 setFlagClass : function(n)
42757 this.flag.dom.className = 'flag '+n;
42760 getValue : function()
42762 var v = this.inputEl().getValue();
42763 if(this.dialCodeHolder) {
42764 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42769 setValue : function(v)
42771 var d = this.getDialCode(v);
42773 //invalid dial code
42774 if(v.length == 0 || !d || d.length == 0) {
42776 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42777 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42783 this.setFlagClass(this.dialCodeMapping[d].iso2);
42784 this.setDialCode(d);
42785 this.inputEl().dom.value = v.replace('+'+d,'');
42786 this.hiddenEl().dom.value = this.getValue();
42791 getDialCode : function(v)
42795 if (v.length == 0) {
42796 return this.dialCodeHolder.dom.value;
42800 if (v.charAt(0) != "+") {
42803 var numericChars = "";
42804 for (var i = 1; i < v.length; i++) {
42805 var c = v.charAt(i);
42808 if (this.dialCodeMapping[numericChars]) {
42809 dialCode = v.substr(1, i);
42811 if (numericChars.length == 4) {
42821 this.setValue(this.defaultDialCode);
42825 hiddenEl : function()
42827 return this.el.select('input.hidden-tel-input',true).first();
42830 // after setting val
42831 onKeyUp : function(e){
42832 this.setValue(this.getValue());
42835 onKeyPress : function(e){
42836 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42843 * @class Roo.bootstrap.MoneyField
42844 * @extends Roo.bootstrap.ComboBox
42845 * Bootstrap MoneyField class
42848 * Create a new MoneyField.
42849 * @param {Object} config Configuration options
42852 Roo.bootstrap.MoneyField = function(config) {
42854 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42858 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42861 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42863 allowDecimals : true,
42865 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42867 decimalSeparator : ".",
42869 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42871 decimalPrecision : 0,
42873 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42875 allowNegative : true,
42877 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42881 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42883 minValue : Number.NEGATIVE_INFINITY,
42885 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42887 maxValue : Number.MAX_VALUE,
42889 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42891 minText : "The minimum value for this field is {0}",
42893 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42895 maxText : "The maximum value for this field is {0}",
42897 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42898 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42900 nanText : "{0} is not a valid number",
42902 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42906 * @cfg {String} defaults currency of the MoneyField
42907 * value should be in lkey
42909 defaultCurrency : false,
42911 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42913 thousandsDelimiter : false,
42915 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42926 getAutoCreate : function()
42928 var align = this.labelAlign || this.parentLabelAlign();
42940 cls : 'form-control roo-money-amount-input',
42941 autocomplete: 'new-password'
42944 var hiddenInput = {
42948 cls: 'hidden-number-input'
42951 if(this.max_length) {
42952 input.maxlength = this.max_length;
42956 hiddenInput.name = this.name;
42959 if (this.disabled) {
42960 input.disabled = true;
42963 var clg = 12 - this.inputlg;
42964 var cmd = 12 - this.inputmd;
42965 var csm = 12 - this.inputsm;
42966 var cxs = 12 - this.inputxs;
42970 cls : 'row roo-money-field',
42974 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42978 cls: 'roo-select2-container input-group',
42982 cls : 'form-control roo-money-currency-input',
42983 autocomplete: 'new-password',
42985 name : this.currencyName
42989 cls : 'input-group-addon',
43003 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43007 cls: this.hasFeedback ? 'has-feedback' : '',
43018 if (this.fieldLabel.length) {
43021 tooltip: 'This field is required'
43027 cls: 'control-label',
43033 html: this.fieldLabel
43036 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43042 if(this.indicatorpos == 'right') {
43043 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43050 if(align == 'left') {
43058 if(this.labelWidth > 12){
43059 label.style = "width: " + this.labelWidth + 'px';
43061 if(this.labelWidth < 13 && this.labelmd == 0){
43062 this.labelmd = this.labelWidth;
43064 if(this.labellg > 0){
43065 label.cls += ' col-lg-' + this.labellg;
43066 input.cls += ' col-lg-' + (12 - this.labellg);
43068 if(this.labelmd > 0){
43069 label.cls += ' col-md-' + this.labelmd;
43070 container.cls += ' col-md-' + (12 - this.labelmd);
43072 if(this.labelsm > 0){
43073 label.cls += ' col-sm-' + this.labelsm;
43074 container.cls += ' col-sm-' + (12 - this.labelsm);
43076 if(this.labelxs > 0){
43077 label.cls += ' col-xs-' + this.labelxs;
43078 container.cls += ' col-xs-' + (12 - this.labelxs);
43089 var settings = this;
43091 ['xs','sm','md','lg'].map(function(size){
43092 if (settings[size]) {
43093 cfg.cls += ' col-' + size + '-' + settings[size];
43100 initEvents : function()
43102 this.indicator = this.indicatorEl();
43104 this.initCurrencyEvent();
43106 this.initNumberEvent();
43109 initCurrencyEvent : function()
43112 throw "can not find store for combo";
43115 this.store = Roo.factory(this.store, Roo.data);
43116 this.store.parent = this;
43120 this.triggerEl = this.el.select('.input-group-addon', true).first();
43122 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43127 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43128 _this.list.setWidth(lw);
43131 this.list.on('mouseover', this.onViewOver, this);
43132 this.list.on('mousemove', this.onViewMove, this);
43133 this.list.on('scroll', this.onViewScroll, this);
43136 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43139 this.view = new Roo.View(this.list, this.tpl, {
43140 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43143 this.view.on('click', this.onViewClick, this);
43145 this.store.on('beforeload', this.onBeforeLoad, this);
43146 this.store.on('load', this.onLoad, this);
43147 this.store.on('loadexception', this.onLoadException, this);
43149 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43150 "up" : function(e){
43151 this.inKeyMode = true;
43155 "down" : function(e){
43156 if(!this.isExpanded()){
43157 this.onTriggerClick();
43159 this.inKeyMode = true;
43164 "enter" : function(e){
43167 if(this.fireEvent("specialkey", this, e)){
43168 this.onViewClick(false);
43174 "esc" : function(e){
43178 "tab" : function(e){
43181 if(this.fireEvent("specialkey", this, e)){
43182 this.onViewClick(false);
43190 doRelay : function(foo, bar, hname){
43191 if(hname == 'down' || this.scope.isExpanded()){
43192 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43200 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43204 initNumberEvent : function(e)
43206 this.inputEl().on("keydown" , this.fireKey, this);
43207 this.inputEl().on("focus", this.onFocus, this);
43208 this.inputEl().on("blur", this.onBlur, this);
43210 this.inputEl().relayEvent('keyup', this);
43212 if(this.indicator){
43213 this.indicator.addClass('invisible');
43216 this.originalValue = this.getValue();
43218 if(this.validationEvent == 'keyup'){
43219 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43220 this.inputEl().on('keyup', this.filterValidation, this);
43222 else if(this.validationEvent !== false){
43223 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43226 if(this.selectOnFocus){
43227 this.on("focus", this.preFocus, this);
43230 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43231 this.inputEl().on("keypress", this.filterKeys, this);
43233 this.inputEl().relayEvent('keypress', this);
43236 var allowed = "0123456789";
43238 if(this.allowDecimals){
43239 allowed += this.decimalSeparator;
43242 if(this.allowNegative){
43246 if(this.thousandsDelimiter) {
43250 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43252 var keyPress = function(e){
43254 var k = e.getKey();
43256 var c = e.getCharCode();
43259 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43260 allowed.indexOf(String.fromCharCode(c)) === -1
43266 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43270 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43275 this.inputEl().on("keypress", keyPress, this);
43279 onTriggerClick : function(e)
43286 this.loadNext = false;
43288 if(this.isExpanded()){
43293 this.hasFocus = true;
43295 if(this.triggerAction == 'all') {
43296 this.doQuery(this.allQuery, true);
43300 this.doQuery(this.getRawValue());
43303 getCurrency : function()
43305 var v = this.currencyEl().getValue();
43310 restrictHeight : function()
43312 this.list.alignTo(this.currencyEl(), this.listAlign);
43313 this.list.alignTo(this.currencyEl(), this.listAlign);
43316 onViewClick : function(view, doFocus, el, e)
43318 var index = this.view.getSelectedIndexes()[0];
43320 var r = this.store.getAt(index);
43323 this.onSelect(r, index);
43327 onSelect : function(record, index){
43329 if(this.fireEvent('beforeselect', this, record, index) !== false){
43331 this.setFromCurrencyData(index > -1 ? record.data : false);
43335 this.fireEvent('select', this, record, index);
43339 setFromCurrencyData : function(o)
43343 this.lastCurrency = o;
43345 if (this.currencyField) {
43346 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43348 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43351 this.lastSelectionText = currency;
43353 //setting default currency
43354 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43355 this.setCurrency(this.defaultCurrency);
43359 this.setCurrency(currency);
43362 setFromData : function(o)
43366 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43368 this.setFromCurrencyData(c);
43373 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43375 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43378 this.setValue(value);
43382 setCurrency : function(v)
43384 this.currencyValue = v;
43387 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43392 setValue : function(v)
43394 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43400 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43402 this.inputEl().dom.value = (v == '') ? '' :
43403 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43405 if(!this.allowZero && v === '0') {
43406 this.hiddenEl().dom.value = '';
43407 this.inputEl().dom.value = '';
43414 getRawValue : function()
43416 var v = this.inputEl().getValue();
43421 getValue : function()
43423 return this.fixPrecision(this.parseValue(this.getRawValue()));
43426 parseValue : function(value)
43428 if(this.thousandsDelimiter) {
43430 r = new RegExp(",", "g");
43431 value = value.replace(r, "");
43434 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43435 return isNaN(value) ? '' : value;
43439 fixPrecision : function(value)
43441 if(this.thousandsDelimiter) {
43443 r = new RegExp(",", "g");
43444 value = value.replace(r, "");
43447 var nan = isNaN(value);
43449 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43450 return nan ? '' : value;
43452 return parseFloat(value).toFixed(this.decimalPrecision);
43455 decimalPrecisionFcn : function(v)
43457 return Math.floor(v);
43460 validateValue : function(value)
43462 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43466 var num = this.parseValue(value);
43469 this.markInvalid(String.format(this.nanText, value));
43473 if(num < this.minValue){
43474 this.markInvalid(String.format(this.minText, this.minValue));
43478 if(num > this.maxValue){
43479 this.markInvalid(String.format(this.maxText, this.maxValue));
43486 validate : function()
43488 if(this.disabled || this.allowBlank){
43493 var currency = this.getCurrency();
43495 if(this.validateValue(this.getRawValue()) && currency.length){
43500 this.markInvalid();
43504 getName: function()
43509 beforeBlur : function()
43515 var v = this.parseValue(this.getRawValue());
43522 onBlur : function()
43526 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43527 //this.el.removeClass(this.focusClass);
43530 this.hasFocus = false;
43532 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43536 var v = this.getValue();
43538 if(String(v) !== String(this.startValue)){
43539 this.fireEvent('change', this, v, this.startValue);
43542 this.fireEvent("blur", this);
43545 inputEl : function()
43547 return this.el.select('.roo-money-amount-input', true).first();
43550 currencyEl : function()
43552 return this.el.select('.roo-money-currency-input', true).first();
43555 hiddenEl : function()
43557 return this.el.select('input.hidden-number-input',true).first();
43561 * @class Roo.bootstrap.BezierSignature
43562 * @extends Roo.bootstrap.Component
43563 * Bootstrap BezierSignature class
43564 * This script refer to:
43565 * Title: Signature Pad
43567 * Availability: https://github.com/szimek/signature_pad
43570 * Create a new BezierSignature
43571 * @param {Object} config The config object
43574 Roo.bootstrap.BezierSignature = function(config){
43575 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43581 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43588 mouse_btn_down: true,
43591 * @cfg {int} canvas height
43593 canvas_height: '200px',
43596 * @cfg {float|function} Radius of a single dot.
43601 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43606 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43611 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43616 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43621 * @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.
43623 bg_color: 'rgba(0, 0, 0, 0)',
43626 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43628 dot_color: 'black',
43631 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43633 velocity_filter_weight: 0.7,
43636 * @cfg {function} Callback when stroke begin.
43641 * @cfg {function} Callback when stroke end.
43645 getAutoCreate : function()
43647 var cls = 'roo-signature column';
43650 cls += ' ' + this.cls;
43660 for(var i = 0; i < col_sizes.length; i++) {
43661 if(this[col_sizes[i]]) {
43662 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43672 cls: 'roo-signature-body',
43676 cls: 'roo-signature-body-canvas',
43677 height: this.canvas_height,
43678 width: this.canvas_width
43685 style: 'display: none'
43693 initEvents: function()
43695 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43697 var canvas = this.canvasEl();
43699 // mouse && touch event swapping...
43700 canvas.dom.style.touchAction = 'none';
43701 canvas.dom.style.msTouchAction = 'none';
43703 this.mouse_btn_down = false;
43704 canvas.on('mousedown', this._handleMouseDown, this);
43705 canvas.on('mousemove', this._handleMouseMove, this);
43706 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43708 if (window.PointerEvent) {
43709 canvas.on('pointerdown', this._handleMouseDown, this);
43710 canvas.on('pointermove', this._handleMouseMove, this);
43711 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43714 if ('ontouchstart' in window) {
43715 canvas.on('touchstart', this._handleTouchStart, this);
43716 canvas.on('touchmove', this._handleTouchMove, this);
43717 canvas.on('touchend', this._handleTouchEnd, this);
43720 Roo.EventManager.onWindowResize(this.resize, this, true);
43722 // file input event
43723 this.fileEl().on('change', this.uploadImage, this);
43730 resize: function(){
43732 var canvas = this.canvasEl().dom;
43733 var ctx = this.canvasElCtx();
43734 var img_data = false;
43736 if(canvas.width > 0) {
43737 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43739 // setting canvas width will clean img data
43742 var style = window.getComputedStyle ?
43743 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43745 var padding_left = parseInt(style.paddingLeft) || 0;
43746 var padding_right = parseInt(style.paddingRight) || 0;
43748 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43751 ctx.putImageData(img_data, 0, 0);
43755 _handleMouseDown: function(e)
43757 if (e.browserEvent.which === 1) {
43758 this.mouse_btn_down = true;
43759 this.strokeBegin(e);
43763 _handleMouseMove: function (e)
43765 if (this.mouse_btn_down) {
43766 this.strokeMoveUpdate(e);
43770 _handleMouseUp: function (e)
43772 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43773 this.mouse_btn_down = false;
43778 _handleTouchStart: function (e) {
43780 e.preventDefault();
43781 if (e.browserEvent.targetTouches.length === 1) {
43782 // var touch = e.browserEvent.changedTouches[0];
43783 // this.strokeBegin(touch);
43785 this.strokeBegin(e); // assume e catching the correct xy...
43789 _handleTouchMove: function (e) {
43790 e.preventDefault();
43791 // var touch = event.targetTouches[0];
43792 // _this._strokeMoveUpdate(touch);
43793 this.strokeMoveUpdate(e);
43796 _handleTouchEnd: function (e) {
43797 var wasCanvasTouched = e.target === this.canvasEl().dom;
43798 if (wasCanvasTouched) {
43799 e.preventDefault();
43800 // var touch = event.changedTouches[0];
43801 // _this._strokeEnd(touch);
43806 reset: function () {
43807 this._lastPoints = [];
43808 this._lastVelocity = 0;
43809 this._lastWidth = (this.min_width + this.max_width) / 2;
43810 this.canvasElCtx().fillStyle = this.dot_color;
43813 strokeMoveUpdate: function(e)
43815 this.strokeUpdate(e);
43817 if (this.throttle) {
43818 this.throttleStroke(this.strokeUpdate, this.throttle);
43821 this.strokeUpdate(e);
43825 strokeBegin: function(e)
43827 var newPointGroup = {
43828 color: this.dot_color,
43832 if (typeof this.onBegin === 'function') {
43836 this.curve_data.push(newPointGroup);
43838 this.strokeUpdate(e);
43841 strokeUpdate: function(e)
43843 var rect = this.canvasEl().dom.getBoundingClientRect();
43844 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43845 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43846 var lastPoints = lastPointGroup.points;
43847 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43848 var isLastPointTooClose = lastPoint
43849 ? point.distanceTo(lastPoint) <= this.min_distance
43851 var color = lastPointGroup.color;
43852 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43853 var curve = this.addPoint(point);
43855 this.drawDot({color: color, point: point});
43858 this.drawCurve({color: color, curve: curve});
43868 strokeEnd: function(e)
43870 this.strokeUpdate(e);
43871 if (typeof this.onEnd === 'function') {
43876 addPoint: function (point) {
43877 var _lastPoints = this._lastPoints;
43878 _lastPoints.push(point);
43879 if (_lastPoints.length > 2) {
43880 if (_lastPoints.length === 3) {
43881 _lastPoints.unshift(_lastPoints[0]);
43883 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43884 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43885 _lastPoints.shift();
43891 calculateCurveWidths: function (startPoint, endPoint) {
43892 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43893 (1 - this.velocity_filter_weight) * this._lastVelocity;
43895 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43898 start: this._lastWidth
43901 this._lastVelocity = velocity;
43902 this._lastWidth = newWidth;
43906 drawDot: function (_a) {
43907 var color = _a.color, point = _a.point;
43908 var ctx = this.canvasElCtx();
43909 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43911 this.drawCurveSegment(point.x, point.y, width);
43913 ctx.fillStyle = color;
43917 drawCurve: function (_a) {
43918 var color = _a.color, curve = _a.curve;
43919 var ctx = this.canvasElCtx();
43920 var widthDelta = curve.endWidth - curve.startWidth;
43921 var drawSteps = Math.floor(curve.length()) * 2;
43923 ctx.fillStyle = color;
43924 for (var i = 0; i < drawSteps; i += 1) {
43925 var t = i / drawSteps;
43931 var x = uuu * curve.startPoint.x;
43932 x += 3 * uu * t * curve.control1.x;
43933 x += 3 * u * tt * curve.control2.x;
43934 x += ttt * curve.endPoint.x;
43935 var y = uuu * curve.startPoint.y;
43936 y += 3 * uu * t * curve.control1.y;
43937 y += 3 * u * tt * curve.control2.y;
43938 y += ttt * curve.endPoint.y;
43939 var width = curve.startWidth + ttt * widthDelta;
43940 this.drawCurveSegment(x, y, width);
43946 drawCurveSegment: function (x, y, width) {
43947 var ctx = this.canvasElCtx();
43949 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43950 this.is_empty = false;
43955 var ctx = this.canvasElCtx();
43956 var canvas = this.canvasEl().dom;
43957 ctx.fillStyle = this.bg_color;
43958 ctx.clearRect(0, 0, canvas.width, canvas.height);
43959 ctx.fillRect(0, 0, canvas.width, canvas.height);
43960 this.curve_data = [];
43962 this.is_empty = true;
43967 return this.el.select('input',true).first();
43970 canvasEl: function()
43972 return this.el.select('canvas',true).first();
43975 canvasElCtx: function()
43977 return this.el.select('canvas',true).first().dom.getContext('2d');
43980 getImage: function(type)
43982 if(this.is_empty) {
43987 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43990 drawFromImage: function(img_src)
43992 var img = new Image();
43994 img.onload = function(){
43995 this.canvasElCtx().drawImage(img, 0, 0);
44000 this.is_empty = false;
44003 selectImage: function()
44005 this.fileEl().dom.click();
44008 uploadImage: function(e)
44010 var reader = new FileReader();
44012 reader.onload = function(e){
44013 var img = new Image();
44014 img.onload = function(){
44016 this.canvasElCtx().drawImage(img, 0, 0);
44018 img.src = e.target.result;
44021 reader.readAsDataURL(e.target.files[0]);
44024 // Bezier Point Constructor
44025 Point: (function () {
44026 function Point(x, y, time) {
44029 this.time = time || Date.now();
44031 Point.prototype.distanceTo = function (start) {
44032 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44034 Point.prototype.equals = function (other) {
44035 return this.x === other.x && this.y === other.y && this.time === other.time;
44037 Point.prototype.velocityFrom = function (start) {
44038 return this.time !== start.time
44039 ? this.distanceTo(start) / (this.time - start.time)
44046 // Bezier Constructor
44047 Bezier: (function () {
44048 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44049 this.startPoint = startPoint;
44050 this.control2 = control2;
44051 this.control1 = control1;
44052 this.endPoint = endPoint;
44053 this.startWidth = startWidth;
44054 this.endWidth = endWidth;
44056 Bezier.fromPoints = function (points, widths, scope) {
44057 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44058 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44059 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44061 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44062 var dx1 = s1.x - s2.x;
44063 var dy1 = s1.y - s2.y;
44064 var dx2 = s2.x - s3.x;
44065 var dy2 = s2.y - s3.y;
44066 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44067 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44068 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44069 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44070 var dxm = m1.x - m2.x;
44071 var dym = m1.y - m2.y;
44072 var k = l2 / (l1 + l2);
44073 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44074 var tx = s2.x - cm.x;
44075 var ty = s2.y - cm.y;
44077 c1: new scope.Point(m1.x + tx, m1.y + ty),
44078 c2: new scope.Point(m2.x + tx, m2.y + ty)
44081 Bezier.prototype.length = function () {
44086 for (var i = 0; i <= steps; i += 1) {
44088 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44089 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44091 var xdiff = cx - px;
44092 var ydiff = cy - py;
44093 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44100 Bezier.prototype.point = function (t, start, c1, c2, end) {
44101 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44102 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44103 + (3.0 * c2 * (1.0 - t) * t * t)
44104 + (end * t * t * t);
44109 throttleStroke: function(fn, wait) {
44110 if (wait === void 0) { wait = 250; }
44112 var timeout = null;
44116 var later = function () {
44117 previous = Date.now();
44119 result = fn.apply(storedContext, storedArgs);
44121 storedContext = null;
44125 return function wrapper() {
44127 for (var _i = 0; _i < arguments.length; _i++) {
44128 args[_i] = arguments[_i];
44130 var now = Date.now();
44131 var remaining = wait - (now - previous);
44132 storedContext = this;
44134 if (remaining <= 0 || remaining > wait) {
44136 clearTimeout(timeout);
44140 result = fn.apply(storedContext, storedArgs);
44142 storedContext = null;
44146 else if (!timeout) {
44147 timeout = window.setTimeout(later, remaining);