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;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952 * @cfg {String} size (sm|lg|xl) default empty
3953 * @cfg {Number} max_width set the max width of modal
3954 * @cfg {Boolean} editableTitle can the title be edited
3959 * Create a new Modal Dialog
3960 * @param {Object} config The config object
3963 Roo.bootstrap.Modal = function(config){
3964 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969 * The raw btnclick event for the button
3970 * @param {Roo.EventObject} e
3975 * Fire when dialog resize
3976 * @param {Roo.bootstrap.Modal} this
3977 * @param {Roo.EventObject} e
3981 * @event titlechanged
3982 * Fire when the editable title has been changed
3983 * @param {Roo.bootstrap.Modal} this
3984 * @param {Roo.EventObject} value
3986 "titlechanged" : true
3989 this.buttons = this.buttons || [];
3992 this.tmpl = Roo.factory(this.tmpl);
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3999 title : 'test dialog',
4009 specificTitle: false,
4011 buttonPosition: 'right',
4033 editableTitle : false,
4035 onRender : function(ct, position)
4037 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4040 var cfg = Roo.apply({}, this.getAutoCreate());
4043 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045 //if (!cfg.name.length) {
4049 cfg.cls += ' ' + this.cls;
4052 cfg.style = this.style;
4054 this.el = Roo.get(document.body).createChild(cfg, position);
4056 //var type = this.el.dom.type;
4059 if(this.tabIndex !== undefined){
4060 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4063 this.dialogEl = this.el.select('.modal-dialog',true).first();
4064 this.bodyEl = this.el.select('.modal-body',true).first();
4065 this.closeEl = this.el.select('.modal-header .close', true).first();
4066 this.headerEl = this.el.select('.modal-header',true).first();
4067 this.titleEl = this.el.select('.modal-title',true).first();
4068 this.footerEl = this.el.select('.modal-footer',true).first();
4070 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072 //this.el.addClass("x-dlg-modal");
4074 if (this.buttons.length) {
4075 Roo.each(this.buttons, function(bb) {
4076 var b = Roo.apply({}, bb);
4077 b.xns = b.xns || Roo.bootstrap;
4078 b.xtype = b.xtype || 'Button';
4079 if (typeof(b.listeners) == 'undefined') {
4080 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4083 var btn = Roo.factory(b);
4085 btn.render(this.getButtonContainer());
4089 // render the children.
4092 if(typeof(this.items) != 'undefined'){
4093 var items = this.items;
4096 for(var i =0;i < items.length;i++) {
4097 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4101 this.items = nitems;
4103 // where are these used - they used to be body/close/footer
4107 //this.el.addClass([this.fieldClass, this.cls]);
4111 getAutoCreate : function()
4113 // we will default to modal-body-overflow - might need to remove or make optional later.
4115 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''),
4116 html : this.html || ''
4121 cls : 'modal-title',
4125 if(this.specificTitle){ // WTF is this?
4130 if (this.allow_close && Roo.bootstrap.version == 3) {
4140 if (this.editableTitle) {
4142 cls: 'form-control roo-editable-title d-none',
4148 if (this.allow_close && Roo.bootstrap.version == 4) {
4158 if(this.size.length){
4159 size = 'modal-' + this.size;
4162 var footer = Roo.bootstrap.version == 3 ?
4164 cls : 'modal-footer',
4168 cls: 'btn-' + this.buttonPosition
4173 { // BS4 uses mr-auto on left buttons....
4174 cls : 'modal-footer'
4185 cls: "modal-dialog " + size,
4188 cls : "modal-content",
4191 cls : 'modal-header',
4206 modal.cls += ' fade';
4212 getChildContainer : function() {
4217 getButtonContainer : function() {
4219 return Roo.bootstrap.version == 4 ?
4220 this.el.select('.modal-footer',true).first()
4221 : this.el.select('.modal-footer div',true).first();
4224 initEvents : function()
4226 if (this.allow_close) {
4227 this.closeEl.on('click', this.hide, this);
4229 Roo.EventManager.onWindowResize(this.resize, this, true);
4230 if (this.editableTitle) {
4231 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4232 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233 this.headerEditEl.on('keyup', function(e) {
4234 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235 this.toggleHeaderInput(false)
4238 this.headerEditEl.on('blur', function(e) {
4239 this.toggleHeaderInput(false)
4248 this.maskEl.setSize(
4249 Roo.lib.Dom.getViewWidth(true),
4250 Roo.lib.Dom.getViewHeight(true)
4253 if (this.fitwindow) {
4257 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263 if(this.max_width !== 0) {
4265 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4268 this.setSize(w, this.height);
4272 if(this.max_height) {
4273 this.setSize(w,Math.min(
4275 Roo.lib.Dom.getViewportHeight(true) - 60
4281 if(!this.fit_content) {
4282 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4286 this.setSize(w, Math.min(
4288 this.headerEl.getHeight() +
4289 this.footerEl.getHeight() +
4290 this.getChildHeight(this.bodyEl.dom.childNodes),
4291 Roo.lib.Dom.getViewportHeight(true) - 60)
4297 setSize : function(w,h)
4308 if (!this.rendered) {
4311 this.toggleHeaderInput(false);
4312 //this.el.setStyle('display', 'block');
4313 this.el.removeClass('hideing');
4314 this.el.dom.style.display='block';
4316 Roo.get(document.body).addClass('modal-open');
4318 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4321 this.el.addClass('show');
4322 this.el.addClass('in');
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 // not sure how we can show data in here..
4331 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4334 Roo.get(document.body).addClass("x-body-masked");
4336 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4337 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338 this.maskEl.dom.style.display = 'block';
4339 this.maskEl.addClass('show');
4344 this.fireEvent('show', this);
4346 // set zindex here - otherwise it appears to be ignored...
4347 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4350 this.items.forEach( function(e) {
4351 e.layout ? e.layout() : false;
4359 if(this.fireEvent("beforehide", this) !== false){
4361 this.maskEl.removeClass('show');
4363 this.maskEl.dom.style.display = '';
4364 Roo.get(document.body).removeClass("x-body-masked");
4365 this.el.removeClass('in');
4366 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368 if(this.animate){ // why
4369 this.el.addClass('hideing');
4370 this.el.removeClass('show');
4372 if (!this.el.hasClass('hideing')) {
4373 return; // it's been shown again...
4376 this.el.dom.style.display='';
4378 Roo.get(document.body).removeClass('modal-open');
4379 this.el.removeClass('hideing');
4383 this.el.removeClass('show');
4384 this.el.dom.style.display='';
4385 Roo.get(document.body).removeClass('modal-open');
4388 this.fireEvent('hide', this);
4391 isVisible : function()
4394 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4398 addButton : function(str, cb)
4402 var b = Roo.apply({}, { html : str } );
4403 b.xns = b.xns || Roo.bootstrap;
4404 b.xtype = b.xtype || 'Button';
4405 if (typeof(b.listeners) == 'undefined') {
4406 b.listeners = { click : cb.createDelegate(this) };
4409 var btn = Roo.factory(b);
4411 btn.render(this.getButtonContainer());
4417 setDefaultButton : function(btn)
4419 //this.el.select('.modal-footer').()
4422 resizeTo: function(w,h)
4424 this.dialogEl.setWidth(w);
4426 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4428 this.bodyEl.setHeight(h - diff);
4430 this.fireEvent('resize', this);
4433 setContentSize : function(w, h)
4437 onButtonClick: function(btn,e)
4440 this.fireEvent('btnclick', btn.name, e);
4443 * Set the title of the Dialog
4444 * @param {String} str new Title
4446 setTitle: function(str) {
4447 this.titleEl.dom.innerHTML = str;
4451 * Set the body of the Dialog
4452 * @param {String} str new Title
4454 setBody: function(str) {
4455 this.bodyEl.dom.innerHTML = str;
4458 * Set the body of the Dialog using the template
4459 * @param {Obj} data - apply this data to the template and replace the body contents.
4461 applyBody: function(obj)
4464 Roo.log("Error - using apply Body without a template");
4467 this.tmpl.overwrite(this.bodyEl, obj);
4470 getChildHeight : function(child_nodes)
4474 child_nodes.length == 0
4479 var child_height = 0;
4481 for(var i = 0; i < child_nodes.length; i++) {
4484 * for modal with tabs...
4485 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487 var layout_childs = child_nodes[i].childNodes;
4489 for(var j = 0; j < layout_childs.length; j++) {
4491 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493 var layout_body_childs = layout_childs[j].childNodes;
4495 for(var k = 0; k < layout_body_childs.length; k++) {
4497 if(layout_body_childs[k].classList.contains('navbar')) {
4498 child_height += layout_body_childs[k].offsetHeight;
4502 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4524 child_height += child_nodes[i].offsetHeight;
4525 // Roo.log(child_nodes[i].offsetHeight);
4528 return child_height;
4530 toggleHeaderInput : function(is_edit)
4532 if (!this.editableTitle) {
4533 return; // not editable.
4535 if (is_edit && this.is_header_editing) {
4536 return; // already editing..
4540 this.headerEditEl.dom.value = this.title;
4541 this.headerEditEl.removeClass('d-none');
4542 this.headerEditEl.dom.focus();
4543 this.titleEl.addClass('d-none');
4545 this.is_header_editing = true;
4548 // flip back to not editing.
4549 this.title = this.headerEditEl.dom.value;
4550 this.headerEditEl.addClass('d-none');
4551 this.titleEl.removeClass('d-none');
4552 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553 this.is_header_editing = false;
4554 this.fireEvent('titlechanged', this, this.title);
4563 Roo.apply(Roo.bootstrap.Modal, {
4565 * Button config that displays a single OK button
4574 * Button config that displays Yes and No buttons
4590 * Button config that displays OK and Cancel buttons
4605 * Button config that displays Yes, No and Cancel buttons
4630 * messagebox - can be used as a replace
4634 * @class Roo.MessageBox
4635 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644 // process text value...
4648 // Show a dialog using config options:
4650 title:'Save Changes?',
4651 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652 buttons: Roo.Msg.YESNOCANCEL,
4659 Roo.bootstrap.MessageBox = function(){
4660 var dlg, opt, mask, waitTimer;
4661 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662 var buttons, activeTextEl, bwidth;
4666 var handleButton = function(button){
4668 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4672 var handleHide = function(){
4674 dlg.el.removeClass(opt.cls);
4677 // Roo.TaskMgr.stop(waitTimer);
4678 // waitTimer = null;
4683 var updateButtons = function(b){
4686 buttons["ok"].hide();
4687 buttons["cancel"].hide();
4688 buttons["yes"].hide();
4689 buttons["no"].hide();
4690 dlg.footerEl.hide();
4694 dlg.footerEl.show();
4695 for(var k in buttons){
4696 if(typeof buttons[k] != "function"){
4699 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700 width += buttons[k].el.getWidth()+15;
4710 var handleEsc = function(d, k, e){
4711 if(opt && opt.closable !== false){
4721 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722 * @return {Roo.BasicDialog} The BasicDialog element
4724 getDialog : function(){
4726 dlg = new Roo.bootstrap.Modal( {
4729 //constraintoviewport:false,
4731 //collapsible : false,
4736 //buttonAlign:"center",
4737 closeClick : function(){
4738 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4741 handleButton("cancel");
4746 dlg.on("hide", handleHide);
4748 //dlg.addKeyListener(27, handleEsc);
4750 this.buttons = buttons;
4751 var bt = this.buttonText;
4752 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757 bodyEl = dlg.bodyEl.createChild({
4759 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760 '<textarea class="roo-mb-textarea"></textarea>' +
4761 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4763 msgEl = bodyEl.dom.firstChild;
4764 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765 textboxEl.enableDisplayMode();
4766 textboxEl.addKeyListener([10,13], function(){
4767 if(dlg.isVisible() && opt && opt.buttons){
4770 }else if(opt.buttons.yes){
4771 handleButton("yes");
4775 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776 textareaEl.enableDisplayMode();
4777 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778 progressEl.enableDisplayMode();
4780 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781 var pf = progressEl.dom.firstChild;
4783 pp = Roo.get(pf.firstChild);
4784 pp.setHeight(pf.offsetHeight);
4792 * Updates the message box body text
4793 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794 * the XHTML-compliant non-breaking space character '&#160;')
4795 * @return {Roo.MessageBox} This message box
4797 updateText : function(text)
4799 if(!dlg.isVisible() && !opt.width){
4800 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803 msgEl.innerHTML = text || ' ';
4805 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808 Math.min(opt.width || cw , this.maxWidth),
4809 Math.max(opt.minWidth || this.minWidth, bwidth)
4812 activeTextEl.setWidth(w);
4814 if(dlg.isVisible()){
4815 dlg.fixedcenter = false;
4817 // to big, make it scroll. = But as usual stupid IE does not support
4820 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824 bodyEl.dom.style.height = '';
4825 bodyEl.dom.style.overflowY = '';
4828 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830 bodyEl.dom.style.overflowX = '';
4833 dlg.setContentSize(w, bodyEl.getHeight());
4834 if(dlg.isVisible()){
4835 dlg.fixedcenter = true;
4841 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4842 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845 * @return {Roo.MessageBox} This message box
4847 updateProgress : function(value, text){
4849 this.updateText(text);
4852 if (pp) { // weird bug on my firefox - for some reason this is not defined
4853 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4860 * Returns true if the message box is currently displayed
4861 * @return {Boolean} True if the message box is visible, else false
4863 isVisible : function(){
4864 return dlg && dlg.isVisible();
4868 * Hides the message box if it is displayed
4871 if(this.isVisible()){
4877 * Displays a new message box, or reinitializes an existing message box, based on the config options
4878 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879 * The following config object properties are supported:
4881 Property Type Description
4882 ---------- --------------- ------------------------------------------------------------------------------------
4883 animEl String/Element An id or Element from which the message box should animate as it opens and
4884 closes (defaults to undefined)
4885 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable Boolean False to hide the top-right close button (defaults to true). Note that
4888 progress and wait dialogs will ignore this property and always hide the
4889 close button as they can only be closed programmatically.
4890 cls String A custom CSS class to apply to the message box element
4891 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4892 displayed (defaults to 75)
4893 fn Function A callback function to execute after closing the dialog. The arguments to the
4894 function will be btn (the name of the button that was clicked, if applicable,
4895 e.g. "ok"), and text (the value of the active text field, if applicable).
4896 Progress and wait dialogs will ignore this option since they do not respond to
4897 user actions and can only be closed programmatically, so any required function
4898 should be called by the same code after it closes the dialog.
4899 icon String A CSS class that provides a background image to be used as an icon for
4900 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4902 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4903 modal Boolean False to allow user interaction with the page while the message box is
4904 displayed (defaults to true)
4905 msg String A string that will replace the existing message box body text (defaults
4906 to the XHTML-compliant non-breaking space character ' ')
4907 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4908 progress Boolean True to display a progress bar (defaults to false)
4909 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4912 title String The title text
4913 value String The string value to set into the active textbox element if displayed
4914 wait Boolean True to display a progress bar (defaults to false)
4915 width Number The width of the dialog in pixels
4922 msg: 'Please enter your address:',
4924 buttons: Roo.MessageBox.OKCANCEL,
4927 animEl: 'addAddressBtn'
4930 * @param {Object} config Configuration options
4931 * @return {Roo.MessageBox} This message box
4933 show : function(options)
4936 // this causes nightmares if you show one dialog after another
4937 // especially on callbacks..
4939 if(this.isVisible()){
4942 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4944 Roo.log("New Dialog Message:" + options.msg )
4945 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4949 var d = this.getDialog();
4951 d.setTitle(opt.title || " ");
4952 d.closeEl.setDisplayed(opt.closable !== false);
4953 activeTextEl = textboxEl;
4954 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959 textareaEl.setHeight(typeof opt.multiline == "number" ?
4960 opt.multiline : this.defaultTextHeight);
4961 activeTextEl = textareaEl;
4970 progressEl.setDisplayed(opt.progress === true);
4972 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974 this.updateProgress(0);
4975 activeTextEl.dom.value = opt.value || "";
4977 dlg.setDefaultButton(activeTextEl);
4979 var bs = opt.buttons;
4983 }else if(bs && bs.yes){
4984 db = buttons["yes"];
4986 dlg.setDefaultButton(db);
4988 bwidth = updateButtons(opt.buttons);
4989 this.updateText(opt.msg);
4991 d.el.addClass(opt.cls);
4993 d.proxyDrag = opt.proxyDrag === true;
4994 d.modal = opt.modal !== false;
4995 d.mask = opt.modal !== false ? mask : false;
4997 // force it to the end of the z-index stack so it gets a cursor in FF
4998 document.body.appendChild(dlg.el.dom);
4999 d.animateTarget = null;
5000 d.show(options.animEl);
5006 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5007 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008 * and closing the message box when the process is complete.
5009 * @param {String} title The title bar text
5010 * @param {String} msg The message box body text
5011 * @return {Roo.MessageBox} This message box
5013 progress : function(title, msg){
5020 minWidth: this.minProgressWidth,
5027 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028 * If a callback function is passed it will be called after the user clicks the button, and the
5029 * id of the button that was clicked will be passed as the only parameter to the callback
5030 * (could also be the top-right close button).
5031 * @param {String} title The title bar text
5032 * @param {String} msg The message box body text
5033 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034 * @param {Object} scope (optional) The scope of the callback function
5035 * @return {Roo.MessageBox} This message box
5037 alert : function(title, msg, fn, scope)
5052 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5053 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054 * You are responsible for closing the message box when the process is complete.
5055 * @param {String} msg The message box body text
5056 * @param {String} title (optional) The title bar text
5057 * @return {Roo.MessageBox} This message box
5059 wait : function(msg, title){
5070 waitTimer = Roo.TaskMgr.start({
5072 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5080 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083 * @param {String} title The title bar text
5084 * @param {String} msg The message box body text
5085 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086 * @param {Object} scope (optional) The scope of the callback function
5087 * @return {Roo.MessageBox} This message box
5089 confirm : function(title, msg, fn, scope){
5093 buttons: this.YESNO,
5102 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5104 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105 * (could also be the top-right close button) and the text that was entered will be passed as the two
5106 * parameters to the callback.
5107 * @param {String} title The title bar text
5108 * @param {String} msg The message box body text
5109 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110 * @param {Object} scope (optional) The scope of the callback function
5111 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113 * @return {Roo.MessageBox} This message box
5115 prompt : function(title, msg, fn, scope, multiline){
5119 buttons: this.OKCANCEL,
5124 multiline: multiline,
5131 * Button config that displays a single OK button
5136 * Button config that displays Yes and No buttons
5139 YESNO : {yes:true, no:true},
5141 * Button config that displays OK and Cancel buttons
5144 OKCANCEL : {ok:true, cancel:true},
5146 * Button config that displays Yes, No and Cancel buttons
5149 YESNOCANCEL : {yes:true, no:true, cancel:true},
5152 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5155 defaultTextHeight : 75,
5157 * The maximum width in pixels of the message box (defaults to 600)
5162 * The minimum width in pixels of the message box (defaults to 100)
5167 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5168 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5171 minProgressWidth : 250,
5173 * An object containing the default button text strings that can be overriden for localized language support.
5174 * Supported properties are: ok, cancel, yes and no.
5175 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5188 * Shorthand for {@link Roo.MessageBox}
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5200 * @class Roo.bootstrap.Navbar
5201 * @extends Roo.bootstrap.Component
5202 * Bootstrap Navbar class
5205 * Create a new Navbar
5206 * @param {Object} config The config object
5210 Roo.bootstrap.Navbar = function(config){
5211 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5215 * @event beforetoggle
5216 * Fire before toggle the menu
5217 * @param {Roo.EventObject} e
5219 "beforetoggle" : true
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5232 getAutoCreate : function(){
5235 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5239 initEvents :function ()
5241 //Roo.log(this.el.select('.navbar-toggle',true));
5242 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5249 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251 var size = this.el.getSize();
5252 this.maskEl.setSize(size.width, size.height);
5253 this.maskEl.enableDisplayMode("block");
5262 getChildContainer : function()
5264 if (this.el && this.el.select('.collapse').getCount()) {
5265 return this.el.select('.collapse',true).first();
5280 onToggle : function()
5283 if(this.fireEvent('beforetoggle', this) === false){
5286 var ce = this.el.select('.navbar-collapse',true).first();
5288 if (!ce.hasClass('show')) {
5298 * Expand the navbar pulldown
5300 expand : function ()
5303 var ce = this.el.select('.navbar-collapse',true).first();
5304 if (ce.hasClass('collapsing')) {
5307 ce.dom.style.height = '';
5309 ce.addClass('in'); // old...
5310 ce.removeClass('collapse');
5311 ce.addClass('show');
5312 var h = ce.getHeight();
5314 ce.removeClass('show');
5315 // at this point we should be able to see it..
5316 ce.addClass('collapsing');
5318 ce.setHeight(0); // resize it ...
5319 ce.on('transitionend', function() {
5320 //Roo.log('done transition');
5321 ce.removeClass('collapsing');
5322 ce.addClass('show');
5323 ce.removeClass('collapse');
5325 ce.dom.style.height = '';
5326 }, this, { single: true} );
5328 ce.dom.scrollTop = 0;
5331 * Collapse the navbar pulldown
5333 collapse : function()
5335 var ce = this.el.select('.navbar-collapse',true).first();
5337 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338 // it's collapsed or collapsing..
5341 ce.removeClass('in'); // old...
5342 ce.setHeight(ce.getHeight());
5343 ce.removeClass('show');
5344 ce.addClass('collapsing');
5346 ce.on('transitionend', function() {
5347 ce.dom.style.height = '';
5348 ce.removeClass('collapsing');
5349 ce.addClass('collapse');
5350 }, this, { single: true} );
5370 * @class Roo.bootstrap.NavSimplebar
5371 * @extends Roo.bootstrap.Navbar
5372 * Bootstrap Sidebar class
5374 * @cfg {Boolean} inverse is inverted color
5376 * @cfg {String} type (nav | pills | tabs)
5377 * @cfg {Boolean} arrangement stacked | justified
5378 * @cfg {String} align (left | right) alignment
5380 * @cfg {Boolean} main (true|false) main nav bar? default false
5381 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383 * @cfg {String} tag (header|footer|nav|div) default is nav
5385 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5389 * Create a new Sidebar
5390 * @param {Object} config The config object
5394 Roo.bootstrap.NavSimplebar = function(config){
5395 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5414 getAutoCreate : function(){
5418 tag : this.tag || 'div',
5419 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421 if (['light','white'].indexOf(this.weight) > -1) {
5422 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424 cfg.cls += ' bg-' + this.weight;
5427 cfg.cls += ' navbar-inverse';
5431 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5442 cls: 'nav nav-' + this.xtype,
5448 this.type = this.type || 'nav';
5449 if (['tabs','pills'].indexOf(this.type) != -1) {
5450 cfg.cn[0].cls += ' nav-' + this.type
5454 if (this.type!=='nav') {
5455 Roo.log('nav type must be nav/tabs/pills')
5457 cfg.cn[0].cls += ' navbar-nav'
5463 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464 cfg.cn[0].cls += ' nav-' + this.arrangement;
5468 if (this.align === 'right') {
5469 cfg.cn[0].cls += ' navbar-right';
5494 * navbar-expand-md fixed-top
5498 * @class Roo.bootstrap.NavHeaderbar
5499 * @extends Roo.bootstrap.NavSimplebar
5500 * Bootstrap Sidebar class
5502 * @cfg {String} brand what is brand
5503 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504 * @cfg {String} brand_href href of the brand
5505 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5506 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5511 * Create a new Sidebar
5512 * @param {Object} config The config object
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5528 desktopCenter : false,
5531 getAutoCreate : function(){
5534 tag: this.nav || 'nav',
5535 cls: 'navbar navbar-expand-md',
5541 if (this.desktopCenter) {
5542 cn.push({cls : 'container', cn : []});
5550 cls: 'navbar-toggle navbar-toggler',
5551 'data-toggle': 'collapse',
5556 html: 'Toggle navigation'
5560 cls: 'icon-bar navbar-toggler-icon'
5573 cn.push( Roo.bootstrap.version == 4 ? btn : {
5575 cls: 'navbar-header',
5584 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5588 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590 if (['light','white'].indexOf(this.weight) > -1) {
5591 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593 cfg.cls += ' bg-' + this.weight;
5596 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599 // tag can override this..
5601 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5604 if (this.brand !== '') {
5605 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608 href: this.brand_href ? this.brand_href : '#',
5609 cls: 'navbar-brand',
5617 cfg.cls += ' main-nav';
5625 getHeaderChildContainer : function()
5627 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628 return this.el.select('.navbar-header',true).first();
5631 return this.getChildContainer();
5634 getChildContainer : function()
5637 return this.el.select('.roo-navbar-collapse',true).first();
5642 initEvents : function()
5644 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646 if (this.autohide) {
5651 Roo.get(document).on('scroll',function(e) {
5652 var ns = Roo.get(document).getScroll().top;
5653 var os = prevScroll;
5657 ft.removeClass('slideDown');
5658 ft.addClass('slideUp');
5661 ft.removeClass('slideUp');
5662 ft.addClass('slideDown');
5683 * @class Roo.bootstrap.NavSidebar
5684 * @extends Roo.bootstrap.Navbar
5685 * Bootstrap Sidebar class
5688 * Create a new Sidebar
5689 * @param {Object} config The config object
5693 Roo.bootstrap.NavSidebar = function(config){
5694 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5699 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701 getAutoCreate : function(){
5706 cls: 'sidebar sidebar-nav'
5728 * @class Roo.bootstrap.NavGroup
5729 * @extends Roo.bootstrap.Component
5730 * Bootstrap NavGroup class
5731 * @cfg {String} align (left|right)
5732 * @cfg {Boolean} inverse
5733 * @cfg {String} type (nav|pills|tab) default nav
5734 * @cfg {String} navId - reference Id for navbar.
5738 * Create a new nav group
5739 * @param {Object} config The config object
5742 Roo.bootstrap.NavGroup = function(config){
5743 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5746 Roo.bootstrap.NavGroup.register(this);
5750 * Fires when the active item changes
5751 * @param {Roo.bootstrap.NavGroup} this
5752 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5771 getAutoCreate : function()
5773 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779 if (Roo.bootstrap.version == 4) {
5780 if (['tabs','pills'].indexOf(this.type) != -1) {
5781 cfg.cls += ' nav-' + this.type;
5783 // trying to remove so header bar can right align top?
5784 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785 // do not use on header bar...
5786 cfg.cls += ' navbar-nav';
5791 if (['tabs','pills'].indexOf(this.type) != -1) {
5792 cfg.cls += ' nav-' + this.type
5794 if (this.type !== 'nav') {
5795 Roo.log('nav type must be nav/tabs/pills')
5797 cfg.cls += ' navbar-nav'
5801 if (this.parent() && this.parent().sidebar) {
5804 cls: 'dashboard-menu sidebar-menu'
5810 if (this.form === true) {
5813 cls: 'navbar-form form-inline'
5815 //nav navbar-right ml-md-auto
5816 if (this.align === 'right') {
5817 cfg.cls += ' navbar-right ml-md-auto';
5819 cfg.cls += ' navbar-left';
5823 if (this.align === 'right') {
5824 cfg.cls += ' navbar-right ml-md-auto';
5826 cfg.cls += ' mr-auto';
5830 cfg.cls += ' navbar-inverse';
5838 * sets the active Navigation item
5839 * @param {Roo.bootstrap.NavItem} the new current navitem
5841 setActiveItem : function(item)
5844 Roo.each(this.navItems, function(v){
5849 v.setActive(false, true);
5856 item.setActive(true, true);
5857 this.fireEvent('changed', this, item, prev);
5862 * gets the active Navigation item
5863 * @return {Roo.bootstrap.NavItem} the current navitem
5865 getActive : function()
5869 Roo.each(this.navItems, function(v){
5880 indexOfNav : function()
5884 Roo.each(this.navItems, function(v,i){
5895 * adds a Navigation item
5896 * @param {Roo.bootstrap.NavItem} the navitem to add
5898 addItem : function(cfg)
5900 if (this.form && Roo.bootstrap.version == 4) {
5903 var cn = new Roo.bootstrap.NavItem(cfg);
5905 cn.parentId = this.id;
5906 cn.onRender(this.el, null);
5910 * register a Navigation item
5911 * @param {Roo.bootstrap.NavItem} the navitem to add
5913 register : function(item)
5915 this.navItems.push( item);
5916 item.navId = this.navId;
5921 * clear all the Navigation item
5924 clearAll : function()
5927 this.el.dom.innerHTML = '';
5930 getNavItem: function(tabId)
5933 Roo.each(this.navItems, function(e) {
5934 if (e.tabId == tabId) {
5944 setActiveNext : function()
5946 var i = this.indexOfNav(this.getActive());
5947 if (i > this.navItems.length) {
5950 this.setActiveItem(this.navItems[i+1]);
5952 setActivePrev : function()
5954 var i = this.indexOfNav(this.getActive());
5958 this.setActiveItem(this.navItems[i-1]);
5960 clearWasActive : function(except) {
5961 Roo.each(this.navItems, function(e) {
5962 if (e.tabId != except.tabId && e.was_active) {
5963 e.was_active = false;
5970 getWasActive : function ()
5973 Roo.each(this.navItems, function(e) {
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5992 * register a Navigation Group
5993 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5995 register : function(navgrp)
5997 this.groups[navgrp.navId] = navgrp;
6001 * fetch a Navigation Group based on the navigation ID
6002 * @param {string} the navgroup to add
6003 * @returns {Roo.bootstrap.NavGroup} the navgroup
6005 get: function(navId) {
6006 if (typeof(this.groups[navId]) == 'undefined') {
6008 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6010 return this.groups[navId] ;
6025 * @class Roo.bootstrap.NavItem
6026 * @extends Roo.bootstrap.Component
6027 * Bootstrap Navbar.NavItem class
6028 * @cfg {String} href link to
6029 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6031 * @cfg {String} html content of button
6032 * @cfg {String} badge text inside badge
6033 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034 * @cfg {String} glyphicon DEPRICATED - use fa
6035 * @cfg {String} icon DEPRICATED - use fa
6036 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037 * @cfg {Boolean} active Is item active
6038 * @cfg {Boolean} disabled Is item disabled
6040 * @cfg {Boolean} preventDefault (true | false) default false
6041 * @cfg {String} tabId the tab that this item activates.
6042 * @cfg {String} tagtype (a|span) render as a href or span?
6043 * @cfg {Boolean} animateRef (true|false) link to element default false
6046 * Create a new Navbar Item
6047 * @param {Object} config The config object
6049 Roo.bootstrap.NavItem = function(config){
6050 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6055 * The raw click event for the entire grid.
6056 * @param {Roo.EventObject} e
6061 * Fires when the active item active state changes
6062 * @param {Roo.bootstrap.NavItem} this
6063 * @param {boolean} state the new state
6069 * Fires when scroll to element
6070 * @param {Roo.bootstrap.NavItem} this
6071 * @param {Object} options
6072 * @param {Roo.EventObject} e
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6089 preventDefault : false,
6097 button_outline : false,
6101 getAutoCreate : function(){
6109 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6111 if (this.disabled) {
6112 cfg.cls += ' disabled';
6116 if (this.button_weight.length) {
6117 cfg.tag = this.href ? 'a' : 'button';
6118 cfg.html = this.html || '';
6119 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6121 cfg.href = this.href;
6124 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6127 // menu .. should add dropdown-menu class - so no need for carat..
6129 if (this.badge !== '') {
6131 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6140 href : this.href || "#",
6141 html: this.html || ''
6144 if (this.tagtype == 'a') {
6145 cfg.cn[0].cls = 'nav-link';
6148 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6151 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6153 if(this.glyphicon) {
6154 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6159 cfg.cn[0].html += " <span class='caret'></span>";
6163 if (this.badge !== '') {
6165 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6173 onRender : function(ct, position)
6175 // Roo.log("Call onRender: " + this.xtype);
6176 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6180 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181 this.navLink = this.el.select('.nav-link',true).first();
6186 initEvents: function()
6188 if (typeof (this.menu) != 'undefined') {
6189 this.menu.parentType = this.xtype;
6190 this.menu.triggerEl = this.el;
6191 this.menu = this.addxtype(Roo.apply({}, this.menu));
6194 this.el.select('a',true).on('click', this.onClick, this);
6196 if(this.tagtype == 'span'){
6197 this.el.select('span',true).on('click', this.onClick, this);
6200 // at this point parent should be available..
6201 this.parent().register(this);
6204 onClick : function(e)
6206 if (e.getTarget('.dropdown-menu-item')) {
6207 // did you click on a menu itemm.... - then don't trigger onclick..
6212 this.preventDefault ||
6215 Roo.log("NavItem - prevent Default?");
6219 if (this.disabled) {
6223 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224 if (tg && tg.transition) {
6225 Roo.log("waiting for the transitionend");
6231 //Roo.log("fire event clicked");
6232 if(this.fireEvent('click', this, e) === false){
6236 if(this.tagtype == 'span'){
6240 //Roo.log(this.href);
6241 var ael = this.el.select('a',true).first();
6244 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247 return; // ignore... - it's a 'hash' to another page.
6249 Roo.log("NavItem - prevent Default?");
6251 this.scrollToElement(e);
6255 var p = this.parent();
6257 if (['tabs','pills'].indexOf(p.type)!==-1) {
6258 if (typeof(p.setActiveItem) !== 'undefined') {
6259 p.setActiveItem(this);
6263 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265 // remove the collapsed menu expand...
6266 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6270 isActive: function () {
6273 setActive : function(state, fire, is_was_active)
6275 if (this.active && !state && this.navId) {
6276 this.was_active = true;
6277 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6279 nv.clearWasActive(this);
6283 this.active = state;
6286 this.el.removeClass('active');
6287 this.navLink ? this.navLink.removeClass('active') : false;
6288 } else if (!this.el.hasClass('active')) {
6290 this.el.addClass('active');
6291 if (Roo.bootstrap.version == 4 && this.navLink ) {
6292 this.navLink.addClass('active');
6297 this.fireEvent('changed', this, state);
6300 // show a panel if it's registered and related..
6302 if (!this.navId || !this.tabId || !state || is_was_active) {
6306 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6310 var pan = tg.getPanelByName(this.tabId);
6314 // if we can not flip to new panel - go back to old nav highlight..
6315 if (false == tg.showPanel(pan)) {
6316 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6318 var onav = nv.getWasActive();
6320 onav.setActive(true, false, true);
6329 // this should not be here...
6330 setDisabled : function(state)
6332 this.disabled = state;
6334 this.el.removeClass('disabled');
6335 } else if (!this.el.hasClass('disabled')) {
6336 this.el.addClass('disabled');
6342 * Fetch the element to display the tooltip on.
6343 * @return {Roo.Element} defaults to this.el
6345 tooltipEl : function()
6347 return this.el.select('' + this.tagtype + '', true).first();
6350 scrollToElement : function(e)
6352 var c = document.body;
6355 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6357 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358 c = document.documentElement;
6361 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367 var o = target.calcOffsetsTo(c);
6374 this.fireEvent('scrollto', this, options, e);
6376 Roo.get(c).scrollTo('top', options.value, true);
6389 * <span> icon </span>
6390 * <span> text </span>
6391 * <span>badge </span>
6395 * @class Roo.bootstrap.NavSidebarItem
6396 * @extends Roo.bootstrap.NavItem
6397 * Bootstrap Navbar.NavSidebarItem class
6398 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399 * {Boolean} open is the menu open
6400 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402 * {String} buttonSize (sm|md|lg)the extra classes for the button
6403 * {Boolean} showArrow show arrow next to the text (default true)
6405 * Create a new Navbar Button
6406 * @param {Object} config The config object
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6414 * The raw click event for the entire grid.
6415 * @param {Roo.EventObject} e
6420 * Fires when the active item active state changes
6421 * @param {Roo.bootstrap.NavSidebarItem} this
6422 * @param {boolean} state the new state
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6432 badgeWeight : 'default',
6438 buttonWeight : 'default',
6444 getAutoCreate : function(){
6449 href : this.href || '#',
6455 if(this.buttonView){
6458 href : this.href || '#',
6459 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6472 cfg.cls += ' active';
6475 if (this.disabled) {
6476 cfg.cls += ' disabled';
6479 cfg.cls += ' open x-open';
6482 if (this.glyphicon || this.icon) {
6483 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6484 a.cn.push({ tag : 'i', cls : c }) ;
6487 if(!this.buttonView){
6490 html : this.html || ''
6497 if (this.badge !== '') {
6498 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6504 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6507 a.cls += ' dropdown-toggle treeview' ;
6513 initEvents : function()
6515 if (typeof (this.menu) != 'undefined') {
6516 this.menu.parentType = this.xtype;
6517 this.menu.triggerEl = this.el;
6518 this.menu = this.addxtype(Roo.apply({}, this.menu));
6521 this.el.on('click', this.onClick, this);
6523 if(this.badge !== ''){
6524 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6529 onClick : function(e)
6536 if(this.preventDefault){
6540 this.fireEvent('click', this, e);
6543 disable : function()
6545 this.setDisabled(true);
6550 this.setDisabled(false);
6553 setDisabled : function(state)
6555 if(this.disabled == state){
6559 this.disabled = state;
6562 this.el.addClass('disabled');
6566 this.el.removeClass('disabled');
6571 setActive : function(state)
6573 if(this.active == state){
6577 this.active = state;
6580 this.el.addClass('active');
6584 this.el.removeClass('active');
6589 isActive: function ()
6594 setBadge : function(str)
6600 this.badgeEl.dom.innerHTML = str;
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6619 * @class Roo.bootstrap.breadcrumb.Nav
6620 * @extends Roo.bootstrap.Component
6621 * Bootstrap Breadcrumb Nav Class
6623 * @children Roo.bootstrap.breadcrumb.Item
6626 * Create a new breadcrumb.Nav
6627 * @param {Object} config The config object
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6639 getAutoCreate : function()
6656 initEvents: function()
6658 this.olEl = this.el.select('ol',true).first();
6660 getChildContainer : function()
6676 * @class Roo.bootstrap.breadcrumb.Nav
6677 * @extends Roo.bootstrap.Component
6678 * Bootstrap Breadcrumb Nav Class
6680 * @children Roo.bootstrap.breadcrumb.Component
6681 * @cfg {String} html the content of the link.
6682 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683 * @cfg {Boolean} active is it active
6687 * Create a new breadcrumb.Nav
6688 * @param {Object} config The config object
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6697 * The img click event for the img.
6698 * @param {Roo.EventObject} e
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6710 getAutoCreate : function()
6715 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6717 if (this.href !== false) {
6724 cfg.html = this.html;
6730 initEvents: function()
6733 this.el.select('a', true).first().on('click',this.onClick, this)
6737 onClick : function(e)
6740 this.fireEvent('click',this, e);
6753 * @class Roo.bootstrap.Row
6754 * @extends Roo.bootstrap.Component
6755 * Bootstrap Row class (contains columns...)
6759 * @param {Object} config The config object
6762 Roo.bootstrap.Row = function(config){
6763 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6768 getAutoCreate : function(){
6787 * @class Roo.bootstrap.Pagination
6788 * @extends Roo.bootstrap.Component
6789 * Bootstrap Pagination class
6790 * @cfg {String} size xs | sm | md | lg
6791 * @cfg {Boolean} inverse false | true
6794 * Create a new Pagination
6795 * @param {Object} config The config object
6798 Roo.bootstrap.Pagination = function(config){
6799 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6808 getAutoCreate : function(){
6814 cfg.cls += ' inverse';
6820 cfg.cls += " " + this.cls;
6838 * @class Roo.bootstrap.PaginationItem
6839 * @extends Roo.bootstrap.Component
6840 * Bootstrap PaginationItem class
6841 * @cfg {String} html text
6842 * @cfg {String} href the link
6843 * @cfg {Boolean} preventDefault (true | false) default true
6844 * @cfg {Boolean} active (true | false) default false
6845 * @cfg {Boolean} disabled default false
6849 * Create a new PaginationItem
6850 * @param {Object} config The config object
6854 Roo.bootstrap.PaginationItem = function(config){
6855 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6860 * The raw click event for the entire grid.
6861 * @param {Roo.EventObject} e
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6871 preventDefault: true,
6876 getAutoCreate : function(){
6882 href : this.href ? this.href : '#',
6883 html : this.html ? this.html : ''
6893 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6897 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903 initEvents: function() {
6905 this.el.on('click', this.onClick, this);
6908 onClick : function(e)
6910 Roo.log('PaginationItem on click ');
6911 if(this.preventDefault){
6919 this.fireEvent('click', this, e);
6935 * @class Roo.bootstrap.Slider
6936 * @extends Roo.bootstrap.Component
6937 * Bootstrap Slider class
6940 * Create a new Slider
6941 * @param {Object} config The config object
6944 Roo.bootstrap.Slider = function(config){
6945 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6950 getAutoCreate : function(){
6954 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6958 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6970 * Ext JS Library 1.1.1
6971 * Copyright(c) 2006-2007, Ext JS, LLC.
6973 * Originally Released Under LGPL - original licence link has changed is not relivant.
6976 * <script type="text/javascript">
6981 * @class Roo.grid.ColumnModel
6982 * @extends Roo.util.Observable
6983 * This is the default implementation of a ColumnModel used by the Grid. It defines
6984 * the columns in the grid.
6987 var colModel = new Roo.grid.ColumnModel([
6988 {header: "Ticker", width: 60, sortable: true, locked: true},
6989 {header: "Company Name", width: 150, sortable: true},
6990 {header: "Market Cap.", width: 100, sortable: true},
6991 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992 {header: "Employees", width: 100, sortable: true, resizable: false}
6997 * The config options listed for this class are options which may appear in each
6998 * individual column definition.
6999 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7001 * @param {Object} config An Array of column config objects. See this class's
7002 * config objects for details.
7004 Roo.grid.ColumnModel = function(config){
7006 * The config passed into the constructor
7008 this.config = config;
7011 // if no id, create one
7012 // if the column does not have a dataIndex mapping,
7013 // map it to the order it is in the config
7014 for(var i = 0, len = config.length; i < len; i++){
7016 if(typeof c.dataIndex == "undefined"){
7019 if(typeof c.renderer == "string"){
7020 c.renderer = Roo.util.Format[c.renderer];
7022 if(typeof c.id == "undefined"){
7025 if(c.editor && c.editor.xtype){
7026 c.editor = Roo.factory(c.editor, Roo.grid);
7028 if(c.editor && c.editor.isFormField){
7029 c.editor = new Roo.grid.GridEditor(c.editor);
7031 this.lookup[c.id] = c;
7035 * The width of columns which have no width specified (defaults to 100)
7038 this.defaultWidth = 100;
7041 * Default sortable of columns which have no sortable specified (defaults to false)
7044 this.defaultSortable = false;
7048 * @event widthchange
7049 * Fires when the width of a column changes.
7050 * @param {ColumnModel} this
7051 * @param {Number} columnIndex The column index
7052 * @param {Number} newWidth The new width
7054 "widthchange": true,
7056 * @event headerchange
7057 * Fires when the text of a header changes.
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Number} newText The new header text
7062 "headerchange": true,
7064 * @event hiddenchange
7065 * Fires when a column is hidden or "unhidden".
7066 * @param {ColumnModel} this
7067 * @param {Number} columnIndex The column index
7068 * @param {Boolean} hidden true if hidden, false otherwise
7070 "hiddenchange": true,
7072 * @event columnmoved
7073 * Fires when a column is moved.
7074 * @param {ColumnModel} this
7075 * @param {Number} oldIndex
7076 * @param {Number} newIndex
7078 "columnmoved" : true,
7080 * @event columlockchange
7081 * Fires when a column's locked state is changed
7082 * @param {ColumnModel} this
7083 * @param {Number} colIndex
7084 * @param {Boolean} locked true if locked
7086 "columnlockchange" : true
7088 Roo.grid.ColumnModel.superclass.constructor.call(this);
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7092 * @cfg {String} header The header text to display in the Grid view.
7095 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097 * specified, the column's index is used as an index into the Record's data Array.
7100 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7104 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105 * Defaults to the value of the {@link #defaultSortable} property.
7106 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7109 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7112 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7115 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7118 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7121 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7127 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7130 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7133 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7136 * @cfg {String} cursor (Optional)
7139 * @cfg {String} tooltip (Optional)
7142 * @cfg {Number} xs (Optional)
7145 * @cfg {Number} sm (Optional)
7148 * @cfg {Number} md (Optional)
7151 * @cfg {Number} lg (Optional)
7154 * Returns the id of the column at the specified index.
7155 * @param {Number} index The column index
7156 * @return {String} the id
7158 getColumnId : function(index){
7159 return this.config[index].id;
7163 * Returns the column for a specified id.
7164 * @param {String} id The column id
7165 * @return {Object} the column
7167 getColumnById : function(id){
7168 return this.lookup[id];
7173 * Returns the column for a specified dataIndex.
7174 * @param {String} dataIndex The column dataIndex
7175 * @return {Object|Boolean} the column or false if not found
7177 getColumnByDataIndex: function(dataIndex){
7178 var index = this.findColumnIndex(dataIndex);
7179 return index > -1 ? this.config[index] : false;
7183 * Returns the index for a specified column id.
7184 * @param {String} id The column id
7185 * @return {Number} the index, or -1 if not found
7187 getIndexById : function(id){
7188 for(var i = 0, len = this.config.length; i < len; i++){
7189 if(this.config[i].id == id){
7197 * Returns the index for a specified column dataIndex.
7198 * @param {String} dataIndex The column dataIndex
7199 * @return {Number} the index, or -1 if not found
7202 findColumnIndex : function(dataIndex){
7203 for(var i = 0, len = this.config.length; i < len; i++){
7204 if(this.config[i].dataIndex == dataIndex){
7212 moveColumn : function(oldIndex, newIndex){
7213 var c = this.config[oldIndex];
7214 this.config.splice(oldIndex, 1);
7215 this.config.splice(newIndex, 0, c);
7216 this.dataMap = null;
7217 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7220 isLocked : function(colIndex){
7221 return this.config[colIndex].locked === true;
7224 setLocked : function(colIndex, value, suppressEvent){
7225 if(this.isLocked(colIndex) == value){
7228 this.config[colIndex].locked = value;
7230 this.fireEvent("columnlockchange", this, colIndex, value);
7234 getTotalLockedWidth : function(){
7236 for(var i = 0; i < this.config.length; i++){
7237 if(this.isLocked(i) && !this.isHidden(i)){
7238 this.totalWidth += this.getColumnWidth(i);
7244 getLockedCount : function(){
7245 for(var i = 0, len = this.config.length; i < len; i++){
7246 if(!this.isLocked(i)){
7251 return this.config.length;
7255 * Returns the number of columns.
7258 getColumnCount : function(visibleOnly){
7259 if(visibleOnly === true){
7261 for(var i = 0, len = this.config.length; i < len; i++){
7262 if(!this.isHidden(i)){
7268 return this.config.length;
7272 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273 * @param {Function} fn
7274 * @param {Object} scope (optional)
7275 * @return {Array} result
7277 getColumnsBy : function(fn, scope){
7279 for(var i = 0, len = this.config.length; i < len; i++){
7280 var c = this.config[i];
7281 if(fn.call(scope||this, c, i) === true){
7289 * Returns true if the specified column is sortable.
7290 * @param {Number} col The column index
7293 isSortable : function(col){
7294 if(typeof this.config[col].sortable == "undefined"){
7295 return this.defaultSortable;
7297 return this.config[col].sortable;
7301 * Returns the rendering (formatting) function defined for the column.
7302 * @param {Number} col The column index.
7303 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7305 getRenderer : function(col){
7306 if(!this.config[col].renderer){
7307 return Roo.grid.ColumnModel.defaultRenderer;
7309 return this.config[col].renderer;
7313 * Sets the rendering (formatting) function for a column.
7314 * @param {Number} col The column index
7315 * @param {Function} fn The function to use to process the cell's raw data
7316 * to return HTML markup for the grid view. The render function is called with
7317 * the following parameters:<ul>
7318 * <li>Data value.</li>
7319 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320 * <li>css A CSS style string to apply to the table cell.</li>
7321 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323 * <li>Row index</li>
7324 * <li>Column index</li>
7325 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7327 setRenderer : function(col, fn){
7328 this.config[col].renderer = fn;
7332 * Returns the width for the specified column.
7333 * @param {Number} col The column index
7336 getColumnWidth : function(col){
7337 return this.config[col].width * 1 || this.defaultWidth;
7341 * Sets the width for a column.
7342 * @param {Number} col The column index
7343 * @param {Number} width The new width
7345 setColumnWidth : function(col, width, suppressEvent){
7346 this.config[col].width = width;
7347 this.totalWidth = null;
7349 this.fireEvent("widthchange", this, col, width);
7354 * Returns the total width of all columns.
7355 * @param {Boolean} includeHidden True to include hidden column widths
7358 getTotalWidth : function(includeHidden){
7359 if(!this.totalWidth){
7360 this.totalWidth = 0;
7361 for(var i = 0, len = this.config.length; i < len; i++){
7362 if(includeHidden || !this.isHidden(i)){
7363 this.totalWidth += this.getColumnWidth(i);
7367 return this.totalWidth;
7371 * Returns the header for the specified column.
7372 * @param {Number} col The column index
7375 getColumnHeader : function(col){
7376 return this.config[col].header;
7380 * Sets the header for a column.
7381 * @param {Number} col The column index
7382 * @param {String} header The new header
7384 setColumnHeader : function(col, header){
7385 this.config[col].header = header;
7386 this.fireEvent("headerchange", this, col, header);
7390 * Returns the tooltip for the specified column.
7391 * @param {Number} col The column index
7394 getColumnTooltip : function(col){
7395 return this.config[col].tooltip;
7398 * Sets the tooltip for a column.
7399 * @param {Number} col The column index
7400 * @param {String} tooltip The new tooltip
7402 setColumnTooltip : function(col, tooltip){
7403 this.config[col].tooltip = tooltip;
7407 * Returns the dataIndex for the specified column.
7408 * @param {Number} col The column index
7411 getDataIndex : function(col){
7412 return this.config[col].dataIndex;
7416 * Sets the dataIndex for a column.
7417 * @param {Number} col The column index
7418 * @param {Number} dataIndex The new dataIndex
7420 setDataIndex : function(col, dataIndex){
7421 this.config[col].dataIndex = dataIndex;
7427 * Returns true if the cell is editable.
7428 * @param {Number} colIndex The column index
7429 * @param {Number} rowIndex The row index - this is nto actually used..?
7432 isCellEditable : function(colIndex, rowIndex){
7433 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7437 * Returns the editor defined for the cell/column.
7438 * return false or null to disable editing.
7439 * @param {Number} colIndex The column index
7440 * @param {Number} rowIndex The row index
7443 getCellEditor : function(colIndex, rowIndex){
7444 return this.config[colIndex].editor;
7448 * Sets if a column is editable.
7449 * @param {Number} col The column index
7450 * @param {Boolean} editable True if the column is editable
7452 setEditable : function(col, editable){
7453 this.config[col].editable = editable;
7458 * Returns true if the column is hidden.
7459 * @param {Number} colIndex The column index
7462 isHidden : function(colIndex){
7463 return this.config[colIndex].hidden;
7468 * Returns true if the column width cannot be changed
7470 isFixed : function(colIndex){
7471 return this.config[colIndex].fixed;
7475 * Returns true if the column can be resized
7478 isResizable : function(colIndex){
7479 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7482 * Sets if a column is hidden.
7483 * @param {Number} colIndex The column index
7484 * @param {Boolean} hidden True if the column is hidden
7486 setHidden : function(colIndex, hidden){
7487 this.config[colIndex].hidden = hidden;
7488 this.totalWidth = null;
7489 this.fireEvent("hiddenchange", this, colIndex, hidden);
7493 * Sets the editor for a column.
7494 * @param {Number} col The column index
7495 * @param {Object} editor The editor object
7497 setEditor : function(col, editor){
7498 this.config[col].editor = editor;
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7504 if(typeof value == "object") {
7507 if(typeof value == "string" && value.length < 1){
7511 return String.format("{0}", value);
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7518 * Ext JS Library 1.1.1
7519 * Copyright(c) 2006-2007, Ext JS, LLC.
7521 * Originally Released Under LGPL - original licence link has changed is not relivant.
7524 * <script type="text/javascript">
7528 * @class Roo.LoadMask
7529 * A simple utility class for generically masking elements while loading data. If the element being masked has
7530 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7532 * element's UpdateManager load indicator and will be destroyed after the initial load.
7534 * Create a new LoadMask
7535 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536 * @param {Object} config The config object
7538 Roo.LoadMask = function(el, config){
7539 this.el = Roo.get(el);
7540 Roo.apply(this, config);
7542 this.store.on('beforeload', this.onBeforeLoad, this);
7543 this.store.on('load', this.onLoad, this);
7544 this.store.on('loadexception', this.onLoadException, this);
7545 this.removeMask = false;
7547 var um = this.el.getUpdateManager();
7548 um.showLoadIndicator = false; // disable the default indicator
7549 um.on('beforeupdate', this.onBeforeLoad, this);
7550 um.on('update', this.onLoad, this);
7551 um.on('failure', this.onLoad, this);
7552 this.removeMask = true;
7556 Roo.LoadMask.prototype = {
7558 * @cfg {Boolean} removeMask
7559 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7564 * The text to display in a centered loading message box (defaults to 'Loading...')
7568 * @cfg {String} msgCls
7569 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7571 msgCls : 'x-mask-loading',
7574 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580 * Disables the mask to prevent it from being displayed
7582 disable : function(){
7583 this.disabled = true;
7587 * Enables the mask so that it can be displayed
7589 enable : function(){
7590 this.disabled = false;
7593 onLoadException : function()
7597 if (typeof(arguments[3]) != 'undefined') {
7598 Roo.MessageBox.alert("Error loading",arguments[3]);
7602 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7610 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7615 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619 onBeforeLoad : function(){
7621 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7626 destroy : function(){
7628 this.store.un('beforeload', this.onBeforeLoad, this);
7629 this.store.un('load', this.onLoad, this);
7630 this.store.un('loadexception', this.onLoadException, this);
7632 var um = this.el.getUpdateManager();
7633 um.un('beforeupdate', this.onBeforeLoad, this);
7634 um.un('update', this.onLoad, this);
7635 um.un('failure', this.onLoad, this);
7646 * @class Roo.bootstrap.Table
7647 * @extends Roo.bootstrap.Component
7648 * Bootstrap Table class
7649 * @cfg {String} cls table class
7650 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651 * @cfg {String} bgcolor Specifies the background color for a table
7652 * @cfg {Number} border Specifies whether the table cells should have borders or not
7653 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654 * @cfg {Number} cellspacing Specifies the space between cells
7655 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657 * @cfg {String} sortable Specifies that the table should be sortable
7658 * @cfg {String} summary Specifies a summary of the content of a table
7659 * @cfg {Number} width Specifies the width of a table
7660 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7662 * @cfg {boolean} striped Should the rows be alternative striped
7663 * @cfg {boolean} bordered Add borders to the table
7664 * @cfg {boolean} hover Add hover highlighting
7665 * @cfg {boolean} condensed Format condensed
7666 * @cfg {boolean} responsive Format condensed
7667 * @cfg {Boolean} loadMask (true|false) default false
7668 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670 * @cfg {Boolean} rowSelection (true|false) default false
7671 * @cfg {Boolean} cellSelection (true|false) default false
7672 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7674 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7675 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7679 * Create a new Table
7680 * @param {Object} config The config object
7683 Roo.bootstrap.Table = function(config){
7684 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7689 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7694 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7696 this.sm.grid = this;
7697 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698 this.sm = this.selModel;
7699 this.sm.xmodule = this.xmodule || false;
7702 if (this.cm && typeof(this.cm.config) == 'undefined') {
7703 this.colModel = new Roo.grid.ColumnModel(this.cm);
7704 this.cm = this.colModel;
7705 this.cm.xmodule = this.xmodule || false;
7708 this.store= Roo.factory(this.store, Roo.data);
7709 this.ds = this.store;
7710 this.ds.xmodule = this.xmodule || false;
7713 if (this.footer && this.store) {
7714 this.footer.dataSource = this.ds;
7715 this.footer = Roo.factory(this.footer);
7722 * Fires when a cell is clicked
7723 * @param {Roo.bootstrap.Table} this
7724 * @param {Roo.Element} el
7725 * @param {Number} rowIndex
7726 * @param {Number} columnIndex
7727 * @param {Roo.EventObject} e
7731 * @event celldblclick
7732 * Fires when a cell is double clicked
7733 * @param {Roo.bootstrap.Table} this
7734 * @param {Roo.Element} el
7735 * @param {Number} rowIndex
7736 * @param {Number} columnIndex
7737 * @param {Roo.EventObject} e
7739 "celldblclick" : true,
7742 * Fires when a row is clicked
7743 * @param {Roo.bootstrap.Table} this
7744 * @param {Roo.Element} el
7745 * @param {Number} rowIndex
7746 * @param {Roo.EventObject} e
7750 * @event rowdblclick
7751 * Fires when a row is double clicked
7752 * @param {Roo.bootstrap.Table} this
7753 * @param {Roo.Element} el
7754 * @param {Number} rowIndex
7755 * @param {Roo.EventObject} e
7757 "rowdblclick" : true,
7760 * Fires when a mouseover occur
7761 * @param {Roo.bootstrap.Table} this
7762 * @param {Roo.Element} el
7763 * @param {Number} rowIndex
7764 * @param {Number} columnIndex
7765 * @param {Roo.EventObject} e
7770 * Fires when a mouseout occur
7771 * @param {Roo.bootstrap.Table} this
7772 * @param {Roo.Element} el
7773 * @param {Number} rowIndex
7774 * @param {Number} columnIndex
7775 * @param {Roo.EventObject} e
7780 * Fires when a row is rendered, so you can change add a style to it.
7781 * @param {Roo.bootstrap.Table} this
7782 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7786 * @event rowsrendered
7787 * Fires when all the rows have been rendered
7788 * @param {Roo.bootstrap.Table} this
7790 'rowsrendered' : true,
7792 * @event contextmenu
7793 * The raw contextmenu event for the entire grid.
7794 * @param {Roo.EventObject} e
7796 "contextmenu" : true,
7798 * @event rowcontextmenu
7799 * Fires when a row is right clicked
7800 * @param {Roo.bootstrap.Table} this
7801 * @param {Number} rowIndex
7802 * @param {Roo.EventObject} e
7804 "rowcontextmenu" : true,
7806 * @event cellcontextmenu
7807 * Fires when a cell is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} rowIndex
7810 * @param {Number} cellIndex
7811 * @param {Roo.EventObject} e
7813 "cellcontextmenu" : true,
7815 * @event headercontextmenu
7816 * Fires when a header is right clicked
7817 * @param {Roo.bootstrap.Table} this
7818 * @param {Number} columnIndex
7819 * @param {Roo.EventObject} e
7821 "headercontextmenu" : true
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7851 rowSelection : false,
7852 cellSelection : false,
7855 // Roo.Element - the tbody
7857 // Roo.Element - thead element
7860 container: false, // used by gridpanel...
7866 auto_hide_footer : false,
7868 getAutoCreate : function()
7870 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7877 if (this.scrollBody) {
7878 cfg.cls += ' table-body-fixed';
7881 cfg.cls += ' table-striped';
7885 cfg.cls += ' table-hover';
7887 if (this.bordered) {
7888 cfg.cls += ' table-bordered';
7890 if (this.condensed) {
7891 cfg.cls += ' table-condensed';
7893 if (this.responsive) {
7894 cfg.cls += ' table-responsive';
7898 cfg.cls+= ' ' +this.cls;
7901 // this lot should be simplifed...
7914 ].forEach(function(k) {
7922 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7925 if(this.store || this.cm){
7926 if(this.headerShow){
7927 cfg.cn.push(this.renderHeader());
7930 cfg.cn.push(this.renderBody());
7932 if(this.footerShow){
7933 cfg.cn.push(this.renderFooter());
7935 // where does this come from?
7936 //cfg.cls+= ' TableGrid';
7939 return { cn : [ cfg ] };
7942 initEvents : function()
7944 if(!this.store || !this.cm){
7947 if (this.selModel) {
7948 this.selModel.initEvents();
7952 //Roo.log('initEvents with ds!!!!');
7954 this.mainBody = this.el.select('tbody', true).first();
7955 this.mainHead = this.el.select('thead', true).first();
7956 this.mainFoot = this.el.select('tfoot', true).first();
7962 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963 e.on('click', _this.sort, _this);
7966 this.mainBody.on("click", this.onClick, this);
7967 this.mainBody.on("dblclick", this.onDblClick, this);
7969 // why is this done????? = it breaks dialogs??
7970 //this.parent().el.setStyle('position', 'relative');
7974 this.footer.parentId = this.id;
7975 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7978 this.el.select('tfoot tr td').first().addClass('hide');
7983 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7986 this.store.on('load', this.onLoad, this);
7987 this.store.on('beforeload', this.onBeforeLoad, this);
7988 this.store.on('update', this.onUpdate, this);
7989 this.store.on('add', this.onAdd, this);
7990 this.store.on("clear", this.clear, this);
7992 this.el.on("contextmenu", this.onContextMenu, this);
7994 this.mainBody.on('scroll', this.onBodyScroll, this);
7996 this.cm.on("headerchange", this.onHeaderChange, this);
7998 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8002 onContextMenu : function(e, t)
8004 this.processEvent("contextmenu", e);
8007 processEvent : function(name, e)
8009 if (name != 'touchstart' ) {
8010 this.fireEvent(name, e);
8013 var t = e.getTarget();
8015 var cell = Roo.get(t);
8021 if(cell.findParent('tfoot', false, true)){
8025 if(cell.findParent('thead', false, true)){
8027 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028 cell = Roo.get(t).findParent('th', false, true);
8030 Roo.log("failed to find th in thead?");
8031 Roo.log(e.getTarget());
8036 var cellIndex = cell.dom.cellIndex;
8038 var ename = name == 'touchstart' ? 'click' : name;
8039 this.fireEvent("header" + ename, this, cellIndex, e);
8044 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045 cell = Roo.get(t).findParent('td', false, true);
8047 Roo.log("failed to find th in tbody?");
8048 Roo.log(e.getTarget());
8053 var row = cell.findParent('tr', false, true);
8054 var cellIndex = cell.dom.cellIndex;
8055 var rowIndex = row.dom.rowIndex - 1;
8059 this.fireEvent("row" + name, this, rowIndex, e);
8063 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069 onMouseover : function(e, el)
8071 var cell = Roo.get(el);
8077 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078 cell = cell.findParent('td', false, true);
8081 var row = cell.findParent('tr', false, true);
8082 var cellIndex = cell.dom.cellIndex;
8083 var rowIndex = row.dom.rowIndex - 1; // start from 0
8085 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8089 onMouseout : function(e, el)
8091 var cell = Roo.get(el);
8097 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098 cell = cell.findParent('td', false, true);
8101 var row = cell.findParent('tr', false, true);
8102 var cellIndex = cell.dom.cellIndex;
8103 var rowIndex = row.dom.rowIndex - 1; // start from 0
8105 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8109 onClick : function(e, el)
8111 var cell = Roo.get(el);
8113 if(!cell || (!this.cellSelection && !this.rowSelection)){
8117 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118 cell = cell.findParent('td', false, true);
8121 if(!cell || typeof(cell) == 'undefined'){
8125 var row = cell.findParent('tr', false, true);
8127 if(!row || typeof(row) == 'undefined'){
8131 var cellIndex = cell.dom.cellIndex;
8132 var rowIndex = this.getRowIndex(row);
8134 // why??? - should these not be based on SelectionModel?
8135 if(this.cellSelection){
8136 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8139 if(this.rowSelection){
8140 this.fireEvent('rowclick', this, row, rowIndex, e);
8146 onDblClick : function(e,el)
8148 var cell = Roo.get(el);
8150 if(!cell || (!this.cellSelection && !this.rowSelection)){
8154 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155 cell = cell.findParent('td', false, true);
8158 if(!cell || typeof(cell) == 'undefined'){
8162 var row = cell.findParent('tr', false, true);
8164 if(!row || typeof(row) == 'undefined'){
8168 var cellIndex = cell.dom.cellIndex;
8169 var rowIndex = this.getRowIndex(row);
8171 if(this.cellSelection){
8172 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8175 if(this.rowSelection){
8176 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8180 sort : function(e,el)
8182 var col = Roo.get(el);
8184 if(!col.hasClass('sortable')){
8188 var sort = col.attr('sort');
8191 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8195 this.store.sortInfo = {field : sort, direction : dir};
8198 Roo.log("calling footer first");
8199 this.footer.onClick('first');
8202 this.store.load({ params : { start : 0 } });
8206 renderHeader : function()
8214 this.totalWidth = 0;
8216 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8218 var config = cm.config[i];
8222 cls : 'x-hcol-' + i,
8224 html: cm.getColumnHeader(i)
8229 if(typeof(config.sortable) != 'undefined' && config.sortable){
8231 c.html = '<i class="glyphicon"></i>' + c.html;
8234 // could use BS4 hidden-..-down
8236 if(typeof(config.lgHeader) != 'undefined'){
8237 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8240 if(typeof(config.mdHeader) != 'undefined'){
8241 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8244 if(typeof(config.smHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8248 if(typeof(config.xsHeader) != 'undefined'){
8249 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8256 if(typeof(config.tooltip) != 'undefined'){
8257 c.tooltip = config.tooltip;
8260 if(typeof(config.colspan) != 'undefined'){
8261 c.colspan = config.colspan;
8264 if(typeof(config.hidden) != 'undefined' && config.hidden){
8265 c.style += ' display:none;';
8268 if(typeof(config.dataIndex) != 'undefined'){
8269 c.sort = config.dataIndex;
8274 if(typeof(config.align) != 'undefined' && config.align.length){
8275 c.style += ' text-align:' + config.align + ';';
8278 if(typeof(config.width) != 'undefined'){
8279 c.style += ' width:' + config.width + 'px;';
8280 this.totalWidth += config.width;
8282 this.totalWidth += 100; // assume minimum of 100 per column?
8285 if(typeof(config.cls) != 'undefined'){
8286 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8289 ['xs','sm','md','lg'].map(function(size){
8291 if(typeof(config[size]) == 'undefined'){
8295 if (!config[size]) { // 0 = hidden
8296 // BS 4 '0' is treated as hide that column and below.
8297 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8301 c.cls += ' col-' + size + '-' + config[size] + (
8302 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8314 renderBody : function()
8324 colspan : this.cm.getColumnCount()
8334 renderFooter : function()
8344 colspan : this.cm.getColumnCount()
8358 // Roo.log('ds onload');
8363 var ds = this.store;
8365 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367 if (_this.store.sortInfo) {
8369 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370 e.select('i', true).addClass(['glyphicon-arrow-up']);
8373 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374 e.select('i', true).addClass(['glyphicon-arrow-down']);
8379 var tbody = this.mainBody;
8381 if(ds.getCount() > 0){
8382 ds.data.each(function(d,rowIndex){
8383 var row = this.renderRow(cm, ds, rowIndex);
8385 tbody.createChild(row);
8389 if(row.cellObjects.length){
8390 Roo.each(row.cellObjects, function(r){
8391 _this.renderCellObject(r);
8398 var tfoot = this.el.select('tfoot', true).first();
8400 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8402 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8404 var total = this.ds.getTotalCount();
8406 if(this.footer.pageSize < total){
8407 this.mainFoot.show();
8411 Roo.each(this.el.select('tbody td', true).elements, function(e){
8412 e.on('mouseover', _this.onMouseover, _this);
8415 Roo.each(this.el.select('tbody td', true).elements, function(e){
8416 e.on('mouseout', _this.onMouseout, _this);
8418 this.fireEvent('rowsrendered', this);
8424 onUpdate : function(ds,record)
8426 this.refreshRow(record);
8430 onRemove : function(ds, record, index, isUpdate){
8431 if(isUpdate !== true){
8432 this.fireEvent("beforerowremoved", this, index, record);
8434 var bt = this.mainBody.dom;
8436 var rows = this.el.select('tbody > tr', true).elements;
8438 if(typeof(rows[index]) != 'undefined'){
8439 bt.removeChild(rows[index].dom);
8442 // if(bt.rows[index]){
8443 // bt.removeChild(bt.rows[index]);
8446 if(isUpdate !== true){
8447 //this.stripeRows(index);
8448 //this.syncRowHeights(index, index);
8450 this.fireEvent("rowremoved", this, index, record);
8454 onAdd : function(ds, records, rowIndex)
8456 //Roo.log('on Add called');
8457 // - note this does not handle multiple adding very well..
8458 var bt = this.mainBody.dom;
8459 for (var i =0 ; i < records.length;i++) {
8460 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461 //Roo.log(records[i]);
8462 //Roo.log(this.store.getAt(rowIndex+i));
8463 this.insertRow(this.store, rowIndex + i, false);
8470 refreshRow : function(record){
8471 var ds = this.store, index;
8472 if(typeof record == 'number'){
8474 record = ds.getAt(index);
8476 index = ds.indexOf(record);
8478 return; // should not happen - but seems to
8481 this.insertRow(ds, index, true);
8483 this.onRemove(ds, record, index+1, true);
8485 //this.syncRowHeights(index, index);
8487 this.fireEvent("rowupdated", this, index, record);
8490 insertRow : function(dm, rowIndex, isUpdate){
8493 this.fireEvent("beforerowsinserted", this, rowIndex);
8495 //var s = this.getScrollState();
8496 var row = this.renderRow(this.cm, this.store, rowIndex);
8497 // insert before rowIndex..
8498 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8502 if(row.cellObjects.length){
8503 Roo.each(row.cellObjects, function(r){
8504 _this.renderCellObject(r);
8509 this.fireEvent("rowsinserted", this, rowIndex);
8510 //this.syncRowHeights(firstRow, lastRow);
8511 //this.stripeRows(firstRow);
8518 getRowDom : function(rowIndex)
8520 var rows = this.el.select('tbody > tr', true).elements;
8522 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8525 // returns the object tree for a tr..
8528 renderRow : function(cm, ds, rowIndex)
8530 var d = ds.getAt(rowIndex);
8534 cls : 'x-row-' + rowIndex,
8538 var cellObjects = [];
8540 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541 var config = cm.config[i];
8543 var renderer = cm.getRenderer(i);
8547 if(typeof(renderer) !== 'undefined'){
8548 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8550 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551 // and are rendered into the cells after the row is rendered - using the id for the element.
8553 if(typeof(value) === 'object'){
8563 rowIndex : rowIndex,
8568 this.fireEvent('rowclass', this, rowcfg);
8572 cls : rowcfg.rowClass + ' x-col-' + i,
8574 html: (typeof(value) === 'object') ? '' : value
8581 if(typeof(config.colspan) != 'undefined'){
8582 td.colspan = config.colspan;
8585 if(typeof(config.hidden) != 'undefined' && config.hidden){
8586 td.style += ' display:none;';
8589 if(typeof(config.align) != 'undefined' && config.align.length){
8590 td.style += ' text-align:' + config.align + ';';
8592 if(typeof(config.valign) != 'undefined' && config.valign.length){
8593 td.style += ' vertical-align:' + config.valign + ';';
8596 if(typeof(config.width) != 'undefined'){
8597 td.style += ' width:' + config.width + 'px;';
8600 if(typeof(config.cursor) != 'undefined'){
8601 td.style += ' cursor:' + config.cursor + ';';
8604 if(typeof(config.cls) != 'undefined'){
8605 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8608 ['xs','sm','md','lg'].map(function(size){
8610 if(typeof(config[size]) == 'undefined'){
8616 if (!config[size]) { // 0 = hidden
8617 // BS 4 '0' is treated as hide that column and below.
8618 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8622 td.cls += ' col-' + size + '-' + config[size] + (
8623 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8633 row.cellObjects = cellObjects;
8641 onBeforeLoad : function()
8650 this.el.select('tbody', true).first().dom.innerHTML = '';
8653 * Show or hide a row.
8654 * @param {Number} rowIndex to show or hide
8655 * @param {Boolean} state hide
8657 setRowVisibility : function(rowIndex, state)
8659 var bt = this.mainBody.dom;
8661 var rows = this.el.select('tbody > tr', true).elements;
8663 if(typeof(rows[rowIndex]) == 'undefined'){
8666 rows[rowIndex].dom.style.display = state ? '' : 'none';
8670 getSelectionModel : function(){
8672 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8674 return this.selModel;
8677 * Render the Roo.bootstrap object from renderder
8679 renderCellObject : function(r)
8683 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8685 var t = r.cfg.render(r.container);
8688 Roo.each(r.cfg.cn, function(c){
8690 container: t.getChildContainer(),
8693 _this.renderCellObject(child);
8698 getRowIndex : function(row)
8702 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8713 * Returns the grid's underlying element = used by panel.Grid
8714 * @return {Element} The element
8716 getGridEl : function(){
8720 * Forces a resize - used by panel.Grid
8721 * @return {Element} The element
8723 autoSize : function()
8725 //var ctr = Roo.get(this.container.dom.parentElement);
8726 var ctr = Roo.get(this.el.dom);
8728 var thd = this.getGridEl().select('thead',true).first();
8729 var tbd = this.getGridEl().select('tbody', true).first();
8730 var tfd = this.getGridEl().select('tfoot', true).first();
8732 var cw = ctr.getWidth();
8733 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8737 tbd.setWidth(ctr.getWidth());
8738 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739 // this needs fixing for various usage - currently only hydra job advers I think..
8741 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8743 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8746 cw = Math.max(cw, this.totalWidth);
8747 this.getGridEl().select('tbody tr',true).setWidth(cw);
8749 // resize 'expandable coloumn?
8751 return; // we doe not have a view in this design..
8754 onBodyScroll: function()
8756 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8758 this.mainHead.setStyle({
8759 'position' : 'relative',
8760 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766 var scrollHeight = this.mainBody.dom.scrollHeight;
8768 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8770 var height = this.mainBody.getHeight();
8772 if(scrollHeight - height == scrollTop) {
8774 var total = this.ds.getTotalCount();
8776 if(this.footer.cursor + this.footer.pageSize < total){
8778 this.footer.ds.load({
8780 start : this.footer.cursor + this.footer.pageSize,
8781 limit : this.footer.pageSize
8791 onHeaderChange : function()
8793 var header = this.renderHeader();
8794 var table = this.el.select('table', true).first();
8796 this.mainHead.remove();
8797 this.mainHead = table.createChild(header, this.mainBody, false);
8800 onHiddenChange : function(colModel, colIndex, hidden)
8802 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8805 this.CSS.updateRule(thSelector, "display", "");
8806 this.CSS.updateRule(tdSelector, "display", "");
8809 this.CSS.updateRule(thSelector, "display", "none");
8810 this.CSS.updateRule(tdSelector, "display", "none");
8813 this.onHeaderChange();
8817 setColumnWidth: function(col_index, width)
8819 // width = "md-2 xs-2..."
8820 if(!this.colModel.config[col_index]) {
8824 var w = width.split(" ");
8826 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8828 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8831 for(var j = 0; j < w.length; j++) {
8837 var size_cls = w[j].split("-");
8839 if(!Number.isInteger(size_cls[1] * 1)) {
8843 if(!this.colModel.config[col_index][size_cls[0]]) {
8847 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8851 h_row[0].classList.replace(
8852 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853 "col-"+size_cls[0]+"-"+size_cls[1]
8856 for(var i = 0; i < rows.length; i++) {
8858 var size_cls = w[j].split("-");
8860 if(!Number.isInteger(size_cls[1] * 1)) {
8864 if(!this.colModel.config[col_index][size_cls[0]]) {
8868 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8872 rows[i].classList.replace(
8873 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874 "col-"+size_cls[0]+"-"+size_cls[1]
8878 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8893 * @class Roo.bootstrap.TableCell
8894 * @extends Roo.bootstrap.Component
8895 * Bootstrap TableCell class
8896 * @cfg {String} html cell contain text
8897 * @cfg {String} cls cell class
8898 * @cfg {String} tag cell tag (td|th) default td
8899 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900 * @cfg {String} align Aligns the content in a cell
8901 * @cfg {String} axis Categorizes cells
8902 * @cfg {String} bgcolor Specifies the background color of a cell
8903 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904 * @cfg {Number} colspan Specifies the number of columns a cell should span
8905 * @cfg {String} headers Specifies one or more header cells a cell is related to
8906 * @cfg {Number} height Sets the height of a cell
8907 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908 * @cfg {Number} rowspan Sets the number of rows a cell should span
8909 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910 * @cfg {String} valign Vertical aligns the content in a cell
8911 * @cfg {Number} width Specifies the width of a cell
8914 * Create a new TableCell
8915 * @param {Object} config The config object
8918 Roo.bootstrap.TableCell = function(config){
8919 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8942 getAutoCreate : function(){
8943 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8963 cfg.align=this.align
8969 cfg.bgcolor=this.bgcolor
8972 cfg.charoff=this.charoff
8975 cfg.colspan=this.colspan
8978 cfg.headers=this.headers
8981 cfg.height=this.height
8984 cfg.nowrap=this.nowrap
8987 cfg.rowspan=this.rowspan
8990 cfg.scope=this.scope
8993 cfg.valign=this.valign
8996 cfg.width=this.width
9015 * @class Roo.bootstrap.TableRow
9016 * @extends Roo.bootstrap.Component
9017 * Bootstrap TableRow class
9018 * @cfg {String} cls row class
9019 * @cfg {String} align Aligns the content in a table row
9020 * @cfg {String} bgcolor Specifies a background color for a table row
9021 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022 * @cfg {String} valign Vertical aligns the content in a table row
9025 * Create a new TableRow
9026 * @param {Object} config The config object
9029 Roo.bootstrap.TableRow = function(config){
9030 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9041 getAutoCreate : function(){
9042 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9052 cfg.align = this.align;
9055 cfg.bgcolor = this.bgcolor;
9058 cfg.charoff = this.charoff;
9061 cfg.valign = this.valign;
9079 * @class Roo.bootstrap.TableBody
9080 * @extends Roo.bootstrap.Component
9081 * Bootstrap TableBody class
9082 * @cfg {String} cls element class
9083 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084 * @cfg {String} align Aligns the content inside the element
9085 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9089 * Create a new TableBody
9090 * @param {Object} config The config object
9093 Roo.bootstrap.TableBody = function(config){
9094 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9105 getAutoCreate : function(){
9106 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9120 cfg.align = this.align;
9123 cfg.charoff = this.charoff;
9126 cfg.valign = this.valign;
9133 // initEvents : function()
9140 // this.store = Roo.factory(this.store, Roo.data);
9141 // this.store.on('load', this.onLoad, this);
9143 // this.store.load();
9147 // onLoad: function ()
9149 // this.fireEvent('load', this);
9159 * Ext JS Library 1.1.1
9160 * Copyright(c) 2006-2007, Ext JS, LLC.
9162 * Originally Released Under LGPL - original licence link has changed is not relivant.
9165 * <script type="text/javascript">
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9171 * @class Roo.form.Action
9172 * Internal Class used to handle form actions
9174 * @param {Roo.form.BasicForm} el The form element or its id
9175 * @param {Object} config Configuration options
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9183 this.options = options || {};
9186 * Client Validation Failed
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9191 * Server Validation Failed
9194 Roo.form.Action.SERVER_INVALID = 'server';
9196 * Connect to Server Failed
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9201 * Reading Data from Server Failed
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9206 Roo.form.Action.prototype = {
9208 failureType : undefined,
9209 response : undefined,
9213 run : function(options){
9218 success : function(response){
9223 handleResponse : function(response){
9227 // default connection failure
9228 failure : function(response){
9230 this.response = response;
9231 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232 this.form.afterAction(this, false);
9235 processResponse : function(response){
9236 this.response = response;
9237 if(!response.responseText){
9240 this.result = this.handleResponse(response);
9244 // utility functions used internally
9245 getUrl : function(appendParams){
9246 var url = this.options.url || this.form.url || this.form.el.dom.action;
9248 var p = this.getParams();
9250 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256 getMethod : function(){
9257 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9260 getParams : function(){
9261 var bp = this.form.baseParams;
9262 var p = this.options.params;
9264 if(typeof p == "object"){
9265 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266 }else if(typeof p == 'string' && bp){
9267 p += '&' + Roo.urlEncode(bp);
9270 p = Roo.urlEncode(bp);
9275 createCallback : function(){
9277 success: this.success,
9278 failure: this.failure,
9280 timeout: (this.form.timeout*1000),
9281 upload: this.form.fileUpload ? this.success : undefined
9286 Roo.form.Action.Submit = function(form, options){
9287 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9293 haveProgress : false,
9294 uploadComplete : false,
9296 // uploadProgress indicator.
9297 uploadProgress : function()
9299 if (!this.form.progressUrl) {
9303 if (!this.haveProgress) {
9304 Roo.MessageBox.progress("Uploading", "Uploading");
9306 if (this.uploadComplete) {
9307 Roo.MessageBox.hide();
9311 this.haveProgress = true;
9313 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9315 var c = new Roo.data.Connection();
9317 url : this.form.progressUrl,
9322 success : function(req){
9323 //console.log(data);
9327 rdata = Roo.decode(req.responseText)
9329 Roo.log("Invalid data from server..");
9333 if (!rdata || !rdata.success) {
9335 Roo.MessageBox.alert(Roo.encode(rdata));
9338 var data = rdata.data;
9340 if (this.uploadComplete) {
9341 Roo.MessageBox.hide();
9346 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9350 this.uploadProgress.defer(2000,this);
9353 failure: function(data) {
9354 Roo.log('progress url failed ');
9365 // run get Values on the form, so it syncs any secondary forms.
9366 this.form.getValues();
9368 var o = this.options;
9369 var method = this.getMethod();
9370 var isPost = method == 'POST';
9371 if(o.clientValidation === false || this.form.isValid()){
9373 if (this.form.progressUrl) {
9374 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375 (new Date() * 1) + '' + Math.random());
9380 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381 form:this.form.el.dom,
9382 url:this.getUrl(!isPost),
9384 params:isPost ? this.getParams() : null,
9385 isUpload: this.form.fileUpload,
9386 formData : this.form.formData
9389 this.uploadProgress();
9391 }else if (o.clientValidation !== false){ // client validation failed
9392 this.failureType = Roo.form.Action.CLIENT_INVALID;
9393 this.form.afterAction(this, false);
9397 success : function(response)
9399 this.uploadComplete= true;
9400 if (this.haveProgress) {
9401 Roo.MessageBox.hide();
9405 var result = this.processResponse(response);
9406 if(result === true || result.success){
9407 this.form.afterAction(this, true);
9411 this.form.markInvalid(result.errors);
9412 this.failureType = Roo.form.Action.SERVER_INVALID;
9414 this.form.afterAction(this, false);
9416 failure : function(response)
9418 this.uploadComplete= true;
9419 if (this.haveProgress) {
9420 Roo.MessageBox.hide();
9423 this.response = response;
9424 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425 this.form.afterAction(this, false);
9428 handleResponse : function(response){
9429 if(this.form.errorReader){
9430 var rs = this.form.errorReader.read(response);
9433 for(var i = 0, len = rs.records.length; i < len; i++) {
9434 var r = rs.records[i];
9438 if(errors.length < 1){
9442 success : rs.success,
9448 ret = Roo.decode(response.responseText);
9452 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9462 Roo.form.Action.Load = function(form, options){
9463 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464 this.reader = this.form.reader;
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9472 Roo.Ajax.request(Roo.apply(
9473 this.createCallback(), {
9474 method:this.getMethod(),
9475 url:this.getUrl(false),
9476 params:this.getParams()
9480 success : function(response){
9482 var result = this.processResponse(response);
9483 if(result === true || !result.success || !result.data){
9484 this.failureType = Roo.form.Action.LOAD_FAILURE;
9485 this.form.afterAction(this, false);
9488 this.form.clearInvalid();
9489 this.form.setValues(result.data);
9490 this.form.afterAction(this, true);
9493 handleResponse : function(response){
9494 if(this.form.reader){
9495 var rs = this.form.reader.read(response);
9496 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9498 success : rs.success,
9502 return Roo.decode(response.responseText);
9506 Roo.form.Action.ACTION_TYPES = {
9507 'load' : Roo.form.Action.Load,
9508 'submit' : Roo.form.Action.Submit
9517 * @class Roo.bootstrap.Form
9518 * @extends Roo.bootstrap.Component
9519 * Bootstrap Form class
9520 * @cfg {String} method GET | POST (default POST)
9521 * @cfg {String} labelAlign top | left (default top)
9522 * @cfg {String} align left | right - for navbars
9523 * @cfg {Boolean} loadMask load mask when submit (default true)
9528 * @param {Object} config The config object
9532 Roo.bootstrap.Form = function(config){
9534 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9536 Roo.bootstrap.Form.popover.apply();
9540 * @event clientvalidation
9541 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542 * @param {Form} this
9543 * @param {Boolean} valid true if the form has passed client-side validation
9545 clientvalidation: true,
9547 * @event beforeaction
9548 * Fires before any action is performed. Return false to cancel the action.
9549 * @param {Form} this
9550 * @param {Action} action The action to be performed
9554 * @event actionfailed
9555 * Fires when an action fails.
9556 * @param {Form} this
9557 * @param {Action} action The action that failed
9559 actionfailed : true,
9561 * @event actioncomplete
9562 * Fires when an action is completed.
9563 * @param {Form} this
9564 * @param {Action} action The action that completed
9566 actioncomplete : true
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9573 * @cfg {String} method
9574 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9579 * The URL to use for form actions if one isn't supplied in the action options.
9582 * @cfg {Boolean} fileUpload
9583 * Set to true if this form is a file upload.
9587 * @cfg {Object} baseParams
9588 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9592 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9596 * @cfg {Sting} align (left|right) for navbar forms
9601 activeAction : null,
9604 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605 * element by passing it or its id or mask the form itself by passing in true.
9608 waitMsgTarget : false,
9613 * @cfg {Boolean} errorMask (true|false) default false
9618 * @cfg {Number} maskOffset Default 100
9623 * @cfg {Boolean} maskBody
9627 getAutoCreate : function(){
9631 method : this.method || 'POST',
9632 id : this.id || Roo.id(),
9635 if (this.parent().xtype.match(/^Nav/)) {
9636 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9640 if (this.labelAlign == 'left' ) {
9641 cfg.cls += ' form-horizontal';
9647 initEvents : function()
9649 this.el.on('submit', this.onSubmit, this);
9650 // this was added as random key presses on the form where triggering form submit.
9651 this.el.on('keypress', function(e) {
9652 if (e.getCharCode() != 13) {
9655 // we might need to allow it for textareas.. and some other items.
9656 // check e.getTarget().
9658 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9662 Roo.log("keypress blocked");
9670 onSubmit : function(e){
9675 * Returns true if client-side validation on the form is successful.
9678 isValid : function(){
9679 var items = this.getItems();
9683 items.each(function(f){
9689 Roo.log('invalid field: ' + f.name);
9693 if(!target && f.el.isVisible(true)){
9699 if(this.errorMask && !valid){
9700 Roo.bootstrap.Form.popover.mask(this, target);
9707 * Returns true if any fields in this form have changed since their original load.
9710 isDirty : function(){
9712 var items = this.getItems();
9713 items.each(function(f){
9723 * Performs a predefined action (submit or load) or custom actions you define on this form.
9724 * @param {String} actionName The name of the action type
9725 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9726 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727 * accept other config options):
9729 Property Type Description
9730 ---------------- --------------- ----------------------------------------------------------------------------------
9731 url String The url for the action (defaults to the form's url)
9732 method String The form method to use (defaults to the form's method, or POST if not defined)
9733 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9735 validate the form on the client (defaults to false)
9737 * @return {BasicForm} this
9739 doAction : function(action, options){
9740 if(typeof action == 'string'){
9741 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9743 if(this.fireEvent('beforeaction', this, action) !== false){
9744 this.beforeAction(action);
9745 action.run.defer(100, action);
9751 beforeAction : function(action){
9752 var o = action.options;
9757 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9759 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9762 // not really supported yet.. ??
9764 //if(this.waitMsgTarget === true){
9765 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766 //}else if(this.waitMsgTarget){
9767 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776 afterAction : function(action, success){
9777 this.activeAction = null;
9778 var o = action.options;
9783 Roo.get(document.body).unmask();
9789 //if(this.waitMsgTarget === true){
9790 // this.el.unmask();
9791 //}else if(this.waitMsgTarget){
9792 // this.waitMsgTarget.unmask();
9794 // Roo.MessageBox.updateProgress(1);
9795 // Roo.MessageBox.hide();
9802 Roo.callback(o.success, o.scope, [this, action]);
9803 this.fireEvent('actioncomplete', this, action);
9807 // failure condition..
9808 // we have a scenario where updates need confirming.
9809 // eg. if a locking scenario exists..
9810 // we look for { errors : { needs_confirm : true }} in the response.
9812 (typeof(action.result) != 'undefined') &&
9813 (typeof(action.result.errors) != 'undefined') &&
9814 (typeof(action.result.errors.needs_confirm) != 'undefined')
9817 Roo.log("not supported yet");
9820 Roo.MessageBox.confirm(
9821 "Change requires confirmation",
9822 action.result.errorMsg,
9827 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9837 Roo.callback(o.failure, o.scope, [this, action]);
9838 // show an error message if no failed handler is set..
9839 if (!this.hasListener('actionfailed')) {
9840 Roo.log("need to add dialog support");
9842 Roo.MessageBox.alert("Error",
9843 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844 action.result.errorMsg :
9845 "Saving Failed, please check your entries or try again"
9850 this.fireEvent('actionfailed', this, action);
9855 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856 * @param {String} id The value to search for
9859 findField : function(id){
9860 var items = this.getItems();
9861 var field = items.get(id);
9863 items.each(function(f){
9864 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9871 return field || null;
9874 * Mark fields in this form invalid in bulk.
9875 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876 * @return {BasicForm} this
9878 markInvalid : function(errors){
9879 if(errors instanceof Array){
9880 for(var i = 0, len = errors.length; i < len; i++){
9881 var fieldError = errors[i];
9882 var f = this.findField(fieldError.id);
9884 f.markInvalid(fieldError.msg);
9890 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891 field.markInvalid(errors[id]);
9895 //Roo.each(this.childForms || [], function (f) {
9896 // f.markInvalid(errors);
9903 * Set values for fields in this form in bulk.
9904 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905 * @return {BasicForm} this
9907 setValues : function(values){
9908 if(values instanceof Array){ // array of objects
9909 for(var i = 0, len = values.length; i < len; i++){
9911 var f = this.findField(v.id);
9913 f.setValue(v.value);
9914 if(this.trackResetOnLoad){
9915 f.originalValue = f.getValue();
9919 }else{ // object hash
9922 if(typeof values[id] != 'function' && (field = this.findField(id))){
9924 if (field.setFromData &&
9926 field.displayField &&
9927 // combos' with local stores can
9928 // be queried via setValue()
9929 // to set their value..
9930 (field.store && !field.store.isLocal)
9934 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936 field.setFromData(sd);
9938 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9940 field.setFromData(values);
9943 field.setValue(values[id]);
9947 if(this.trackResetOnLoad){
9948 field.originalValue = field.getValue();
9954 //Roo.each(this.childForms || [], function (f) {
9955 // f.setValues(values);
9962 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963 * they are returned as an array.
9964 * @param {Boolean} asString
9967 getValues : function(asString){
9968 //if (this.childForms) {
9969 // copy values from the child forms
9970 // Roo.each(this.childForms, function (f) {
9971 // this.setValues(f.getValues());
9977 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978 if(asString === true){
9981 return Roo.urlDecode(fs);
9985 * Returns the fields in this form as an object with key/value pairs.
9986 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9989 getFieldValues : function(with_hidden)
9991 var items = this.getItems();
9993 items.each(function(f){
9999 var v = f.getValue();
10001 if (f.inputType =='radio') {
10002 if (typeof(ret[f.getName()]) == 'undefined') {
10003 ret[f.getName()] = ''; // empty..
10006 if (!f.el.dom.checked) {
10010 v = f.el.dom.value;
10014 if(f.xtype == 'MoneyField'){
10015 ret[f.currencyName] = f.getCurrency();
10018 // not sure if this supported any more..
10019 if ((typeof(v) == 'object') && f.getRawValue) {
10020 v = f.getRawValue() ; // dates..
10022 // combo boxes where name != hiddenName...
10023 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024 ret[f.name] = f.getRawValue();
10026 ret[f.getName()] = v;
10033 * Clears all invalid messages in this form.
10034 * @return {BasicForm} this
10036 clearInvalid : function(){
10037 var items = this.getItems();
10039 items.each(function(f){
10047 * Resets this form.
10048 * @return {BasicForm} this
10050 reset : function(){
10051 var items = this.getItems();
10052 items.each(function(f){
10056 Roo.each(this.childForms || [], function (f) {
10064 getItems : function()
10066 var r=new Roo.util.MixedCollection(false, function(o){
10067 return o.id || (o.id = Roo.id());
10069 var iter = function(el) {
10076 Roo.each(el.items,function(e) {
10085 hideFields : function(items)
10087 Roo.each(items, function(i){
10089 var f = this.findField(i);
10100 showFields : function(items)
10102 Roo.each(items, function(i){
10104 var f = this.findField(i);
10117 Roo.apply(Roo.bootstrap.Form, {
10133 intervalID : false,
10139 if(this.isApplied){
10144 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10150 this.maskEl.top.enableDisplayMode("block");
10151 this.maskEl.left.enableDisplayMode("block");
10152 this.maskEl.bottom.enableDisplayMode("block");
10153 this.maskEl.right.enableDisplayMode("block");
10155 this.toolTip = new Roo.bootstrap.Tooltip({
10156 cls : 'roo-form-error-popover',
10158 'left' : ['r-l', [-2,0], 'right'],
10159 'right' : ['l-r', [2,0], 'left'],
10160 'bottom' : ['tl-bl', [0,2], 'top'],
10161 'top' : [ 'bl-tl', [0,-2], 'bottom']
10165 this.toolTip.render(Roo.get(document.body));
10167 this.toolTip.el.enableDisplayMode("block");
10169 Roo.get(document.body).on('click', function(){
10173 Roo.get(document.body).on('touchstart', function(){
10177 this.isApplied = true
10180 mask : function(form, target)
10184 this.target = target;
10186 if(!this.form.errorMask || !target.el){
10190 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10192 Roo.log(scrollable);
10194 var ot = this.target.el.calcOffsetsTo(scrollable);
10196 var scrollTo = ot[1] - this.form.maskOffset;
10198 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10200 scrollable.scrollTo('top', scrollTo);
10202 var box = this.target.el.getBox();
10204 var zIndex = Roo.bootstrap.Modal.zIndex++;
10207 this.maskEl.top.setStyle('position', 'absolute');
10208 this.maskEl.top.setStyle('z-index', zIndex);
10209 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210 this.maskEl.top.setLeft(0);
10211 this.maskEl.top.setTop(0);
10212 this.maskEl.top.show();
10214 this.maskEl.left.setStyle('position', 'absolute');
10215 this.maskEl.left.setStyle('z-index', zIndex);
10216 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217 this.maskEl.left.setLeft(0);
10218 this.maskEl.left.setTop(box.y - this.padding);
10219 this.maskEl.left.show();
10221 this.maskEl.bottom.setStyle('position', 'absolute');
10222 this.maskEl.bottom.setStyle('z-index', zIndex);
10223 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224 this.maskEl.bottom.setLeft(0);
10225 this.maskEl.bottom.setTop(box.bottom + this.padding);
10226 this.maskEl.bottom.show();
10228 this.maskEl.right.setStyle('position', 'absolute');
10229 this.maskEl.right.setStyle('z-index', zIndex);
10230 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231 this.maskEl.right.setLeft(box.right + this.padding);
10232 this.maskEl.right.setTop(box.y - this.padding);
10233 this.maskEl.right.show();
10235 this.toolTip.bindEl = this.target.el;
10237 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10239 var tip = this.target.blankText;
10241 if(this.target.getValue() !== '' ) {
10243 if (this.target.invalidText.length) {
10244 tip = this.target.invalidText;
10245 } else if (this.target.regexText.length){
10246 tip = this.target.regexText;
10250 this.toolTip.show(tip);
10252 this.intervalID = window.setInterval(function() {
10253 Roo.bootstrap.Form.popover.unmask();
10256 window.onwheel = function(){ return false;};
10258 (function(){ this.isMasked = true; }).defer(500, this);
10262 unmask : function()
10264 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10268 this.maskEl.top.setStyle('position', 'absolute');
10269 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270 this.maskEl.top.hide();
10272 this.maskEl.left.setStyle('position', 'absolute');
10273 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274 this.maskEl.left.hide();
10276 this.maskEl.bottom.setStyle('position', 'absolute');
10277 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.bottom.hide();
10280 this.maskEl.right.setStyle('position', 'absolute');
10281 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.right.hide();
10284 this.toolTip.hide();
10286 this.toolTip.el.hide();
10288 window.onwheel = function(){ return true;};
10290 if(this.intervalID){
10291 window.clearInterval(this.intervalID);
10292 this.intervalID = false;
10295 this.isMasked = false;
10305 * Ext JS Library 1.1.1
10306 * Copyright(c) 2006-2007, Ext JS, LLC.
10308 * Originally Released Under LGPL - original licence link has changed is not relivant.
10311 * <script type="text/javascript">
10314 * @class Roo.form.VTypes
10315 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10318 Roo.form.VTypes = function(){
10319 // closure these in so they are only created once.
10320 var alpha = /^[a-zA-Z_]+$/;
10321 var alphanum = /^[a-zA-Z0-9_]+$/;
10322 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10325 // All these messages and functions are configurable
10328 * The function used to validate email addresses
10329 * @param {String} value The email address
10331 'email' : function(v){
10332 return email.test(v);
10335 * The error text to display when the email validation function returns false
10338 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10340 * The keystroke filter mask to be applied on email input
10343 'emailMask' : /[a-z0-9_\.\-@]/i,
10346 * The function used to validate URLs
10347 * @param {String} value The URL
10349 'url' : function(v){
10350 return url.test(v);
10353 * The error text to display when the url validation function returns false
10356 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10359 * The function used to validate alpha values
10360 * @param {String} value The value
10362 'alpha' : function(v){
10363 return alpha.test(v);
10366 * The error text to display when the alpha validation function returns false
10369 'alphaText' : 'This field should only contain letters and _',
10371 * The keystroke filter mask to be applied on alpha input
10374 'alphaMask' : /[a-z_]/i,
10377 * The function used to validate alphanumeric values
10378 * @param {String} value The value
10380 'alphanum' : function(v){
10381 return alphanum.test(v);
10384 * The error text to display when the alphanumeric validation function returns false
10387 'alphanumText' : 'This field should only contain letters, numbers and _',
10389 * The keystroke filter mask to be applied on alphanumeric input
10392 'alphanumMask' : /[a-z0-9_]/i
10402 * @class Roo.bootstrap.Input
10403 * @extends Roo.bootstrap.Component
10404 * Bootstrap Input class
10405 * @cfg {Boolean} disabled is it disabled
10406 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10407 * @cfg {String} name name of the input
10408 * @cfg {string} fieldLabel - the label associated
10409 * @cfg {string} placeholder - placeholder to put in text.
10410 * @cfg {string} before - input group add on before
10411 * @cfg {string} after - input group add on after
10412 * @cfg {string} size - (lg|sm) or leave empty..
10413 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415 * @cfg {Number} md colspan out of 12 for computer-sized screens
10416 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417 * @cfg {string} value default value of the input
10418 * @cfg {Number} labelWidth set the width of label
10419 * @cfg {Number} labellg set the width of label (1-12)
10420 * @cfg {Number} labelmd set the width of label (1-12)
10421 * @cfg {Number} labelsm set the width of label (1-12)
10422 * @cfg {Number} labelxs set the width of label (1-12)
10423 * @cfg {String} labelAlign (top|left)
10424 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426 * @cfg {String} indicatorpos (left|right) default left
10427 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10431 * @cfg {String} align (left|center|right) Default left
10432 * @cfg {Boolean} forceFeedback (true|false) Default false
10435 * Create a new Input
10436 * @param {Object} config The config object
10439 Roo.bootstrap.Input = function(config){
10441 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10446 * Fires when this field receives input focus.
10447 * @param {Roo.form.Field} this
10452 * Fires when this field loses input focus.
10453 * @param {Roo.form.Field} this
10457 * @event specialkey
10458 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10459 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460 * @param {Roo.form.Field} this
10461 * @param {Roo.EventObject} e The event object
10466 * Fires just before the field blurs if the field value has changed.
10467 * @param {Roo.form.Field} this
10468 * @param {Mixed} newValue The new value
10469 * @param {Mixed} oldValue The original value
10474 * Fires after the field has been marked as invalid.
10475 * @param {Roo.form.Field} this
10476 * @param {String} msg The validation message
10481 * Fires after the field has been validated with no errors.
10482 * @param {Roo.form.Field} this
10487 * Fires after the key up
10488 * @param {Roo.form.Field} this
10489 * @param {Roo.EventObject} e The event Object
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10497 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498 automatic validation (defaults to "keyup").
10500 validationEvent : "keyup",
10502 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10504 validateOnBlur : true,
10506 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10508 validationDelay : 250,
10510 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10512 focusClass : "x-form-focus", // not needed???
10516 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10518 invalidClass : "has-warning",
10521 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10523 validClass : "has-success",
10526 * @cfg {Boolean} hasFeedback (true|false) default true
10528 hasFeedback : true,
10531 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10533 invalidFeedbackClass : "glyphicon-warning-sign",
10536 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10538 validFeedbackClass : "glyphicon-ok",
10541 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10543 selectOnFocus : false,
10546 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10550 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10555 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10557 disableKeyFilter : false,
10560 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10564 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10568 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10570 blankText : "Please complete this mandatory field",
10573 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10577 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10579 maxLength : Number.MAX_VALUE,
10581 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10583 minLengthText : "The minimum length for this field is {0}",
10585 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10587 maxLengthText : "The maximum length for this field is {0}",
10591 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592 * If available, this function will be called only after the basic validators all return true, and will be passed the
10593 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10597 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10603 * @cfg {String} regexText -- Depricated - use Invalid Text
10608 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614 autocomplete: false,
10618 inputType : 'text',
10621 placeholder: false,
10626 preventMark: false,
10627 isFormField : true,
10630 labelAlign : false,
10633 formatedValue : false,
10634 forceFeedback : false,
10636 indicatorpos : 'left',
10646 parentLabelAlign : function()
10649 while (parent.parent()) {
10650 parent = parent.parent();
10651 if (typeof(parent.labelAlign) !='undefined') {
10652 return parent.labelAlign;
10659 getAutoCreate : function()
10661 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667 if(this.inputType != 'hidden'){
10668 cfg.cls = 'form-group' //input-group
10674 type : this.inputType,
10675 value : this.value,
10676 cls : 'form-control',
10677 placeholder : this.placeholder || '',
10678 autocomplete : this.autocomplete || 'new-password'
10680 if (this.inputType == 'file') {
10681 input.style = 'overflow:hidden'; // why not in CSS?
10684 if(this.capture.length){
10685 input.capture = this.capture;
10688 if(this.accept.length){
10689 input.accept = this.accept + "/*";
10693 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10696 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697 input.maxLength = this.maxLength;
10700 if (this.disabled) {
10701 input.disabled=true;
10704 if (this.readOnly) {
10705 input.readonly=true;
10709 input.name = this.name;
10713 input.cls += ' input-' + this.size;
10717 ['xs','sm','md','lg'].map(function(size){
10718 if (settings[size]) {
10719 cfg.cls += ' col-' + size + '-' + settings[size];
10723 var inputblock = input;
10727 cls: 'glyphicon form-control-feedback'
10730 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10733 cls : 'has-feedback',
10741 if (this.before || this.after) {
10744 cls : 'input-group',
10748 if (this.before && typeof(this.before) == 'string') {
10750 inputblock.cn.push({
10752 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10756 if (this.before && typeof(this.before) == 'object') {
10757 this.before = Roo.factory(this.before);
10759 inputblock.cn.push({
10761 cls : 'roo-input-before input-group-prepend input-group-' +
10762 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10766 inputblock.cn.push(input);
10768 if (this.after && typeof(this.after) == 'string') {
10769 inputblock.cn.push({
10771 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10775 if (this.after && typeof(this.after) == 'object') {
10776 this.after = Roo.factory(this.after);
10778 inputblock.cn.push({
10780 cls : 'roo-input-after input-group-append input-group-' +
10781 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10785 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786 inputblock.cls += ' has-feedback';
10787 inputblock.cn.push(feedback);
10792 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793 tooltip : 'This field is required'
10795 if (this.allowBlank ) {
10796 indicator.style = this.allowBlank ? ' display:none' : '';
10798 if (align ==='left' && this.fieldLabel.length) {
10800 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10807 cls : 'control-label col-form-label',
10808 html : this.fieldLabel
10819 var labelCfg = cfg.cn[1];
10820 var contentCfg = cfg.cn[2];
10822 if(this.indicatorpos == 'right'){
10827 cls : 'control-label col-form-label',
10831 html : this.fieldLabel
10845 labelCfg = cfg.cn[0];
10846 contentCfg = cfg.cn[1];
10850 if(this.labelWidth > 12){
10851 labelCfg.style = "width: " + this.labelWidth + 'px';
10854 if(this.labelWidth < 13 && this.labelmd == 0){
10855 this.labelmd = this.labelWidth;
10858 if(this.labellg > 0){
10859 labelCfg.cls += ' col-lg-' + this.labellg;
10860 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10863 if(this.labelmd > 0){
10864 labelCfg.cls += ' col-md-' + this.labelmd;
10865 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10868 if(this.labelsm > 0){
10869 labelCfg.cls += ' col-sm-' + this.labelsm;
10870 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10873 if(this.labelxs > 0){
10874 labelCfg.cls += ' col-xs-' + this.labelxs;
10875 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10879 } else if ( this.fieldLabel.length) {
10886 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887 tooltip : 'This field is required',
10888 style : this.allowBlank ? ' display:none' : ''
10892 //cls : 'input-group-addon',
10893 html : this.fieldLabel
10901 if(this.indicatorpos == 'right'){
10906 //cls : 'input-group-addon',
10907 html : this.fieldLabel
10912 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913 tooltip : 'This field is required',
10914 style : this.allowBlank ? ' display:none' : ''
10934 if (this.parentType === 'Navbar' && this.parent().bar) {
10935 cfg.cls += ' navbar-form';
10938 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939 // on BS4 we do this only if not form
10940 cfg.cls += ' navbar-form';
10948 * return the real input element.
10950 inputEl: function ()
10952 return this.el.select('input.form-control',true).first();
10955 tooltipEl : function()
10957 return this.inputEl();
10960 indicatorEl : function()
10962 if (Roo.bootstrap.version == 4) {
10963 return false; // not enabled in v4 yet.
10966 var indicator = this.el.select('i.roo-required-indicator',true).first();
10976 setDisabled : function(v)
10978 var i = this.inputEl().dom;
10980 i.removeAttribute('disabled');
10984 i.setAttribute('disabled','true');
10986 initEvents : function()
10989 this.inputEl().on("keydown" , this.fireKey, this);
10990 this.inputEl().on("focus", this.onFocus, this);
10991 this.inputEl().on("blur", this.onBlur, this);
10993 this.inputEl().relayEvent('keyup', this);
10995 this.indicator = this.indicatorEl();
10997 if(this.indicator){
10998 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11001 // reference to original value for reset
11002 this.originalValue = this.getValue();
11003 //Roo.form.TextField.superclass.initEvents.call(this);
11004 if(this.validationEvent == 'keyup'){
11005 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006 this.inputEl().on('keyup', this.filterValidation, this);
11008 else if(this.validationEvent !== false){
11009 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11012 if(this.selectOnFocus){
11013 this.on("focus", this.preFocus, this);
11016 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017 this.inputEl().on("keypress", this.filterKeys, this);
11019 this.inputEl().relayEvent('keypress', this);
11022 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11023 this.el.on("click", this.autoSize, this);
11026 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11030 if (typeof(this.before) == 'object') {
11031 this.before.render(this.el.select('.roo-input-before',true).first());
11033 if (typeof(this.after) == 'object') {
11034 this.after.render(this.el.select('.roo-input-after',true).first());
11037 this.inputEl().on('change', this.onChange, this);
11040 filterValidation : function(e){
11041 if(!e.isNavKeyPress()){
11042 this.validationTask.delay(this.validationDelay);
11046 * Validates the field value
11047 * @return {Boolean} True if the value is valid, else false
11049 validate : function(){
11050 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051 if(this.disabled || this.validateValue(this.getRawValue())){
11056 this.markInvalid();
11062 * Validates a value according to the field's validation rules and marks the field as invalid
11063 * if the validation fails
11064 * @param {Mixed} value The value to validate
11065 * @return {Boolean} True if the value is valid, else false
11067 validateValue : function(value)
11069 if(this.getVisibilityEl().hasClass('hidden')){
11073 if(value.length < 1) { // if it's blank
11074 if(this.allowBlank){
11080 if(value.length < this.minLength){
11083 if(value.length > this.maxLength){
11087 var vt = Roo.form.VTypes;
11088 if(!vt[this.vtype](value, this)){
11092 if(typeof this.validator == "function"){
11093 var msg = this.validator(value);
11097 if (typeof(msg) == 'string') {
11098 this.invalidText = msg;
11102 if(this.regex && !this.regex.test(value)){
11110 fireKey : function(e){
11111 //Roo.log('field ' + e.getKey());
11112 if(e.isNavKeyPress()){
11113 this.fireEvent("specialkey", this, e);
11116 focus : function (selectText){
11118 this.inputEl().focus();
11119 if(selectText === true){
11120 this.inputEl().dom.select();
11126 onFocus : function(){
11127 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128 // this.el.addClass(this.focusClass);
11130 if(!this.hasFocus){
11131 this.hasFocus = true;
11132 this.startValue = this.getValue();
11133 this.fireEvent("focus", this);
11137 beforeBlur : Roo.emptyFn,
11141 onBlur : function(){
11143 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144 //this.el.removeClass(this.focusClass);
11146 this.hasFocus = false;
11147 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11150 var v = this.getValue();
11151 if(String(v) !== String(this.startValue)){
11152 this.fireEvent('change', this, v, this.startValue);
11154 this.fireEvent("blur", this);
11157 onChange : function(e)
11159 var v = this.getValue();
11160 if(String(v) !== String(this.startValue)){
11161 this.fireEvent('change', this, v, this.startValue);
11167 * Resets the current field value to the originally loaded value and clears any validation messages
11169 reset : function(){
11170 this.setValue(this.originalValue);
11174 * Returns the name of the field
11175 * @return {Mixed} name The name field
11177 getName: function(){
11181 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11182 * @return {Mixed} value The field value
11184 getValue : function(){
11186 var v = this.inputEl().getValue();
11191 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11192 * @return {Mixed} value The field value
11194 getRawValue : function(){
11195 var v = this.inputEl().getValue();
11201 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11202 * @param {Mixed} value The value to set
11204 setRawValue : function(v){
11205 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11208 selectText : function(start, end){
11209 var v = this.getRawValue();
11211 start = start === undefined ? 0 : start;
11212 end = end === undefined ? v.length : end;
11213 var d = this.inputEl().dom;
11214 if(d.setSelectionRange){
11215 d.setSelectionRange(start, end);
11216 }else if(d.createTextRange){
11217 var range = d.createTextRange();
11218 range.moveStart("character", start);
11219 range.moveEnd("character", v.length-end);
11226 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11227 * @param {Mixed} value The value to set
11229 setValue : function(v){
11232 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238 processValue : function(value){
11239 if(this.stripCharsRe){
11240 var newValue = value.replace(this.stripCharsRe, '');
11241 if(newValue !== value){
11242 this.setRawValue(newValue);
11249 preFocus : function(){
11251 if(this.selectOnFocus){
11252 this.inputEl().dom.select();
11255 filterKeys : function(e){
11256 var k = e.getKey();
11257 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11260 var c = e.getCharCode(), cc = String.fromCharCode(c);
11261 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11264 if(!this.maskRe.test(cc)){
11269 * Clear any invalid styles/messages for this field
11271 clearInvalid : function(){
11273 if(!this.el || this.preventMark){ // not rendered
11278 this.el.removeClass([this.invalidClass, 'is-invalid']);
11280 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11282 var feedback = this.el.select('.form-control-feedback', true).first();
11285 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11290 if(this.indicator){
11291 this.indicator.removeClass('visible');
11292 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11295 this.fireEvent('valid', this);
11299 * Mark this field as valid
11301 markValid : function()
11303 if(!this.el || this.preventMark){ // not rendered...
11307 this.el.removeClass([this.invalidClass, this.validClass]);
11308 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11310 var feedback = this.el.select('.form-control-feedback', true).first();
11313 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11316 if(this.indicator){
11317 this.indicator.removeClass('visible');
11318 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11326 if(this.allowBlank && !this.getRawValue().length){
11329 if (Roo.bootstrap.version == 3) {
11330 this.el.addClass(this.validClass);
11332 this.inputEl().addClass('is-valid');
11335 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11337 var feedback = this.el.select('.form-control-feedback', true).first();
11340 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11346 this.fireEvent('valid', this);
11350 * Mark this field as invalid
11351 * @param {String} msg The validation message
11353 markInvalid : function(msg)
11355 if(!this.el || this.preventMark){ // not rendered
11359 this.el.removeClass([this.invalidClass, this.validClass]);
11360 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11362 var feedback = this.el.select('.form-control-feedback', true).first();
11365 this.el.select('.form-control-feedback', true).first().removeClass(
11366 [this.invalidFeedbackClass, this.validFeedbackClass]);
11373 if(this.allowBlank && !this.getRawValue().length){
11377 if(this.indicator){
11378 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379 this.indicator.addClass('visible');
11381 if (Roo.bootstrap.version == 3) {
11382 this.el.addClass(this.invalidClass);
11384 this.inputEl().addClass('is-invalid');
11389 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11391 var feedback = this.el.select('.form-control-feedback', true).first();
11394 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11396 if(this.getValue().length || this.forceFeedback){
11397 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11404 this.fireEvent('invalid', this, msg);
11407 SafariOnKeyDown : function(event)
11409 // this is a workaround for a password hang bug on chrome/ webkit.
11410 if (this.inputEl().dom.type != 'password') {
11414 var isSelectAll = false;
11416 if(this.inputEl().dom.selectionEnd > 0){
11417 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11419 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420 event.preventDefault();
11425 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11427 event.preventDefault();
11428 // this is very hacky as keydown always get's upper case.
11430 var cc = String.fromCharCode(event.getCharCode());
11431 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11435 adjustWidth : function(tag, w){
11436 tag = tag.toLowerCase();
11437 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439 if(tag == 'input'){
11442 if(tag == 'textarea'){
11445 }else if(Roo.isOpera){
11446 if(tag == 'input'){
11449 if(tag == 'textarea'){
11457 setFieldLabel : function(v)
11459 if(!this.rendered){
11463 if(this.indicatorEl()){
11464 var ar = this.el.select('label > span',true);
11466 if (ar.elements.length) {
11467 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468 this.fieldLabel = v;
11472 var br = this.el.select('label',true);
11474 if(br.elements.length) {
11475 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476 this.fieldLabel = v;
11480 Roo.log('Cannot Found any of label > span || label in input');
11484 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485 this.fieldLabel = v;
11500 * @class Roo.bootstrap.TextArea
11501 * @extends Roo.bootstrap.Input
11502 * Bootstrap TextArea class
11503 * @cfg {Number} cols Specifies the visible width of a text area
11504 * @cfg {Number} rows Specifies the visible number of lines in a text area
11505 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507 * @cfg {string} html text
11510 * Create a new TextArea
11511 * @param {Object} config The config object
11514 Roo.bootstrap.TextArea = function(config){
11515 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11529 getAutoCreate : function(){
11531 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537 if(this.inputType != 'hidden'){
11538 cfg.cls = 'form-group' //input-group
11546 value : this.value || '',
11547 html: this.html || '',
11548 cls : 'form-control',
11549 placeholder : this.placeholder || ''
11553 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554 input.maxLength = this.maxLength;
11558 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11562 input.cols = this.cols;
11565 if (this.readOnly) {
11566 input.readonly = true;
11570 input.name = this.name;
11574 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11578 ['xs','sm','md','lg'].map(function(size){
11579 if (settings[size]) {
11580 cfg.cls += ' col-' + size + '-' + settings[size];
11584 var inputblock = input;
11586 if(this.hasFeedback && !this.allowBlank){
11590 cls: 'glyphicon form-control-feedback'
11594 cls : 'has-feedback',
11603 if (this.before || this.after) {
11606 cls : 'input-group',
11610 inputblock.cn.push({
11612 cls : 'input-group-addon',
11617 inputblock.cn.push(input);
11619 if(this.hasFeedback && !this.allowBlank){
11620 inputblock.cls += ' has-feedback';
11621 inputblock.cn.push(feedback);
11625 inputblock.cn.push({
11627 cls : 'input-group-addon',
11634 if (align ==='left' && this.fieldLabel.length) {
11639 cls : 'control-label',
11640 html : this.fieldLabel
11651 if(this.labelWidth > 12){
11652 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11655 if(this.labelWidth < 13 && this.labelmd == 0){
11656 this.labelmd = this.labelWidth;
11659 if(this.labellg > 0){
11660 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11664 if(this.labelmd > 0){
11665 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11669 if(this.labelsm > 0){
11670 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11674 if(this.labelxs > 0){
11675 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11679 } else if ( this.fieldLabel.length) {
11684 //cls : 'input-group-addon',
11685 html : this.fieldLabel
11703 if (this.disabled) {
11704 input.disabled=true;
11711 * return the real textarea element.
11713 inputEl: function ()
11715 return this.el.select('textarea.form-control',true).first();
11719 * Clear any invalid styles/messages for this field
11721 clearInvalid : function()
11724 if(!this.el || this.preventMark){ // not rendered
11728 var label = this.el.select('label', true).first();
11729 var icon = this.el.select('i.fa-star', true).first();
11734 this.el.removeClass( this.validClass);
11735 this.inputEl().removeClass('is-invalid');
11737 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11739 var feedback = this.el.select('.form-control-feedback', true).first();
11742 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11747 this.fireEvent('valid', this);
11751 * Mark this field as valid
11753 markValid : function()
11755 if(!this.el || this.preventMark){ // not rendered
11759 this.el.removeClass([this.invalidClass, this.validClass]);
11760 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11762 var feedback = this.el.select('.form-control-feedback', true).first();
11765 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11768 if(this.disabled || this.allowBlank){
11772 var label = this.el.select('label', true).first();
11773 var icon = this.el.select('i.fa-star', true).first();
11778 if (Roo.bootstrap.version == 3) {
11779 this.el.addClass(this.validClass);
11781 this.inputEl().addClass('is-valid');
11785 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11787 var feedback = this.el.select('.form-control-feedback', true).first();
11790 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11796 this.fireEvent('valid', this);
11800 * Mark this field as invalid
11801 * @param {String} msg The validation message
11803 markInvalid : function(msg)
11805 if(!this.el || this.preventMark){ // not rendered
11809 this.el.removeClass([this.invalidClass, this.validClass]);
11810 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11812 var feedback = this.el.select('.form-control-feedback', true).first();
11815 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11818 if(this.disabled || this.allowBlank){
11822 var label = this.el.select('label', true).first();
11823 var icon = this.el.select('i.fa-star', true).first();
11825 if(!this.getValue().length && label && !icon){
11826 this.el.createChild({
11828 cls : 'text-danger fa fa-lg fa-star',
11829 tooltip : 'This field is required',
11830 style : 'margin-right:5px;'
11834 if (Roo.bootstrap.version == 3) {
11835 this.el.addClass(this.invalidClass);
11837 this.inputEl().addClass('is-invalid');
11840 // fixme ... this may be depricated need to test..
11841 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11843 var feedback = this.el.select('.form-control-feedback', true).first();
11846 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11848 if(this.getValue().length || this.forceFeedback){
11849 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11856 this.fireEvent('invalid', this, msg);
11864 * trigger field - base class for combo..
11869 * @class Roo.bootstrap.TriggerField
11870 * @extends Roo.bootstrap.Input
11871 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874 * for which you can provide a custom implementation. For example:
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11881 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11884 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11888 * Create a new TriggerField.
11889 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890 * to the base TextField)
11892 Roo.bootstrap.TriggerField = function(config){
11893 this.mimicing = false;
11894 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11899 * @cfg {String} triggerClass A CSS class to apply to the trigger
11902 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11907 * @cfg {Boolean} removable (true|false) special filter default false
11911 /** @cfg {Boolean} grow @hide */
11912 /** @cfg {Number} growMin @hide */
11913 /** @cfg {Number} growMax @hide */
11919 autoSize: Roo.emptyFn,
11923 deferHeight : true,
11926 actionMode : 'wrap',
11931 getAutoCreate : function(){
11933 var align = this.labelAlign || this.parentLabelAlign();
11938 cls: 'form-group' //input-group
11945 type : this.inputType,
11946 cls : 'form-control',
11947 autocomplete: 'new-password',
11948 placeholder : this.placeholder || ''
11952 input.name = this.name;
11955 input.cls += ' input-' + this.size;
11958 if (this.disabled) {
11959 input.disabled=true;
11962 var inputblock = input;
11964 if(this.hasFeedback && !this.allowBlank){
11968 cls: 'glyphicon form-control-feedback'
11971 if(this.removable && !this.editable ){
11973 cls : 'has-feedback',
11979 cls : 'roo-combo-removable-btn close'
11986 cls : 'has-feedback',
11995 if(this.removable && !this.editable ){
11997 cls : 'roo-removable',
12003 cls : 'roo-combo-removable-btn close'
12010 if (this.before || this.after) {
12013 cls : 'input-group',
12017 inputblock.cn.push({
12019 cls : 'input-group-addon input-group-prepend input-group-text',
12024 inputblock.cn.push(input);
12026 if(this.hasFeedback && !this.allowBlank){
12027 inputblock.cls += ' has-feedback';
12028 inputblock.cn.push(feedback);
12032 inputblock.cn.push({
12034 cls : 'input-group-addon input-group-append input-group-text',
12043 var ibwrap = inputblock;
12048 cls: 'roo-select2-choices',
12052 cls: 'roo-select2-search-field',
12064 cls: 'roo-select2-container input-group',
12069 cls: 'form-hidden-field'
12075 if(!this.multiple && this.showToggleBtn){
12081 if (this.caret != false) {
12084 cls: 'fa fa-' + this.caret
12091 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12093 Roo.bootstrap.version == 3 ? caret : '',
12096 cls: 'combobox-clear',
12110 combobox.cls += ' roo-select2-container-multi';
12114 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115 tooltip : 'This field is required'
12117 if (Roo.bootstrap.version == 4) {
12120 style : 'display:none'
12125 if (align ==='left' && this.fieldLabel.length) {
12127 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12134 cls : 'control-label',
12135 html : this.fieldLabel
12147 var labelCfg = cfg.cn[1];
12148 var contentCfg = cfg.cn[2];
12150 if(this.indicatorpos == 'right'){
12155 cls : 'control-label',
12159 html : this.fieldLabel
12173 labelCfg = cfg.cn[0];
12174 contentCfg = cfg.cn[1];
12177 if(this.labelWidth > 12){
12178 labelCfg.style = "width: " + this.labelWidth + 'px';
12181 if(this.labelWidth < 13 && this.labelmd == 0){
12182 this.labelmd = this.labelWidth;
12185 if(this.labellg > 0){
12186 labelCfg.cls += ' col-lg-' + this.labellg;
12187 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12190 if(this.labelmd > 0){
12191 labelCfg.cls += ' col-md-' + this.labelmd;
12192 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12195 if(this.labelsm > 0){
12196 labelCfg.cls += ' col-sm-' + this.labelsm;
12197 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12200 if(this.labelxs > 0){
12201 labelCfg.cls += ' col-xs-' + this.labelxs;
12202 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12205 } else if ( this.fieldLabel.length) {
12206 // Roo.log(" label");
12211 //cls : 'input-group-addon',
12212 html : this.fieldLabel
12220 if(this.indicatorpos == 'right'){
12228 html : this.fieldLabel
12242 // Roo.log(" no label && no align");
12249 ['xs','sm','md','lg'].map(function(size){
12250 if (settings[size]) {
12251 cfg.cls += ' col-' + size + '-' + settings[size];
12262 onResize : function(w, h){
12263 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 // if(typeof w == 'number'){
12265 // var x = w - this.trigger.getWidth();
12266 // this.inputEl().setWidth(this.adjustWidth('input', x));
12267 // this.trigger.setStyle('left', x+'px');
12272 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12275 getResizeEl : function(){
12276 return this.inputEl();
12280 getPositionEl : function(){
12281 return this.inputEl();
12285 alignErrorIcon : function(){
12286 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12290 initEvents : function(){
12294 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296 if(!this.multiple && this.showToggleBtn){
12297 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298 if(this.hideTrigger){
12299 this.trigger.setDisplayed(false);
12301 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12305 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12308 if(this.removable && !this.editable && !this.tickable){
12309 var close = this.closeTriggerEl();
12312 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313 close.on('click', this.removeBtnClick, this, close);
12317 //this.trigger.addClassOnOver('x-form-trigger-over');
12318 //this.trigger.addClassOnClick('x-form-trigger-click');
12321 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12325 closeTriggerEl : function()
12327 var close = this.el.select('.roo-combo-removable-btn', true).first();
12328 return close ? close : false;
12331 removeBtnClick : function(e, h, el)
12333 e.preventDefault();
12335 if(this.fireEvent("remove", this) !== false){
12337 this.fireEvent("afterremove", this)
12341 createList : function()
12343 this.list = Roo.get(document.body).createChild({
12344 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345 cls: 'typeahead typeahead-long dropdown-menu shadow',
12346 style: 'display:none'
12349 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12354 initTrigger : function(){
12359 onDestroy : function(){
12361 this.trigger.removeAllListeners();
12362 // this.trigger.remove();
12365 // this.wrap.remove();
12367 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12371 onFocus : function(){
12372 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12374 if(!this.mimicing){
12375 this.wrap.addClass('x-trigger-wrap-focus');
12376 this.mimicing = true;
12377 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378 if(this.monitorTab){
12379 this.el.on("keydown", this.checkTab, this);
12386 checkTab : function(e){
12387 if(e.getKey() == e.TAB){
12388 this.triggerBlur();
12393 onBlur : function(){
12398 mimicBlur : function(e, t){
12400 if(!this.wrap.contains(t) && this.validateBlur()){
12401 this.triggerBlur();
12407 triggerBlur : function(){
12408 this.mimicing = false;
12409 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410 if(this.monitorTab){
12411 this.el.un("keydown", this.checkTab, this);
12413 //this.wrap.removeClass('x-trigger-wrap-focus');
12414 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12418 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419 validateBlur : function(e, t){
12424 onDisable : function(){
12425 this.inputEl().dom.disabled = true;
12426 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12428 // this.wrap.addClass('x-item-disabled');
12433 onEnable : function(){
12434 this.inputEl().dom.disabled = false;
12435 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12437 // this.el.removeClass('x-item-disabled');
12442 onShow : function(){
12443 var ae = this.getActionEl();
12446 ae.dom.style.display = '';
12447 ae.dom.style.visibility = 'visible';
12453 onHide : function(){
12454 var ae = this.getActionEl();
12455 ae.dom.style.display = 'none';
12459 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12460 * by an implementing function.
12462 * @param {EventObject} e
12464 onTriggerClick : Roo.emptyFn
12472 * @class Roo.bootstrap.CardUploader
12473 * @extends Roo.bootstrap.Button
12474 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475 * @cfg {Number} errorTimeout default 3000
12476 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12477 * @cfg {Array} html The button text.
12481 * Create a new CardUploader
12482 * @param {Object} config The config object
12485 Roo.bootstrap.CardUploader = function(config){
12489 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12492 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12502 errorTimeout : 3000,
12506 fileCollection : false,
12509 getAutoCreate : function()
12513 cls :'form-group' ,
12518 //cls : 'input-group-addon',
12519 html : this.fieldLabel
12526 value : this.value,
12527 cls : 'd-none form-control'
12532 multiple : 'multiple',
12534 cls : 'd-none roo-card-upload-selector'
12538 cls : 'roo-card-uploader-button-container w-100 mb-2'
12541 cls : 'card-columns roo-card-uploader-container'
12551 getChildContainer : function() /// what children are added to.
12553 return this.containerEl;
12556 getButtonContainer : function() /// what children are added to.
12558 return this.el.select(".roo-card-uploader-button-container").first();
12561 initEvents : function()
12564 Roo.bootstrap.Input.prototype.initEvents.call(this);
12568 xns: Roo.bootstrap,
12571 container_method : 'getButtonContainer' ,
12572 html : this.html, // fix changable?
12575 'click' : function(btn, e) {
12584 this.urlAPI = (window.createObjectURL && window) ||
12585 (window.URL && URL.revokeObjectURL && URL) ||
12586 (window.webkitURL && webkitURL);
12591 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12593 this.selectorEl.on('change', this.onFileSelected, this);
12596 this.images.forEach(function(img) {
12599 this.images = false;
12601 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607 onClick : function(e)
12609 e.preventDefault();
12611 this.selectorEl.dom.click();
12615 onFileSelected : function(e)
12617 e.preventDefault();
12619 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12623 Roo.each(this.selectorEl.dom.files, function(file){
12624 this.addFile(file);
12633 addFile : function(file)
12636 if(typeof(file) === 'string'){
12637 throw "Add file by name?"; // should not happen
12641 if(!file || !this.urlAPI){
12651 var url = _this.urlAPI.createObjectURL( file);
12654 id : Roo.bootstrap.CardUploader.ID--,
12655 is_uploaded : false,
12658 mimetype : file.type,
12665 addCard : function (data)
12667 // hidden input element?
12668 // if the file is not an image...
12669 //then we need to use something other that and header_image
12674 xns : Roo.bootstrap,
12675 xtype : 'CardFooter',
12678 xns : Roo.bootstrap,
12684 xns : Roo.bootstrap,
12686 html : String.format("<small>{0}</small>", data.title),
12687 cls : 'col-11 text-left',
12692 click : function() {
12693 this.downloadCard(data.id)
12699 xns : Roo.bootstrap,
12707 click : function() {
12708 t.removeCard(data.id)
12720 var cn = this.addxtype(
12723 xns : Roo.bootstrap,
12726 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12728 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12733 initEvents : function() {
12734 Roo.bootstrap.Card.prototype.initEvents.call(this);
12735 this.imgEl = this.el.select('.card-img-top').first();
12737 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738 this.imgEl.set({ 'pointer' : 'cursor' });
12747 // dont' really need ot update items.
12748 // this.items.push(cn);
12749 this.fileCollection.add(cn);
12750 this.updateInput();
12753 removeCard : function(id)
12756 var card = this.fileCollection.get(id);
12757 card.data.is_deleted = 1;
12758 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759 this.fileCollection.remove(card);
12760 //this.items = this.items.filter(function(e) { return e != card });
12761 // dont' really need ot update items.
12762 card.el.dom.parentNode.removeChild(card.el.dom);
12767 this.fileCollection.each(function(card) {
12768 card.el.dom.parentNode.removeChild(card.el.dom);
12770 this.fileCollection.clear();
12771 this.updateInput();
12774 updateInput : function()
12777 this.fileCollection.each(function(e) {
12781 this.inputEl().dom.value = JSON.stringify(data);
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12790 * Ext JS Library 1.1.1
12791 * Copyright(c) 2006-2007, Ext JS, LLC.
12793 * Originally Released Under LGPL - original licence link has changed is not relivant.
12796 * <script type="text/javascript">
12801 * @class Roo.data.SortTypes
12803 * Defines the default sorting (casting?) comparison functions used when sorting data.
12805 Roo.data.SortTypes = {
12807 * Default sort that does nothing
12808 * @param {Mixed} s The value being converted
12809 * @return {Mixed} The comparison value
12811 none : function(s){
12816 * The regular expression used to strip tags
12820 stripTagsRE : /<\/?[^>]+>/gi,
12823 * Strips all HTML tags to sort on text only
12824 * @param {Mixed} s The value being converted
12825 * @return {String} The comparison value
12827 asText : function(s){
12828 return String(s).replace(this.stripTagsRE, "");
12832 * Strips all HTML tags to sort on text only - Case insensitive
12833 * @param {Mixed} s The value being converted
12834 * @return {String} The comparison value
12836 asUCText : function(s){
12837 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12841 * Case insensitive string
12842 * @param {Mixed} s The value being converted
12843 * @return {String} The comparison value
12845 asUCString : function(s) {
12846 return String(s).toUpperCase();
12851 * @param {Mixed} s The value being converted
12852 * @return {Number} The comparison value
12854 asDate : function(s) {
12858 if(s instanceof Date){
12859 return s.getTime();
12861 return Date.parse(String(s));
12866 * @param {Mixed} s The value being converted
12867 * @return {Float} The comparison value
12869 asFloat : function(s) {
12870 var val = parseFloat(String(s).replace(/,/g, ""));
12879 * @param {Mixed} s The value being converted
12880 * @return {Number} The comparison value
12882 asInt : function(s) {
12883 var val = parseInt(String(s).replace(/,/g, ""));
12891 * Ext JS Library 1.1.1
12892 * Copyright(c) 2006-2007, Ext JS, LLC.
12894 * Originally Released Under LGPL - original licence link has changed is not relivant.
12897 * <script type="text/javascript">
12901 * @class Roo.data.Record
12902 * Instances of this class encapsulate both record <em>definition</em> information, and record
12903 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904 * to access Records cached in an {@link Roo.data.Store} object.<br>
12906 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12910 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12912 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913 * {@link #create}. The parameters are the same.
12914 * @param {Array} data An associative Array of data values keyed by the field name.
12915 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917 * not specified an integer id is generated.
12919 Roo.data.Record = function(data, id){
12920 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12925 * Generate a constructor for a specific record layout.
12926 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928 * Each field definition object may contain the following properties: <ul>
12929 * <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,
12930 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933 * is being used, then this is a string containing the javascript expression to reference the data relative to
12934 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936 * this may be omitted.</p></li>
12937 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938 * <ul><li>auto (Default, implies no conversion)</li>
12943 * <li>date</li></ul></p></li>
12944 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947 * by the Reader into an object that will be stored in the Record. It is passed the
12948 * following parameters:<ul>
12949 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12951 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12953 * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955 {name: 'title', mapping: 'topic_title'},
12956 {name: 'author', mapping: 'username'},
12957 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959 {name: 'lastPoster', mapping: 'user2'},
12960 {name: 'excerpt', mapping: 'post_text'}
12963 var myNewRecord = new TopicRecord({
12964 title: 'Do my job please',
12967 lastPost: new Date(),
12968 lastPoster: 'Animal',
12969 excerpt: 'No way dude!'
12971 myStore.add(myNewRecord);
12976 Roo.data.Record.create = function(o){
12977 var f = function(){
12978 f.superclass.constructor.apply(this, arguments);
12980 Roo.extend(f, Roo.data.Record);
12981 var p = f.prototype;
12982 p.fields = new Roo.util.MixedCollection(false, function(field){
12985 for(var i = 0, len = o.length; i < len; i++){
12986 p.fields.add(new Roo.data.Field(o[i]));
12988 f.getField = function(name){
12989 return p.fields.get(name);
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12999 Roo.data.Record.prototype = {
13001 * Readonly flag - true if this record has been modified.
13010 join : function(store){
13011 this.store = store;
13015 * Set the named field to the specified value.
13016 * @param {String} name The name of the field to set.
13017 * @param {Object} value The value to set the field to.
13019 set : function(name, value){
13020 if(this.data[name] == value){
13024 if(!this.modified){
13025 this.modified = {};
13027 if(typeof this.modified[name] == 'undefined'){
13028 this.modified[name] = this.data[name];
13030 this.data[name] = value;
13031 if(!this.editing && this.store){
13032 this.store.afterEdit(this);
13037 * Get the value of the named field.
13038 * @param {String} name The name of the field to get the value of.
13039 * @return {Object} The value of the field.
13041 get : function(name){
13042 return this.data[name];
13046 beginEdit : function(){
13047 this.editing = true;
13048 this.modified = {};
13052 cancelEdit : function(){
13053 this.editing = false;
13054 delete this.modified;
13058 endEdit : function(){
13059 this.editing = false;
13060 if(this.dirty && this.store){
13061 this.store.afterEdit(this);
13066 * Usually called by the {@link Roo.data.Store} which owns the Record.
13067 * Rejects all changes made to the Record since either creation, or the last commit operation.
13068 * Modified fields are reverted to their original values.
13070 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071 * of reject operations.
13073 reject : function(){
13074 var m = this.modified;
13076 if(typeof m[n] != "function"){
13077 this.data[n] = m[n];
13080 this.dirty = false;
13081 delete this.modified;
13082 this.editing = false;
13084 this.store.afterReject(this);
13089 * Usually called by the {@link Roo.data.Store} which owns the Record.
13090 * Commits all changes made to the Record since either creation, or the last commit operation.
13092 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093 * of commit operations.
13095 commit : function(){
13096 this.dirty = false;
13097 delete this.modified;
13098 this.editing = false;
13100 this.store.afterCommit(this);
13105 hasError : function(){
13106 return this.error != null;
13110 clearError : function(){
13115 * Creates a copy of this record.
13116 * @param {String} id (optional) A new record id if you don't want to use this record's id
13119 copy : function(newId) {
13120 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13124 * Ext JS Library 1.1.1
13125 * Copyright(c) 2006-2007, Ext JS, LLC.
13127 * Originally Released Under LGPL - original licence link has changed is not relivant.
13130 * <script type="text/javascript">
13136 * @class Roo.data.Store
13137 * @extends Roo.util.Observable
13138 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13141 * 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
13142 * has no knowledge of the format of the data returned by the Proxy.<br>
13144 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145 * instances from the data object. These records are cached and made available through accessor functions.
13147 * Creates a new Store.
13148 * @param {Object} config A config object containing the objects needed for the Store to access data,
13149 * and read the data into Records.
13151 Roo.data.Store = function(config){
13152 this.data = new Roo.util.MixedCollection(false);
13153 this.data.getKey = function(o){
13156 this.baseParams = {};
13158 this.paramNames = {
13163 "multisort" : "_multisort"
13166 if(config && config.data){
13167 this.inlineData = config.data;
13168 delete config.data;
13171 Roo.apply(this, config);
13173 if(this.reader){ // reader passed
13174 this.reader = Roo.factory(this.reader, Roo.data);
13175 this.reader.xmodule = this.xmodule || false;
13176 if(!this.recordType){
13177 this.recordType = this.reader.recordType;
13179 if(this.reader.onMetaChange){
13180 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13184 if(this.recordType){
13185 this.fields = this.recordType.prototype.fields;
13187 this.modified = [];
13191 * @event datachanged
13192 * Fires when the data cache has changed, and a widget which is using this Store
13193 * as a Record cache should refresh its view.
13194 * @param {Store} this
13196 datachanged : true,
13198 * @event metachange
13199 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200 * @param {Store} this
13201 * @param {Object} meta The JSON metadata
13206 * Fires when Records have been added to the Store
13207 * @param {Store} this
13208 * @param {Roo.data.Record[]} records The array of Records added
13209 * @param {Number} index The index at which the record(s) were added
13214 * Fires when a Record has been removed from the Store
13215 * @param {Store} this
13216 * @param {Roo.data.Record} record The Record that was removed
13217 * @param {Number} index The index at which the record was removed
13222 * Fires when a Record has been updated
13223 * @param {Store} this
13224 * @param {Roo.data.Record} record The Record that was updated
13225 * @param {String} operation The update operation being performed. Value may be one of:
13227 Roo.data.Record.EDIT
13228 Roo.data.Record.REJECT
13229 Roo.data.Record.COMMIT
13235 * Fires when the data cache has been cleared.
13236 * @param {Store} this
13240 * @event beforeload
13241 * Fires before a request is made for a new data object. If the beforeload handler returns false
13242 * the load action will be canceled.
13243 * @param {Store} this
13244 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13248 * @event beforeloadadd
13249 * Fires after a new set of Records has been loaded.
13250 * @param {Store} this
13251 * @param {Roo.data.Record[]} records The Records that were loaded
13252 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13254 beforeloadadd : true,
13257 * Fires after a new set of Records has been loaded, before they are added to the store.
13258 * @param {Store} this
13259 * @param {Roo.data.Record[]} records The Records that were loaded
13260 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261 * @params {Object} return from reader
13265 * @event loadexception
13266 * Fires if an exception occurs in the Proxy during loading.
13267 * Called with the signature of the Proxy's "loadexception" event.
13268 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13271 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272 * @param {Object} load options
13273 * @param {Object} jsonData from your request (normally this contains the Exception)
13275 loadexception : true
13279 this.proxy = Roo.factory(this.proxy, Roo.data);
13280 this.proxy.xmodule = this.xmodule || false;
13281 this.relayEvents(this.proxy, ["loadexception"]);
13283 this.sortToggle = {};
13284 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13286 Roo.data.Store.superclass.constructor.call(this);
13288 if(this.inlineData){
13289 this.loadData(this.inlineData);
13290 delete this.inlineData;
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13296 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13297 * without a remote query - used by combo/forms at present.
13301 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13304 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13307 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13311 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312 * on any HTTP request
13315 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13318 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13322 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13325 remoteSort : false,
13328 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329 * loaded or when a record is removed. (defaults to false).
13331 pruneModifiedRecords : false,
13334 lastOptions : null,
13337 * Add Records to the Store and fires the add event.
13338 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13340 add : function(records){
13341 records = [].concat(records);
13342 for(var i = 0, len = records.length; i < len; i++){
13343 records[i].join(this);
13345 var index = this.data.length;
13346 this.data.addAll(records);
13347 this.fireEvent("add", this, records, index);
13351 * Remove a Record from the Store and fires the remove event.
13352 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13354 remove : function(record){
13355 var index = this.data.indexOf(record);
13356 this.data.removeAt(index);
13358 if(this.pruneModifiedRecords){
13359 this.modified.remove(record);
13361 this.fireEvent("remove", this, record, index);
13365 * Remove all Records from the Store and fires the clear event.
13367 removeAll : function(){
13369 if(this.pruneModifiedRecords){
13370 this.modified = [];
13372 this.fireEvent("clear", this);
13376 * Inserts Records to the Store at the given index and fires the add event.
13377 * @param {Number} index The start index at which to insert the passed Records.
13378 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13380 insert : function(index, records){
13381 records = [].concat(records);
13382 for(var i = 0, len = records.length; i < len; i++){
13383 this.data.insert(index, records[i]);
13384 records[i].join(this);
13386 this.fireEvent("add", this, records, index);
13390 * Get the index within the cache of the passed Record.
13391 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392 * @return {Number} The index of the passed Record. Returns -1 if not found.
13394 indexOf : function(record){
13395 return this.data.indexOf(record);
13399 * Get the index within the cache of the Record with the passed id.
13400 * @param {String} id The id of the Record to find.
13401 * @return {Number} The index of the Record. Returns -1 if not found.
13403 indexOfId : function(id){
13404 return this.data.indexOfKey(id);
13408 * Get the Record with the specified id.
13409 * @param {String} id The id of the Record to find.
13410 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13412 getById : function(id){
13413 return this.data.key(id);
13417 * Get the Record at the specified index.
13418 * @param {Number} index The index of the Record to find.
13419 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13421 getAt : function(index){
13422 return this.data.itemAt(index);
13426 * Returns a range of Records between specified indices.
13427 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429 * @return {Roo.data.Record[]} An array of Records
13431 getRange : function(start, end){
13432 return this.data.getRange(start, end);
13436 storeOptions : function(o){
13437 o = Roo.apply({}, o);
13440 this.lastOptions = o;
13444 * Loads the Record cache from the configured Proxy using the configured Reader.
13446 * If using remote paging, then the first load call must specify the <em>start</em>
13447 * and <em>limit</em> properties in the options.params property to establish the initial
13448 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13450 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451 * and this call will return before the new data has been loaded. Perform any post-processing
13452 * in a callback function, or in a "load" event handler.</strong>
13454 * @param {Object} options An object containing properties which control loading options:<ul>
13455 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457 * passed the following arguments:<ul>
13458 * <li>r : Roo.data.Record[]</li>
13459 * <li>options: Options object from the load call</li>
13460 * <li>success: Boolean success indicator</li></ul></li>
13461 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13465 load : function(options){
13466 options = options || {};
13467 if(this.fireEvent("beforeload", this, options) !== false){
13468 this.storeOptions(options);
13469 var p = Roo.apply(options.params || {}, this.baseParams);
13470 // if meta was not loaded from remote source.. try requesting it.
13471 if (!this.reader.metaFromRemote) {
13472 p._requestMeta = 1;
13474 if(this.sortInfo && this.remoteSort){
13475 var pn = this.paramNames;
13476 p[pn["sort"]] = this.sortInfo.field;
13477 p[pn["dir"]] = this.sortInfo.direction;
13479 if (this.multiSort) {
13480 var pn = this.paramNames;
13481 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13484 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13489 * Reloads the Record cache from the configured Proxy using the configured Reader and
13490 * the options from the last load operation performed.
13491 * @param {Object} options (optional) An object containing properties which may override the options
13492 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493 * the most recently used options are reused).
13495 reload : function(options){
13496 this.load(Roo.applyIf(options||{}, this.lastOptions));
13500 // Called as a callback by the Reader during a load operation.
13501 loadRecords : function(o, options, success){
13502 if(!o || success === false){
13503 if(success !== false){
13504 this.fireEvent("load", this, [], options, o);
13506 if(options.callback){
13507 options.callback.call(options.scope || this, [], options, false);
13511 // if data returned failure - throw an exception.
13512 if (o.success === false) {
13513 // show a message if no listener is registered.
13514 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13517 // loadmask wil be hooked into this..
13518 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13521 var r = o.records, t = o.totalRecords || r.length;
13523 this.fireEvent("beforeloadadd", this, r, options, o);
13525 if(!options || options.add !== true){
13526 if(this.pruneModifiedRecords){
13527 this.modified = [];
13529 for(var i = 0, len = r.length; i < len; i++){
13533 this.data = this.snapshot;
13534 delete this.snapshot;
13537 this.data.addAll(r);
13538 this.totalLength = t;
13540 this.fireEvent("datachanged", this);
13542 this.totalLength = Math.max(t, this.data.length+r.length);
13546 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13548 var e = new Roo.data.Record({});
13550 e.set(this.parent.displayField, this.parent.emptyTitle);
13551 e.set(this.parent.valueField, '');
13556 this.fireEvent("load", this, r, options, o);
13557 if(options.callback){
13558 options.callback.call(options.scope || this, r, options, true);
13564 * Loads data from a passed data block. A Reader which understands the format of the data
13565 * must have been configured in the constructor.
13566 * @param {Object} data The data block from which to read the Records. The format of the data expected
13567 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13570 loadData : function(o, append){
13571 var r = this.reader.readRecords(o);
13572 this.loadRecords(r, {add: append}, true);
13576 * using 'cn' the nested child reader read the child array into it's child stores.
13577 * @param {Object} rec The record with a 'children array
13579 loadDataFromChildren : function(rec)
13581 this.loadData(this.reader.toLoadData(rec));
13586 * Gets the number of cached records.
13588 * <em>If using paging, this may not be the total size of the dataset. If the data object
13589 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590 * the data set size</em>
13592 getCount : function(){
13593 return this.data.length || 0;
13597 * Gets the total number of records in the dataset as returned by the server.
13599 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600 * the dataset size</em>
13602 getTotalCount : function(){
13603 return this.totalLength || 0;
13607 * Returns the sort state of the Store as an object with two properties:
13609 field {String} The name of the field by which the Records are sorted
13610 direction {String} The sort order, "ASC" or "DESC"
13613 getSortState : function(){
13614 return this.sortInfo;
13618 applySort : function(){
13619 if(this.sortInfo && !this.remoteSort){
13620 var s = this.sortInfo, f = s.field;
13621 var st = this.fields.get(f).sortType;
13622 var fn = function(r1, r2){
13623 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13626 this.data.sort(s.direction, fn);
13627 if(this.snapshot && this.snapshot != this.data){
13628 this.snapshot.sort(s.direction, fn);
13634 * Sets the default sort column and order to be used by the next load operation.
13635 * @param {String} fieldName The name of the field to sort by.
13636 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13638 setDefaultSort : function(field, dir){
13639 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13643 * Sort the Records.
13644 * If remote sorting is used, the sort is performed on the server, and the cache is
13645 * reloaded. If local sorting is used, the cache is sorted internally.
13646 * @param {String} fieldName The name of the field to sort by.
13647 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13649 sort : function(fieldName, dir){
13650 var f = this.fields.get(fieldName);
13652 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13654 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13660 this.sortToggle[f.name] = dir;
13661 this.sortInfo = {field: f.name, direction: dir};
13662 if(!this.remoteSort){
13664 this.fireEvent("datachanged", this);
13666 this.load(this.lastOptions);
13671 * Calls the specified function for each of the Records in the cache.
13672 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673 * Returning <em>false</em> aborts and exits the iteration.
13674 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13676 each : function(fn, scope){
13677 this.data.each(fn, scope);
13681 * Gets all records modified since the last commit. Modified records are persisted across load operations
13682 * (e.g., during paging).
13683 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13685 getModifiedRecords : function(){
13686 return this.modified;
13690 createFilterFn : function(property, value, anyMatch){
13691 if(!value.exec){ // not a regex
13692 value = String(value);
13693 if(value.length == 0){
13696 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13698 return function(r){
13699 return value.test(r.data[property]);
13704 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705 * @param {String} property A field on your records
13706 * @param {Number} start The record index to start at (defaults to 0)
13707 * @param {Number} end The last record index to include (defaults to length - 1)
13708 * @return {Number} The sum
13710 sum : function(property, start, end){
13711 var rs = this.data.items, v = 0;
13712 start = start || 0;
13713 end = (end || end === 0) ? end : rs.length-1;
13715 for(var i = start; i <= end; i++){
13716 v += (rs[i].data[property] || 0);
13722 * Filter the records by a specified property.
13723 * @param {String} field A field on your records
13724 * @param {String/RegExp} value Either a string that the field
13725 * should start with or a RegExp to test against the field
13726 * @param {Boolean} anyMatch True to match any part not just the beginning
13728 filter : function(property, value, anyMatch){
13729 var fn = this.createFilterFn(property, value, anyMatch);
13730 return fn ? this.filterBy(fn) : this.clearFilter();
13734 * Filter by a function. The specified function will be called with each
13735 * record in this data source. If the function returns true the record is included,
13736 * otherwise it is filtered.
13737 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738 * @param {Object} scope (optional) The scope of the function (defaults to this)
13740 filterBy : function(fn, scope){
13741 this.snapshot = this.snapshot || this.data;
13742 this.data = this.queryBy(fn, scope||this);
13743 this.fireEvent("datachanged", this);
13747 * Query the records by a specified property.
13748 * @param {String} field A field on your records
13749 * @param {String/RegExp} value Either a string that the field
13750 * should start with or a RegExp to test against the field
13751 * @param {Boolean} anyMatch True to match any part not just the beginning
13752 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13754 query : function(property, value, anyMatch){
13755 var fn = this.createFilterFn(property, value, anyMatch);
13756 return fn ? this.queryBy(fn) : this.data.clone();
13760 * Query by a function. The specified function will be called with each
13761 * record in this data source. If the function returns true the record is included
13763 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764 * @param {Object} scope (optional) The scope of the function (defaults to this)
13765 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13767 queryBy : function(fn, scope){
13768 var data = this.snapshot || this.data;
13769 return data.filterBy(fn, scope||this);
13773 * Collects unique values for a particular dataIndex from this store.
13774 * @param {String} dataIndex The property to collect
13775 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777 * @return {Array} An array of the unique values
13779 collect : function(dataIndex, allowNull, bypassFilter){
13780 var d = (bypassFilter === true && this.snapshot) ?
13781 this.snapshot.items : this.data.items;
13782 var v, sv, r = [], l = {};
13783 for(var i = 0, len = d.length; i < len; i++){
13784 v = d[i].data[dataIndex];
13786 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13795 * Revert to a view of the Record cache with no filtering applied.
13796 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13798 clearFilter : function(suppressEvent){
13799 if(this.snapshot && this.snapshot != this.data){
13800 this.data = this.snapshot;
13801 delete this.snapshot;
13802 if(suppressEvent !== true){
13803 this.fireEvent("datachanged", this);
13809 afterEdit : function(record){
13810 if(this.modified.indexOf(record) == -1){
13811 this.modified.push(record);
13813 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13817 afterReject : function(record){
13818 this.modified.remove(record);
13819 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13823 afterCommit : function(record){
13824 this.modified.remove(record);
13825 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13829 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13832 commitChanges : function(){
13833 var m = this.modified.slice(0);
13834 this.modified = [];
13835 for(var i = 0, len = m.length; i < len; i++){
13841 * Cancel outstanding changes on all changed records.
13843 rejectChanges : function(){
13844 var m = this.modified.slice(0);
13845 this.modified = [];
13846 for(var i = 0, len = m.length; i < len; i++){
13851 onMetaChange : function(meta, rtype, o){
13852 this.recordType = rtype;
13853 this.fields = rtype.prototype.fields;
13854 delete this.snapshot;
13855 this.sortInfo = meta.sortInfo || this.sortInfo;
13856 this.modified = [];
13857 this.fireEvent('metachange', this, this.reader.meta);
13860 moveIndex : function(data, type)
13862 var index = this.indexOf(data);
13864 var newIndex = index + type;
13868 this.insert(newIndex, data);
13873 * Ext JS Library 1.1.1
13874 * Copyright(c) 2006-2007, Ext JS, LLC.
13876 * Originally Released Under LGPL - original licence link has changed is not relivant.
13879 * <script type="text/javascript">
13883 * @class Roo.data.SimpleStore
13884 * @extends Roo.data.Store
13885 * Small helper class to make creating Stores from Array data easier.
13886 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887 * @cfg {Array} fields An array of field definition objects, or field name strings.
13888 * @cfg {Object} an existing reader (eg. copied from another store)
13889 * @cfg {Array} data The multi-dimensional array of data
13891 * @param {Object} config
13893 Roo.data.SimpleStore = function(config)
13895 Roo.data.SimpleStore.superclass.constructor.call(this, {
13897 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13900 Roo.data.Record.create(config.fields)
13902 proxy : new Roo.data.MemoryProxy(config.data)
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13908 * Ext JS Library 1.1.1
13909 * Copyright(c) 2006-2007, Ext JS, LLC.
13911 * Originally Released Under LGPL - original licence link has changed is not relivant.
13914 * <script type="text/javascript">
13919 * @extends Roo.data.Store
13920 * @class Roo.data.JsonStore
13921 * Small helper class to make creating Stores for JSON data easier. <br/>
13923 var store = new Roo.data.JsonStore({
13924 url: 'get-images.php',
13926 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13929 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930 * JsonReader and HttpProxy (unless inline data is provided).</b>
13931 * @cfg {Array} fields An array of field definition objects, or field name strings.
13933 * @param {Object} config
13935 Roo.data.JsonStore = function(c){
13936 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938 reader: new Roo.data.JsonReader(c, c.fields)
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13943 * Ext JS Library 1.1.1
13944 * Copyright(c) 2006-2007, Ext JS, LLC.
13946 * Originally Released Under LGPL - original licence link has changed is not relivant.
13949 * <script type="text/javascript">
13953 Roo.data.Field = function(config){
13954 if(typeof config == "string"){
13955 config = {name: config};
13957 Roo.apply(this, config);
13960 this.type = "auto";
13963 var st = Roo.data.SortTypes;
13964 // named sortTypes are supported, here we look them up
13965 if(typeof this.sortType == "string"){
13966 this.sortType = st[this.sortType];
13969 // set default sortType for strings and dates
13970 if(!this.sortType){
13973 this.sortType = st.asUCString;
13976 this.sortType = st.asDate;
13979 this.sortType = st.none;
13984 var stripRe = /[\$,%]/g;
13986 // prebuilt conversion function for this field, instead of
13987 // switching every time we're reading a value
13989 var cv, dateFormat = this.dateFormat;
13994 cv = function(v){ return v; };
13997 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14001 return v !== undefined && v !== null && v !== '' ?
14002 parseInt(String(v).replace(stripRe, ""), 10) : '';
14007 return v !== undefined && v !== null && v !== '' ?
14008 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14013 cv = function(v){ return v === true || v === "true" || v == 1; };
14020 if(v instanceof Date){
14024 if(dateFormat == "timestamp"){
14025 return new Date(v*1000);
14027 return Date.parseDate(v, dateFormat);
14029 var parsed = Date.parse(v);
14030 return parsed ? new Date(parsed) : null;
14039 Roo.data.Field.prototype = {
14047 * Ext JS Library 1.1.1
14048 * Copyright(c) 2006-2007, Ext JS, LLC.
14050 * Originally Released Under LGPL - original licence link has changed is not relivant.
14053 * <script type="text/javascript">
14056 // Base class for reading structured data from a data source. This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14060 * @class Roo.data.DataReader
14061 * Base class for reading structured data from a data source. This class is intended to be
14062 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14065 Roo.data.DataReader = function(meta, recordType){
14069 this.recordType = recordType instanceof Array ?
14070 Roo.data.Record.create(recordType) : recordType;
14073 Roo.data.DataReader.prototype = {
14076 readerType : 'Data',
14078 * Create an empty record
14079 * @param {Object} data (optional) - overlay some values
14080 * @return {Roo.data.Record} record created.
14082 newRow : function(d) {
14084 this.recordType.prototype.fields.each(function(c) {
14086 case 'int' : da[c.name] = 0; break;
14087 case 'date' : da[c.name] = new Date(); break;
14088 case 'float' : da[c.name] = 0.0; break;
14089 case 'boolean' : da[c.name] = false; break;
14090 default : da[c.name] = ""; break;
14094 return new this.recordType(Roo.apply(da, d));
14100 * Ext JS Library 1.1.1
14101 * Copyright(c) 2006-2007, Ext JS, LLC.
14103 * Originally Released Under LGPL - original licence link has changed is not relivant.
14106 * <script type="text/javascript">
14110 * @class Roo.data.DataProxy
14111 * @extends Roo.data.Observable
14112 * This class is an abstract base class for implementations which provide retrieval of
14113 * unformatted data objects.<br>
14115 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116 * (of the appropriate type which knows how to parse the data object) to provide a block of
14117 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14119 * Custom implementations must implement the load method as described in
14120 * {@link Roo.data.HttpProxy#load}.
14122 Roo.data.DataProxy = function(){
14125 * @event beforeload
14126 * Fires before a network request is made to retrieve a data object.
14127 * @param {Object} This DataProxy object.
14128 * @param {Object} params The params parameter to the load function.
14133 * Fires before the load method's callback is called.
14134 * @param {Object} This DataProxy object.
14135 * @param {Object} o The data object.
14136 * @param {Object} arg The callback argument object passed to the load function.
14140 * @event loadexception
14141 * Fires if an Exception occurs during data retrieval.
14142 * @param {Object} This DataProxy object.
14143 * @param {Object} o The data object.
14144 * @param {Object} arg The callback argument object passed to the load function.
14145 * @param {Object} e The Exception.
14147 loadexception : true
14149 Roo.data.DataProxy.superclass.constructor.call(this);
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14155 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14159 * Ext JS Library 1.1.1
14160 * Copyright(c) 2006-2007, Ext JS, LLC.
14162 * Originally Released Under LGPL - original licence link has changed is not relivant.
14165 * <script type="text/javascript">
14168 * @class Roo.data.MemoryProxy
14169 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170 * to the Reader when its load method is called.
14172 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14174 Roo.data.MemoryProxy = function(data){
14178 Roo.data.MemoryProxy.superclass.constructor.call(this);
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14185 * Load data from the requested source (in this case an in-memory
14186 * data object passed to the constructor), read the data object into
14187 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188 * process that block using the passed callback.
14189 * @param {Object} params This parameter is not used by the MemoryProxy class.
14190 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191 * object into a block of Roo.data.Records.
14192 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193 * The function must be passed <ul>
14194 * <li>The Record block object</li>
14195 * <li>The "arg" argument from the load function</li>
14196 * <li>A boolean success indicator</li>
14198 * @param {Object} scope The scope in which to call the callback
14199 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14201 load : function(params, reader, callback, scope, arg){
14202 params = params || {};
14205 result = reader.readRecords(params.data ? params.data :this.data);
14207 this.fireEvent("loadexception", this, arg, null, e);
14208 callback.call(scope, null, arg, false);
14211 callback.call(scope, result, arg, true);
14215 update : function(params, records){
14220 * Ext JS Library 1.1.1
14221 * Copyright(c) 2006-2007, Ext JS, LLC.
14223 * Originally Released Under LGPL - original licence link has changed is not relivant.
14226 * <script type="text/javascript">
14229 * @class Roo.data.HttpProxy
14230 * @extends Roo.data.DataProxy
14231 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232 * configured to reference a certain URL.<br><br>
14234 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235 * from which the running page was served.<br><br>
14237 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14239 * Be aware that to enable the browser to parse an XML document, the server must set
14240 * the Content-Type header in the HTTP response to "text/xml".
14242 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244 * will be used to make the request.
14246 Roo.data.HttpProxy = function(conn){
14247 Roo.data.HttpProxy.superclass.constructor.call(this);
14248 // is conn a conn config or a real conn?
14250 this.useAjax = !conn || !conn.events;
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255 // thse are take from connection...
14258 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14261 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262 * extra parameters to each request made by this object. (defaults to undefined)
14265 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266 * to each request made by this object. (defaults to undefined)
14269 * @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)
14272 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14275 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14285 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287 * a finer-grained basis than the DataProxy events.
14289 getConnection : function(){
14290 return this.useAjax ? Roo.Ajax : this.conn;
14294 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296 * process that block using the passed callback.
14297 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298 * for the request to the remote server.
14299 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300 * object into a block of Roo.data.Records.
14301 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302 * The function must be passed <ul>
14303 * <li>The Record block object</li>
14304 * <li>The "arg" argument from the load function</li>
14305 * <li>A boolean success indicator</li>
14307 * @param {Object} scope The scope in which to call the callback
14308 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14310 load : function(params, reader, callback, scope, arg){
14311 if(this.fireEvent("beforeload", this, params) !== false){
14313 params : params || {},
14315 callback : callback,
14320 callback : this.loadResponse,
14324 Roo.applyIf(o, this.conn);
14325 if(this.activeRequest){
14326 Roo.Ajax.abort(this.activeRequest);
14328 this.activeRequest = Roo.Ajax.request(o);
14330 this.conn.request(o);
14333 callback.call(scope||this, null, arg, false);
14338 loadResponse : function(o, success, response){
14339 delete this.activeRequest;
14341 this.fireEvent("loadexception", this, o, response);
14342 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14347 result = o.reader.read(response);
14349 this.fireEvent("loadexception", this, o, response, e);
14350 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14354 this.fireEvent("load", this, o, o.request.arg);
14355 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14359 update : function(dataSet){
14364 updateResponse : function(dataSet){
14369 * Ext JS Library 1.1.1
14370 * Copyright(c) 2006-2007, Ext JS, LLC.
14372 * Originally Released Under LGPL - original licence link has changed is not relivant.
14375 * <script type="text/javascript">
14379 * @class Roo.data.ScriptTagProxy
14380 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381 * other than the originating domain of the running page.<br><br>
14383 * <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
14384 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14386 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387 * source code that is used as the source inside a <script> tag.<br><br>
14389 * In order for the browser to process the returned data, the server must wrap the data object
14390 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392 * depending on whether the callback name was passed:
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14399 response.setContentType("text/javascript");
14401 response.setContentType("application/x-json");
14403 Writer out = response.getWriter();
14405 out.write(cb + "(");
14407 out.print(dataBlock.toJsonString());
14414 * @param {Object} config A configuration object.
14416 Roo.data.ScriptTagProxy = function(config){
14417 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418 Roo.apply(this, config);
14419 this.head = document.getElementsByTagName("head")[0];
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14426 * @cfg {String} url The URL from which to request the data object.
14429 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14433 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434 * the server the name of the callback function set up by the load call to process the returned data object.
14435 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436 * javascript output which calls this named function passing the data object as its only parameter.
14438 callbackParam : "callback",
14440 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441 * name to the request.
14446 * Load data from the configured URL, read the data object into
14447 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448 * process that block using the passed callback.
14449 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450 * for the request to the remote server.
14451 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452 * object into a block of Roo.data.Records.
14453 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454 * The function must be passed <ul>
14455 * <li>The Record block object</li>
14456 * <li>The "arg" argument from the load function</li>
14457 * <li>A boolean success indicator</li>
14459 * @param {Object} scope The scope in which to call the callback
14460 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14462 load : function(params, reader, callback, scope, arg){
14463 if(this.fireEvent("beforeload", this, params) !== false){
14465 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14467 var url = this.url;
14468 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14470 url += "&_dc=" + (new Date().getTime());
14472 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14475 cb : "stcCallback"+transId,
14476 scriptId : "stcScript"+transId,
14480 callback : callback,
14486 window[trans.cb] = function(o){
14487 conn.handleResponse(o, trans);
14490 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14492 if(this.autoAbort !== false){
14496 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14498 var script = document.createElement("script");
14499 script.setAttribute("src", url);
14500 script.setAttribute("type", "text/javascript");
14501 script.setAttribute("id", trans.scriptId);
14502 this.head.appendChild(script);
14504 this.trans = trans;
14506 callback.call(scope||this, null, arg, false);
14511 isLoading : function(){
14512 return this.trans ? true : false;
14516 * Abort the current server request.
14518 abort : function(){
14519 if(this.isLoading()){
14520 this.destroyTrans(this.trans);
14525 destroyTrans : function(trans, isLoaded){
14526 this.head.removeChild(document.getElementById(trans.scriptId));
14527 clearTimeout(trans.timeoutId);
14529 window[trans.cb] = undefined;
14531 delete window[trans.cb];
14534 // if hasn't been loaded, wait for load to remove it to prevent script error
14535 window[trans.cb] = function(){
14536 window[trans.cb] = undefined;
14538 delete window[trans.cb];
14545 handleResponse : function(o, trans){
14546 this.trans = false;
14547 this.destroyTrans(trans, true);
14550 result = trans.reader.readRecords(o);
14552 this.fireEvent("loadexception", this, o, trans.arg, e);
14553 trans.callback.call(trans.scope||window, null, trans.arg, false);
14556 this.fireEvent("load", this, o, trans.arg);
14557 trans.callback.call(trans.scope||window, result, trans.arg, true);
14561 handleFailure : function(trans){
14562 this.trans = false;
14563 this.destroyTrans(trans, false);
14564 this.fireEvent("loadexception", this, null, trans.arg);
14565 trans.callback.call(trans.scope||window, null, trans.arg, false);
14569 * Ext JS Library 1.1.1
14570 * Copyright(c) 2006-2007, Ext JS, LLC.
14572 * Originally Released Under LGPL - original licence link has changed is not relivant.
14575 * <script type="text/javascript">
14579 * @class Roo.data.JsonReader
14580 * @extends Roo.data.DataReader
14581 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582 * based on mappings in a provided Roo.data.Record constructor.
14584 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585 * in the reply previously.
14590 var RecordDef = Roo.data.Record.create([
14591 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14592 {name: 'occupation'} // This field will use "occupation" as the mapping.
14594 var myReader = new Roo.data.JsonReader({
14595 totalProperty: "results", // The property which contains the total dataset size (optional)
14596 root: "rows", // The property which contains an Array of row objects
14597 id: "id" // The property within each row object that provides an ID for the record (optional)
14601 * This would consume a JSON file like this:
14603 { 'results': 2, 'rows': [
14604 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14608 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610 * paged from the remote server.
14611 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612 * @cfg {String} root name of the property which contains the Array of row objects.
14613 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614 * @cfg {Array} fields Array of field definition objects
14616 * Create a new JsonReader
14617 * @param {Object} meta Metadata configuration options
14618 * @param {Object} recordType Either an Array of field definition objects,
14619 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14621 Roo.data.JsonReader = function(meta, recordType){
14624 // set some defaults:
14625 Roo.applyIf(meta, {
14626 totalProperty: 'total',
14627 successProperty : 'success',
14632 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14636 readerType : 'Json',
14639 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14640 * Used by Store query builder to append _requestMeta to params.
14643 metaFromRemote : false,
14645 * This method is only used by a DataProxy which has retrieved data from a remote server.
14646 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647 * @return {Object} data A data block which is used by an Roo.data.Store object as
14648 * a cache of Roo.data.Records.
14650 read : function(response){
14651 var json = response.responseText;
14653 var o = /* eval:var:o */ eval("("+json+")");
14655 throw {message: "JsonReader.read: Json object not found"};
14661 this.metaFromRemote = true;
14662 this.meta = o.metaData;
14663 this.recordType = Roo.data.Record.create(o.metaData.fields);
14664 this.onMetaChange(this.meta, this.recordType, o);
14666 return this.readRecords(o);
14669 // private function a store will implement
14670 onMetaChange : function(meta, recordType, o){
14677 simpleAccess: function(obj, subsc) {
14684 getJsonAccessor: function(){
14686 return function(expr) {
14688 return(re.test(expr))
14689 ? new Function("obj", "return obj." + expr)
14694 return Roo.emptyFn;
14699 * Create a data block containing Roo.data.Records from an XML document.
14700 * @param {Object} o An object which contains an Array of row objects in the property specified
14701 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702 * which contains the total size of the dataset.
14703 * @return {Object} data A data block which is used by an Roo.data.Store object as
14704 * a cache of Roo.data.Records.
14706 readRecords : function(o){
14708 * After any data loads, the raw JSON data is available for further custom processing.
14712 var s = this.meta, Record = this.recordType,
14713 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14715 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14717 if(s.totalProperty) {
14718 this.getTotal = this.getJsonAccessor(s.totalProperty);
14720 if(s.successProperty) {
14721 this.getSuccess = this.getJsonAccessor(s.successProperty);
14723 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14725 var g = this.getJsonAccessor(s.id);
14726 this.getId = function(rec) {
14728 return (r === undefined || r === "") ? null : r;
14731 this.getId = function(){return null;};
14734 for(var jj = 0; jj < fl; jj++){
14736 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737 this.ef[jj] = this.getJsonAccessor(map);
14741 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742 if(s.totalProperty){
14743 var vt = parseInt(this.getTotal(o), 10);
14748 if(s.successProperty){
14749 var vs = this.getSuccess(o);
14750 if(vs === false || vs === 'false'){
14755 for(var i = 0; i < c; i++){
14758 var id = this.getId(n);
14759 for(var j = 0; j < fl; j++){
14761 var v = this.ef[j](n);
14763 Roo.log('missing convert for ' + f.name);
14767 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14769 var record = new Record(values, id);
14771 records[i] = record;
14777 totalRecords : totalRecords
14780 // used when loading children.. @see loadDataFromChildren
14781 toLoadData: function(rec)
14783 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785 return { data : data, total : data.length };
14790 * Ext JS Library 1.1.1
14791 * Copyright(c) 2006-2007, Ext JS, LLC.
14793 * Originally Released Under LGPL - original licence link has changed is not relivant.
14796 * <script type="text/javascript">
14800 * @class Roo.data.ArrayReader
14801 * @extends Roo.data.DataReader
14802 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803 * Each element of that Array represents a row of data fields. The
14804 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14809 var RecordDef = Roo.data.Record.create([
14810 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14811 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14813 var myReader = new Roo.data.ArrayReader({
14814 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14818 * This would consume an Array like this:
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14824 * Create a new JsonReader
14825 * @param {Object} meta Metadata configuration options.
14826 * @param {Object|Array} recordType Either an Array of field definition objects
14828 * @cfg {Array} fields Array of field definition objects
14829 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830 * as specified to {@link Roo.data.Record#create},
14831 * or an {@link Roo.data.Record} object
14834 * created using {@link Roo.data.Record#create}.
14836 Roo.data.ArrayReader = function(meta, recordType)
14838 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14844 * Create a data block containing Roo.data.Records from an XML document.
14845 * @param {Object} o An Array of row objects which represents the dataset.
14846 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847 * a cache of Roo.data.Records.
14849 readRecords : function(o)
14851 var sid = this.meta ? this.meta.id : null;
14852 var recordType = this.recordType, fields = recordType.prototype.fields;
14855 for(var i = 0; i < root.length; i++){
14858 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859 for(var j = 0, jlen = fields.length; j < jlen; j++){
14860 var f = fields.items[j];
14861 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14864 values[f.name] = v;
14866 var record = new recordType(values, id);
14868 records[records.length] = record;
14872 totalRecords : records.length
14875 // used when loading children.. @see loadDataFromChildren
14876 toLoadData: function(rec)
14878 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14890 * @class Roo.bootstrap.ComboBox
14891 * @extends Roo.bootstrap.TriggerField
14892 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893 * @cfg {Boolean} append (true|false) default false
14894 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899 * @cfg {Boolean} animate default true
14900 * @cfg {Boolean} emptyResultText only for touch device
14901 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902 * @cfg {String} emptyTitle default ''
14903 * @cfg {Number} width fixed with? experimental
14905 * Create a new ComboBox.
14906 * @param {Object} config Configuration options
14908 Roo.bootstrap.ComboBox = function(config){
14909 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14913 * Fires when the dropdown list is expanded
14914 * @param {Roo.bootstrap.ComboBox} combo This combo box
14919 * Fires when the dropdown list is collapsed
14920 * @param {Roo.bootstrap.ComboBox} combo This combo box
14924 * @event beforeselect
14925 * Fires before a list item is selected. Return false to cancel the selection.
14926 * @param {Roo.bootstrap.ComboBox} combo This combo box
14927 * @param {Roo.data.Record} record The data record returned from the underlying store
14928 * @param {Number} index The index of the selected item in the dropdown list
14930 'beforeselect' : true,
14933 * Fires when a list item is selected
14934 * @param {Roo.bootstrap.ComboBox} combo This combo box
14935 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936 * @param {Number} index The index of the selected item in the dropdown list
14940 * @event beforequery
14941 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942 * The event object passed has these properties:
14943 * @param {Roo.bootstrap.ComboBox} combo This combo box
14944 * @param {String} query The query
14945 * @param {Boolean} forceAll true to force "all" query
14946 * @param {Boolean} cancel true to cancel the query
14947 * @param {Object} e The query event object
14949 'beforequery': true,
14952 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953 * @param {Roo.bootstrap.ComboBox} combo This combo box
14958 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959 * @param {Roo.bootstrap.ComboBox} combo This combo box
14960 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14965 * Fires when the remove value from the combobox array
14966 * @param {Roo.bootstrap.ComboBox} combo This combo box
14970 * @event afterremove
14971 * Fires when the remove value from the combobox array
14972 * @param {Roo.bootstrap.ComboBox} combo This combo box
14974 'afterremove' : true,
14976 * @event specialfilter
14977 * Fires when specialfilter
14978 * @param {Roo.bootstrap.ComboBox} combo This combo box
14980 'specialfilter' : true,
14983 * Fires when tick the element
14984 * @param {Roo.bootstrap.ComboBox} combo This combo box
14988 * @event touchviewdisplay
14989 * Fires when touch view require special display (default is using displayField)
14990 * @param {Roo.bootstrap.ComboBox} combo This combo box
14991 * @param {Object} cfg set html .
14993 'touchviewdisplay' : true
14998 this.tickItems = [];
15000 this.selectedIndex = -1;
15001 if(this.mode == 'local'){
15002 if(config.queryDelay === undefined){
15003 this.queryDelay = 10;
15005 if(config.minChars === undefined){
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15014 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015 * rendering into an Roo.Editor, defaults to false)
15018 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15022 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15025 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026 * the dropdown list (defaults to undefined, with no header element)
15030 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15034 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15036 listWidth: undefined,
15038 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039 * mode = 'remote' or 'text' if mode = 'local')
15041 displayField: undefined,
15044 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045 * mode = 'remote' or 'value' if mode = 'local').
15046 * Note: use of a valueField requires the user make a selection
15047 * in order for a value to be mapped.
15049 valueField: undefined,
15051 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15056 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057 * field's data value (defaults to the underlying DOM element's name)
15059 hiddenName: undefined,
15061 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15065 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15067 selectedClass: 'active',
15070 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15074 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075 * anchor positions (defaults to 'tl-bl')
15077 listAlign: 'tl-bl?',
15079 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15083 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15084 * query specified by the allQuery config option (defaults to 'query')
15086 triggerAction: 'query',
15088 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089 * (defaults to 4, does not apply if editable = false)
15093 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15098 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15103 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15108 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15109 * when editable = true (defaults to false)
15111 selectOnFocus:false,
15113 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15115 queryParam: 'query',
15117 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15118 * when mode = 'remote' (defaults to 'Loading...')
15120 loadingText: 'Loading...',
15122 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15126 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15130 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131 * traditional select (defaults to true)
15135 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15139 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15143 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144 * listWidth has a higher value)
15148 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149 * allow the user to set arbitrary text into the field (defaults to false)
15151 forceSelection:false,
15153 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154 * if typeAhead = true (defaults to 250)
15156 typeAheadDelay : 250,
15158 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15161 valueNotFoundText : undefined,
15163 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15165 blockFocus : false,
15168 * @cfg {Boolean} disableClear Disable showing of clear button.
15170 disableClear : false,
15172 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15174 alwaysQuery : false,
15177 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15182 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15184 invalidClass : "has-warning",
15187 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15189 validClass : "has-success",
15192 * @cfg {Boolean} specialFilter (true|false) special filter default false
15194 specialFilter : false,
15197 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15199 mobileTouchView : true,
15202 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15204 useNativeIOS : false,
15207 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15209 mobile_restrict_height : false,
15211 ios_options : false,
15223 btnPosition : 'right',
15224 triggerList : true,
15225 showToggleBtn : true,
15227 emptyResultText: 'Empty',
15228 triggerText : 'Select',
15232 // element that contains real text value.. (when hidden is used..)
15234 getAutoCreate : function()
15239 * Render classic select for iso
15242 if(Roo.isIOS && this.useNativeIOS){
15243 cfg = this.getAutoCreateNativeIOS();
15251 if(Roo.isTouch && this.mobileTouchView){
15252 cfg = this.getAutoCreateTouchView();
15259 if(!this.tickable){
15260 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15265 * ComboBox with tickable selections
15268 var align = this.labelAlign || this.parentLabelAlign();
15271 cls : 'form-group roo-combobox-tickable' //input-group
15274 var btn_text_select = '';
15275 var btn_text_done = '';
15276 var btn_text_cancel = '';
15278 if (this.btn_text_show) {
15279 btn_text_select = 'Select';
15280 btn_text_done = 'Done';
15281 btn_text_cancel = 'Cancel';
15286 cls : 'tickable-buttons',
15291 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292 //html : this.triggerText
15293 html: btn_text_select
15299 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15301 html: btn_text_done
15307 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15309 html: btn_text_cancel
15315 buttons.cn.unshift({
15317 cls: 'roo-select2-search-field-input'
15323 Roo.each(buttons.cn, function(c){
15325 c.cls += ' btn-' + _this.size;
15328 if (_this.disabled) {
15335 style : 'display: contents',
15340 cls: 'form-hidden-field'
15344 cls: 'roo-select2-choices',
15348 cls: 'roo-select2-search-field',
15359 cls: 'roo-select2-container input-group roo-select2-container-multi',
15365 // cls: 'typeahead typeahead-long dropdown-menu',
15366 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15371 if(this.hasFeedback && !this.allowBlank){
15375 cls: 'glyphicon form-control-feedback'
15378 combobox.cn.push(feedback);
15385 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386 tooltip : 'This field is required'
15388 if (Roo.bootstrap.version == 4) {
15391 style : 'display:none'
15394 if (align ==='left' && this.fieldLabel.length) {
15396 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15403 cls : 'control-label col-form-label',
15404 html : this.fieldLabel
15416 var labelCfg = cfg.cn[1];
15417 var contentCfg = cfg.cn[2];
15420 if(this.indicatorpos == 'right'){
15426 cls : 'control-label col-form-label',
15430 html : this.fieldLabel
15446 labelCfg = cfg.cn[0];
15447 contentCfg = cfg.cn[1];
15451 if(this.labelWidth > 12){
15452 labelCfg.style = "width: " + this.labelWidth + 'px';
15454 if(this.width * 1 > 0){
15455 contentCfg.style = "width: " + this.width + 'px';
15457 if(this.labelWidth < 13 && this.labelmd == 0){
15458 this.labelmd = this.labelWidth;
15461 if(this.labellg > 0){
15462 labelCfg.cls += ' col-lg-' + this.labellg;
15463 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15466 if(this.labelmd > 0){
15467 labelCfg.cls += ' col-md-' + this.labelmd;
15468 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15471 if(this.labelsm > 0){
15472 labelCfg.cls += ' col-sm-' + this.labelsm;
15473 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15476 if(this.labelxs > 0){
15477 labelCfg.cls += ' col-xs-' + this.labelxs;
15478 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15482 } else if ( this.fieldLabel.length) {
15483 // Roo.log(" label");
15488 //cls : 'input-group-addon',
15489 html : this.fieldLabel
15494 if(this.indicatorpos == 'right'){
15498 //cls : 'input-group-addon',
15499 html : this.fieldLabel
15509 // Roo.log(" no label && no align");
15516 ['xs','sm','md','lg'].map(function(size){
15517 if (settings[size]) {
15518 cfg.cls += ' col-' + size + '-' + settings[size];
15526 _initEventsCalled : false,
15529 initEvents: function()
15531 if (this._initEventsCalled) { // as we call render... prevent looping...
15534 this._initEventsCalled = true;
15537 throw "can not find store for combo";
15540 this.indicator = this.indicatorEl();
15542 this.store = Roo.factory(this.store, Roo.data);
15543 this.store.parent = this;
15545 // if we are building from html. then this element is so complex, that we can not really
15546 // use the rendered HTML.
15547 // so we have to trash and replace the previous code.
15548 if (Roo.XComponent.build_from_html) {
15549 // remove this element....
15550 var e = this.el.dom, k=0;
15551 while (e ) { e = e.previousSibling; ++k;}
15556 this.rendered = false;
15558 this.render(this.parent().getChildContainer(true), k);
15561 if(Roo.isIOS && this.useNativeIOS){
15562 this.initIOSView();
15570 if(Roo.isTouch && this.mobileTouchView){
15571 this.initTouchView();
15576 this.initTickableEvents();
15580 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15582 if(this.hiddenName){
15584 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15586 this.hiddenField.dom.value =
15587 this.hiddenValue !== undefined ? this.hiddenValue :
15588 this.value !== undefined ? this.value : '';
15590 // prevent input submission
15591 this.el.dom.removeAttribute('name');
15592 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15597 // this.el.dom.setAttribute('autocomplete', 'off');
15600 var cls = 'x-combo-list';
15602 //this.list = new Roo.Layer({
15603 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610 _this.list.setWidth(lw);
15613 this.list.on('mouseover', this.onViewOver, this);
15614 this.list.on('mousemove', this.onViewMove, this);
15615 this.list.on('scroll', this.onViewScroll, this);
15618 this.list.swallowEvent('mousewheel');
15619 this.assetHeight = 0;
15622 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623 this.assetHeight += this.header.getHeight();
15626 this.innerList = this.list.createChild({cls:cls+'-inner'});
15627 this.innerList.on('mouseover', this.onViewOver, this);
15628 this.innerList.on('mousemove', this.onViewMove, this);
15629 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15631 if(this.allowBlank && !this.pageSize && !this.disableClear){
15632 this.footer = this.list.createChild({cls:cls+'-ft'});
15633 this.pageTb = new Roo.Toolbar(this.footer);
15637 this.footer = this.list.createChild({cls:cls+'-ft'});
15638 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639 {pageSize: this.pageSize});
15643 if (this.pageTb && this.allowBlank && !this.disableClear) {
15645 this.pageTb.add(new Roo.Toolbar.Fill(), {
15646 cls: 'x-btn-icon x-btn-clear',
15648 handler: function()
15651 _this.clearValue();
15652 _this.onSelect(false, -1);
15657 this.assetHeight += this.footer.getHeight();
15662 this.tpl = Roo.bootstrap.version == 4 ?
15663 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15664 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15667 this.view = new Roo.View(this.list, this.tpl, {
15668 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15670 //this.view.wrapEl.setDisplayed(false);
15671 this.view.on('click', this.onViewClick, this);
15674 this.store.on('beforeload', this.onBeforeLoad, this);
15675 this.store.on('load', this.onLoad, this);
15676 this.store.on('loadexception', this.onLoadException, this);
15678 if(this.resizable){
15679 this.resizer = new Roo.Resizable(this.list, {
15680 pinned:true, handles:'se'
15682 this.resizer.on('resize', function(r, w, h){
15683 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684 this.listWidth = w;
15685 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686 this.restrictHeight();
15688 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15691 if(!this.editable){
15692 this.editable = true;
15693 this.setEditable(false);
15698 if (typeof(this.events.add.listeners) != 'undefined') {
15700 this.addicon = this.wrap.createChild(
15701 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15703 this.addicon.on('click', function(e) {
15704 this.fireEvent('add', this);
15707 if (typeof(this.events.edit.listeners) != 'undefined') {
15709 this.editicon = this.wrap.createChild(
15710 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15711 if (this.addicon) {
15712 this.editicon.setStyle('margin-left', '40px');
15714 this.editicon.on('click', function(e) {
15716 // we fire even if inothing is selected..
15717 this.fireEvent('edit', this, this.lastData );
15723 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724 "up" : function(e){
15725 this.inKeyMode = true;
15729 "down" : function(e){
15730 if(!this.isExpanded()){
15731 this.onTriggerClick();
15733 this.inKeyMode = true;
15738 "enter" : function(e){
15739 // this.onViewClick();
15743 if(this.fireEvent("specialkey", this, e)){
15744 this.onViewClick(false);
15750 "esc" : function(e){
15754 "tab" : function(e){
15757 if(this.fireEvent("specialkey", this, e)){
15758 this.onViewClick(false);
15766 doRelay : function(foo, bar, hname){
15767 if(hname == 'down' || this.scope.isExpanded()){
15768 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15777 this.queryDelay = Math.max(this.queryDelay || 10,
15778 this.mode == 'local' ? 10 : 250);
15781 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15783 if(this.typeAhead){
15784 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15786 if(this.editable !== false){
15787 this.inputEl().on("keyup", this.onKeyUp, this);
15789 if(this.forceSelection){
15790 this.inputEl().on('blur', this.doForce, this);
15794 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15799 initTickableEvents: function()
15803 if(this.hiddenName){
15805 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15807 this.hiddenField.dom.value =
15808 this.hiddenValue !== undefined ? this.hiddenValue :
15809 this.value !== undefined ? this.value : '';
15811 // prevent input submission
15812 this.el.dom.removeAttribute('name');
15813 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15818 // this.list = this.el.select('ul.dropdown-menu',true).first();
15820 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822 if(this.triggerList){
15823 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15826 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15829 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15832 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15835 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15840 this.cancelBtn.hide();
15845 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846 _this.list.setWidth(lw);
15849 this.list.on('mouseover', this.onViewOver, this);
15850 this.list.on('mousemove', this.onViewMove, this);
15852 this.list.on('scroll', this.onViewScroll, this);
15855 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15856 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15859 this.view = new Roo.View(this.list, this.tpl, {
15864 selectedClass: this.selectedClass
15867 //this.view.wrapEl.setDisplayed(false);
15868 this.view.on('click', this.onViewClick, this);
15872 this.store.on('beforeload', this.onBeforeLoad, this);
15873 this.store.on('load', this.onLoad, this);
15874 this.store.on('loadexception', this.onLoadException, this);
15877 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878 "up" : function(e){
15879 this.inKeyMode = true;
15883 "down" : function(e){
15884 this.inKeyMode = true;
15888 "enter" : function(e){
15889 if(this.fireEvent("specialkey", this, e)){
15890 this.onViewClick(false);
15896 "esc" : function(e){
15897 this.onTickableFooterButtonClick(e, false, false);
15900 "tab" : function(e){
15901 this.fireEvent("specialkey", this, e);
15903 this.onTickableFooterButtonClick(e, false, false);
15910 doRelay : function(e, fn, key){
15911 if(this.scope.isExpanded()){
15912 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15921 this.queryDelay = Math.max(this.queryDelay || 10,
15922 this.mode == 'local' ? 10 : 250);
15925 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15927 if(this.typeAhead){
15928 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15931 if(this.editable !== false){
15932 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15935 this.indicator = this.indicatorEl();
15937 if(this.indicator){
15938 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939 this.indicator.hide();
15944 onDestroy : function(){
15946 this.view.setStore(null);
15947 this.view.el.removeAllListeners();
15948 this.view.el.remove();
15949 this.view.purgeListeners();
15952 this.list.dom.innerHTML = '';
15956 this.store.un('beforeload', this.onBeforeLoad, this);
15957 this.store.un('load', this.onLoad, this);
15958 this.store.un('loadexception', this.onLoadException, this);
15960 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15964 fireKey : function(e){
15965 if(e.isNavKeyPress() && !this.list.isVisible()){
15966 this.fireEvent("specialkey", this, e);
15971 onResize: function(w, h)
15975 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15977 // if(typeof w != 'number'){
15978 // // we do not handle it!?!?
15981 // var tw = this.trigger.getWidth();
15982 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15985 // this.inputEl().setWidth( this.adjustWidth('input', x));
15987 // //this.trigger.setStyle('left', x+'px');
15989 // if(this.list && this.listWidth === undefined){
15990 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 // this.list.setWidth(lw);
15992 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16000 * Allow or prevent the user from directly editing the field text. If false is passed,
16001 * the user will only be able to select from the items defined in the dropdown list. This method
16002 * is the runtime equivalent of setting the 'editable' config option at config time.
16003 * @param {Boolean} value True to allow the user to directly edit the field text
16005 setEditable : function(value){
16006 if(value == this.editable){
16009 this.editable = value;
16011 this.inputEl().dom.setAttribute('readOnly', true);
16012 this.inputEl().on('mousedown', this.onTriggerClick, this);
16013 this.inputEl().addClass('x-combo-noedit');
16015 this.inputEl().dom.setAttribute('readOnly', false);
16016 this.inputEl().un('mousedown', this.onTriggerClick, this);
16017 this.inputEl().removeClass('x-combo-noedit');
16023 onBeforeLoad : function(combo,opts){
16024 if(!this.hasFocus){
16028 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16030 this.restrictHeight();
16031 this.selectedIndex = -1;
16035 onLoad : function(){
16037 this.hasQuery = false;
16039 if(!this.hasFocus){
16043 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044 this.loading.hide();
16047 if(this.store.getCount() > 0){
16050 this.restrictHeight();
16051 if(this.lastQuery == this.allQuery){
16052 if(this.editable && !this.tickable){
16053 this.inputEl().dom.select();
16057 !this.selectByValue(this.value, true) &&
16060 !this.store.lastOptions ||
16061 typeof(this.store.lastOptions.add) == 'undefined' ||
16062 this.store.lastOptions.add != true
16065 this.select(0, true);
16068 if(this.autoFocus){
16071 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072 this.taTask.delay(this.typeAheadDelay);
16076 this.onEmptyResults();
16082 onLoadException : function()
16084 this.hasQuery = false;
16086 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087 this.loading.hide();
16090 if(this.tickable && this.editable){
16095 // only causes errors at present
16096 //Roo.log(this.store.reader.jsonData);
16097 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16099 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105 onTypeAhead : function(){
16106 if(this.store.getCount() > 0){
16107 var r = this.store.getAt(0);
16108 var newValue = r.data[this.displayField];
16109 var len = newValue.length;
16110 var selStart = this.getRawValue().length;
16112 if(selStart != len){
16113 this.setRawValue(newValue);
16114 this.selectText(selStart, newValue.length);
16120 onSelect : function(record, index){
16122 if(this.fireEvent('beforeselect', this, record, index) !== false){
16124 this.setFromData(index > -1 ? record.data : false);
16127 this.fireEvent('select', this, record, index);
16132 * Returns the currently selected field value or empty string if no value is set.
16133 * @return {String} value The selected value
16135 getValue : function()
16137 if(Roo.isIOS && this.useNativeIOS){
16138 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16142 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16145 if(this.valueField){
16146 return typeof this.value != 'undefined' ? this.value : '';
16148 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16152 getRawValue : function()
16154 if(Roo.isIOS && this.useNativeIOS){
16155 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16158 var v = this.inputEl().getValue();
16164 * Clears any text/value currently set in the field
16166 clearValue : function(){
16168 if(this.hiddenField){
16169 this.hiddenField.dom.value = '';
16172 this.setRawValue('');
16173 this.lastSelectionText = '';
16174 this.lastData = false;
16176 var close = this.closeTriggerEl();
16187 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16188 * will be displayed in the field. If the value does not match the data value of an existing item,
16189 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190 * Otherwise the field will be blank (although the value will still be set).
16191 * @param {String} value The value to match
16193 setValue : function(v)
16195 if(Roo.isIOS && this.useNativeIOS){
16196 this.setIOSValue(v);
16206 if(this.valueField){
16207 var r = this.findRecord(this.valueField, v);
16209 text = r.data[this.displayField];
16210 }else if(this.valueNotFoundText !== undefined){
16211 text = this.valueNotFoundText;
16214 this.lastSelectionText = text;
16215 if(this.hiddenField){
16216 this.hiddenField.dom.value = v;
16218 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16221 var close = this.closeTriggerEl();
16224 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230 * @property {Object} the last set data for the element
16235 * Sets the value of the field based on a object which is related to the record format for the store.
16236 * @param {Object} value the value to set as. or false on reset?
16238 setFromData : function(o){
16245 var dv = ''; // display value
16246 var vv = ''; // value value..
16248 if (this.displayField) {
16249 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16251 // this is an error condition!!!
16252 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16255 if(this.valueField){
16256 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16259 var close = this.closeTriggerEl();
16262 if(dv.length || vv * 1 > 0){
16264 this.blockFocus=true;
16270 if(this.hiddenField){
16271 this.hiddenField.dom.value = vv;
16273 this.lastSelectionText = dv;
16274 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16278 // no hidden field.. - we store the value in 'value', but still display
16279 // display field!!!!
16280 this.lastSelectionText = dv;
16281 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16288 reset : function(){
16289 // overridden so that last data is reset..
16296 this.setValue(this.originalValue);
16297 //this.clearInvalid();
16298 this.lastData = false;
16300 this.view.clearSelections();
16306 findRecord : function(prop, value){
16308 if(this.store.getCount() > 0){
16309 this.store.each(function(r){
16310 if(r.data[prop] == value){
16320 getName: function()
16322 // returns hidden if it's set..
16323 if (!this.rendered) {return ''};
16324 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16328 onViewMove : function(e, t){
16329 this.inKeyMode = false;
16333 onViewOver : function(e, t){
16334 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16337 var item = this.view.findItemFromChild(t);
16340 var index = this.view.indexOf(item);
16341 this.select(index, false);
16346 onViewClick : function(view, doFocus, el, e)
16348 var index = this.view.getSelectedIndexes()[0];
16350 var r = this.store.getAt(index);
16354 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16361 Roo.each(this.tickItems, function(v,k){
16363 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16365 _this.tickItems.splice(k, 1);
16367 if(typeof(e) == 'undefined' && view == false){
16368 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16380 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381 this.tickItems.push(r.data);
16384 if(typeof(e) == 'undefined' && view == false){
16385 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16392 this.onSelect(r, index);
16394 if(doFocus !== false && !this.blockFocus){
16395 this.inputEl().focus();
16400 restrictHeight : function(){
16401 //this.innerList.dom.style.height = '';
16402 //var inner = this.innerList.dom;
16403 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405 //this.list.beginUpdate();
16406 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407 this.list.alignTo(this.inputEl(), this.listAlign);
16408 this.list.alignTo(this.inputEl(), this.listAlign);
16409 //this.list.endUpdate();
16413 onEmptyResults : function(){
16415 if(this.tickable && this.editable){
16416 this.hasFocus = false;
16417 this.restrictHeight();
16425 * Returns true if the dropdown list is expanded, else false.
16427 isExpanded : function(){
16428 return this.list.isVisible();
16432 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434 * @param {String} value The data value of the item to select
16435 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436 * selected item if it is not currently in view (defaults to true)
16437 * @return {Boolean} True if the value matched an item in the list, else false
16439 selectByValue : function(v, scrollIntoView){
16440 if(v !== undefined && v !== null){
16441 var r = this.findRecord(this.valueField || this.displayField, v);
16443 this.select(this.store.indexOf(r), scrollIntoView);
16451 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453 * @param {Number} index The zero-based index of the list item to select
16454 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455 * selected item if it is not currently in view (defaults to true)
16457 select : function(index, scrollIntoView){
16458 this.selectedIndex = index;
16459 this.view.select(index);
16460 if(scrollIntoView !== false){
16461 var el = this.view.getNode(index);
16463 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16466 this.list.scrollChildIntoView(el, false);
16472 selectNext : function(){
16473 var ct = this.store.getCount();
16475 if(this.selectedIndex == -1){
16477 }else if(this.selectedIndex < ct-1){
16478 this.select(this.selectedIndex+1);
16484 selectPrev : function(){
16485 var ct = this.store.getCount();
16487 if(this.selectedIndex == -1){
16489 }else if(this.selectedIndex != 0){
16490 this.select(this.selectedIndex-1);
16496 onKeyUp : function(e){
16497 if(this.editable !== false && !e.isSpecialKey()){
16498 this.lastKey = e.getKey();
16499 this.dqTask.delay(this.queryDelay);
16504 validateBlur : function(){
16505 return !this.list || !this.list.isVisible();
16509 initQuery : function(){
16511 var v = this.getRawValue();
16513 if(this.tickable && this.editable){
16514 v = this.tickableInputEl().getValue();
16521 doForce : function(){
16522 if(this.inputEl().dom.value.length > 0){
16523 this.inputEl().dom.value =
16524 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16531 * query allowing the query action to be canceled if needed.
16532 * @param {String} query The SQL query to execute
16533 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16535 * saved in the current store (defaults to false)
16537 doQuery : function(q, forceAll){
16539 if(q === undefined || q === null){
16544 forceAll: forceAll,
16548 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16553 forceAll = qe.forceAll;
16554 if(forceAll === true || (q.length >= this.minChars)){
16556 this.hasQuery = true;
16558 if(this.lastQuery != q || this.alwaysQuery){
16559 this.lastQuery = q;
16560 if(this.mode == 'local'){
16561 this.selectedIndex = -1;
16563 this.store.clearFilter();
16566 if(this.specialFilter){
16567 this.fireEvent('specialfilter', this);
16572 this.store.filter(this.displayField, q);
16575 this.store.fireEvent("datachanged", this.store);
16582 this.store.baseParams[this.queryParam] = q;
16584 var options = {params : this.getParams(q)};
16587 options.add = true;
16588 options.params.start = this.page * this.pageSize;
16591 this.store.load(options);
16594 * this code will make the page width larger, at the beginning, the list not align correctly,
16595 * we should expand the list on onLoad
16596 * so command out it
16601 this.selectedIndex = -1;
16606 this.loadNext = false;
16610 getParams : function(q){
16612 //p[this.queryParam] = q;
16616 p.limit = this.pageSize;
16622 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16624 collapse : function(){
16625 if(!this.isExpanded()){
16631 this.hasFocus = false;
16635 this.cancelBtn.hide();
16636 this.trigger.show();
16639 this.tickableInputEl().dom.value = '';
16640 this.tickableInputEl().blur();
16645 Roo.get(document).un('mousedown', this.collapseIf, this);
16646 Roo.get(document).un('mousewheel', this.collapseIf, this);
16647 if (!this.editable) {
16648 Roo.get(document).un('keydown', this.listKeyPress, this);
16650 this.fireEvent('collapse', this);
16656 collapseIf : function(e){
16657 var in_combo = e.within(this.el);
16658 var in_list = e.within(this.list);
16659 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16661 if (in_combo || in_list || is_list) {
16662 //e.stopPropagation();
16667 this.onTickableFooterButtonClick(e, false, false);
16675 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16677 expand : function(){
16679 if(this.isExpanded() || !this.hasFocus){
16683 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684 this.list.setWidth(lw);
16690 this.restrictHeight();
16694 this.tickItems = Roo.apply([], this.item);
16697 this.cancelBtn.show();
16698 this.trigger.hide();
16701 this.tickableInputEl().focus();
16706 Roo.get(document).on('mousedown', this.collapseIf, this);
16707 Roo.get(document).on('mousewheel', this.collapseIf, this);
16708 if (!this.editable) {
16709 Roo.get(document).on('keydown', this.listKeyPress, this);
16712 this.fireEvent('expand', this);
16716 // Implements the default empty TriggerField.onTriggerClick function
16717 onTriggerClick : function(e)
16719 Roo.log('trigger click');
16721 if(this.disabled || !this.triggerList){
16726 this.loadNext = false;
16728 if(this.isExpanded()){
16730 if (!this.blockFocus) {
16731 this.inputEl().focus();
16735 this.hasFocus = true;
16736 if(this.triggerAction == 'all') {
16737 this.doQuery(this.allQuery, true);
16739 this.doQuery(this.getRawValue());
16741 if (!this.blockFocus) {
16742 this.inputEl().focus();
16747 onTickableTriggerClick : function(e)
16754 this.loadNext = false;
16755 this.hasFocus = true;
16757 if(this.triggerAction == 'all') {
16758 this.doQuery(this.allQuery, true);
16760 this.doQuery(this.getRawValue());
16764 onSearchFieldClick : function(e)
16766 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767 this.onTickableFooterButtonClick(e, false, false);
16771 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16776 this.loadNext = false;
16777 this.hasFocus = true;
16779 if(this.triggerAction == 'all') {
16780 this.doQuery(this.allQuery, true);
16782 this.doQuery(this.getRawValue());
16786 listKeyPress : function(e)
16788 //Roo.log('listkeypress');
16789 // scroll to first matching element based on key pres..
16790 if (e.isSpecialKey()) {
16793 var k = String.fromCharCode(e.getKey()).toUpperCase();
16796 var csel = this.view.getSelectedNodes();
16797 var cselitem = false;
16799 var ix = this.view.indexOf(csel[0]);
16800 cselitem = this.store.getAt(ix);
16801 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807 this.store.each(function(v) {
16809 // start at existing selection.
16810 if (cselitem.id == v.id) {
16816 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817 match = this.store.indexOf(v);
16823 if (match === false) {
16824 return true; // no more action?
16827 this.view.select(match);
16828 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829 sn.scrollIntoView(sn.dom.parentNode, false);
16832 onViewScroll : function(e, t){
16834 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){
16838 this.hasQuery = true;
16840 this.loading = this.list.select('.loading', true).first();
16842 if(this.loading === null){
16843 this.list.createChild({
16845 cls: 'loading roo-select2-more-results roo-select2-active',
16846 html: 'Loading more results...'
16849 this.loading = this.list.select('.loading', true).first();
16851 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16853 this.loading.hide();
16856 this.loading.show();
16861 this.loadNext = true;
16863 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16868 addItem : function(o)
16870 var dv = ''; // display value
16872 if (this.displayField) {
16873 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16875 // this is an error condition!!!
16876 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16883 var choice = this.choices.createChild({
16885 cls: 'roo-select2-search-choice',
16894 cls: 'roo-select2-search-choice-close fa fa-times',
16899 }, this.searchField);
16901 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16903 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16911 this.inputEl().dom.value = '';
16916 onRemoveItem : function(e, _self, o)
16918 e.preventDefault();
16920 this.lastItem = Roo.apply([], this.item);
16922 var index = this.item.indexOf(o.data) * 1;
16925 Roo.log('not this item?!');
16929 this.item.splice(index, 1);
16934 this.fireEvent('remove', this, e);
16940 syncValue : function()
16942 if(!this.item.length){
16949 Roo.each(this.item, function(i){
16950 if(_this.valueField){
16951 value.push(i[_this.valueField]);
16958 this.value = value.join(',');
16960 if(this.hiddenField){
16961 this.hiddenField.dom.value = this.value;
16964 this.store.fireEvent("datachanged", this.store);
16969 clearItem : function()
16971 if(!this.multiple){
16977 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16985 if(this.tickable && !Roo.isTouch){
16986 this.view.refresh();
16990 inputEl: function ()
16992 if(Roo.isIOS && this.useNativeIOS){
16993 return this.el.select('select.roo-ios-select', true).first();
16996 if(Roo.isTouch && this.mobileTouchView){
16997 return this.el.select('input.form-control',true).first();
17001 return this.searchField;
17004 return this.el.select('input.form-control',true).first();
17007 onTickableFooterButtonClick : function(e, btn, el)
17009 e.preventDefault();
17011 this.lastItem = Roo.apply([], this.item);
17013 if(btn && btn.name == 'cancel'){
17014 this.tickItems = Roo.apply([], this.item);
17023 Roo.each(this.tickItems, function(o){
17031 validate : function()
17033 if(this.getVisibilityEl().hasClass('hidden')){
17037 var v = this.getRawValue();
17040 v = this.getValue();
17043 if(this.disabled || this.allowBlank || v.length){
17048 this.markInvalid();
17052 tickableInputEl : function()
17054 if(!this.tickable || !this.editable){
17055 return this.inputEl();
17058 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17062 getAutoCreateTouchView : function()
17067 cls: 'form-group' //input-group
17073 type : this.inputType,
17074 cls : 'form-control x-combo-noedit',
17075 autocomplete: 'new-password',
17076 placeholder : this.placeholder || '',
17081 input.name = this.name;
17085 input.cls += ' input-' + this.size;
17088 if (this.disabled) {
17089 input.disabled = true;
17093 cls : 'roo-combobox-wrap',
17100 inputblock.cls += ' input-group';
17102 inputblock.cn.unshift({
17104 cls : 'input-group-addon input-group-prepend input-group-text',
17109 if(this.removable && !this.multiple){
17110 inputblock.cls += ' roo-removable';
17112 inputblock.cn.push({
17115 cls : 'roo-combo-removable-btn close'
17119 if(this.hasFeedback && !this.allowBlank){
17121 inputblock.cls += ' has-feedback';
17123 inputblock.cn.push({
17125 cls: 'glyphicon form-control-feedback'
17132 inputblock.cls += (this.before) ? '' : ' input-group';
17134 inputblock.cn.push({
17136 cls : 'input-group-addon input-group-append input-group-text',
17142 var ibwrap = inputblock;
17147 cls: 'roo-select2-choices',
17151 cls: 'roo-select2-search-field',
17164 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17169 cls: 'form-hidden-field'
17175 if(!this.multiple && this.showToggleBtn){
17181 if (this.caret != false) {
17184 cls: 'fa fa-' + this.caret
17191 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17193 Roo.bootstrap.version == 3 ? caret : '',
17196 cls: 'combobox-clear',
17210 combobox.cls += ' roo-select2-container-multi';
17213 var align = this.labelAlign || this.parentLabelAlign();
17215 if (align ==='left' && this.fieldLabel.length) {
17220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221 tooltip : 'This field is required'
17225 cls : 'control-label col-form-label',
17226 html : this.fieldLabel
17230 cls : 'roo-combobox-wrap ',
17237 var labelCfg = cfg.cn[1];
17238 var contentCfg = cfg.cn[2];
17241 if(this.indicatorpos == 'right'){
17246 cls : 'control-label col-form-label',
17250 html : this.fieldLabel
17254 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255 tooltip : 'This field is required'
17260 cls : "roo-combobox-wrap ",
17268 labelCfg = cfg.cn[0];
17269 contentCfg = cfg.cn[1];
17274 if(this.labelWidth > 12){
17275 labelCfg.style = "width: " + this.labelWidth + 'px';
17278 if(this.labelWidth < 13 && this.labelmd == 0){
17279 this.labelmd = this.labelWidth;
17282 if(this.labellg > 0){
17283 labelCfg.cls += ' col-lg-' + this.labellg;
17284 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17287 if(this.labelmd > 0){
17288 labelCfg.cls += ' col-md-' + this.labelmd;
17289 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17292 if(this.labelsm > 0){
17293 labelCfg.cls += ' col-sm-' + this.labelsm;
17294 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17297 if(this.labelxs > 0){
17298 labelCfg.cls += ' col-xs-' + this.labelxs;
17299 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17303 } else if ( this.fieldLabel.length) {
17307 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308 tooltip : 'This field is required'
17312 cls : 'control-label',
17313 html : this.fieldLabel
17324 if(this.indicatorpos == 'right'){
17328 cls : 'control-label',
17329 html : this.fieldLabel,
17333 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334 tooltip : 'This field is required'
17351 var settings = this;
17353 ['xs','sm','md','lg'].map(function(size){
17354 if (settings[size]) {
17355 cfg.cls += ' col-' + size + '-' + settings[size];
17362 initTouchView : function()
17364 this.renderTouchView();
17366 this.touchViewEl.on('scroll', function(){
17367 this.el.dom.scrollTop = 0;
17370 this.originalValue = this.getValue();
17372 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17374 this.inputEl().on("click", this.showTouchView, this);
17375 if (this.triggerEl) {
17376 this.triggerEl.on("click", this.showTouchView, this);
17380 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17383 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17385 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386 this.store.on('load', this.onTouchViewLoad, this);
17387 this.store.on('loadexception', this.onTouchViewLoadException, this);
17389 if(this.hiddenName){
17391 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17393 this.hiddenField.dom.value =
17394 this.hiddenValue !== undefined ? this.hiddenValue :
17395 this.value !== undefined ? this.value : '';
17397 this.el.dom.removeAttribute('name');
17398 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17402 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17406 if(this.removable && !this.multiple){
17407 var close = this.closeTriggerEl();
17409 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410 close.on('click', this.removeBtnClick, this, close);
17414 * fix the bug in Safari iOS8
17416 this.inputEl().on("focus", function(e){
17417 document.activeElement.blur();
17420 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17427 renderTouchView : function()
17429 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17432 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17435 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437 this.touchViewBodyEl.setStyle('overflow', 'auto');
17439 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17447 showTouchView : function()
17453 this.touchViewHeaderEl.hide();
17455 if(this.modalTitle.length){
17456 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457 this.touchViewHeaderEl.show();
17460 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461 this.touchViewEl.show();
17463 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17465 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17468 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17470 if(this.modalTitle.length){
17471 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17474 this.touchViewBodyEl.setHeight(bodyHeight);
17478 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17480 this.touchViewEl.addClass(['in','show']);
17483 if(this._touchViewMask){
17484 Roo.get(document.body).addClass("x-body-masked");
17485 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17486 this._touchViewMask.setStyle('z-index', 10000);
17487 this._touchViewMask.addClass('show');
17490 this.doTouchViewQuery();
17494 hideTouchView : function()
17496 this.touchViewEl.removeClass(['in','show']);
17500 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17502 this.touchViewEl.setStyle('display', 'none');
17505 if(this._touchViewMask){
17506 this._touchViewMask.removeClass('show');
17507 Roo.get(document.body).removeClass("x-body-masked");
17511 setTouchViewValue : function()
17518 Roo.each(this.tickItems, function(o){
17523 this.hideTouchView();
17526 doTouchViewQuery : function()
17535 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17539 if(!this.alwaysQuery || this.mode == 'local'){
17540 this.onTouchViewLoad();
17547 onTouchViewBeforeLoad : function(combo,opts)
17553 onTouchViewLoad : function()
17555 if(this.store.getCount() < 1){
17556 this.onTouchViewEmptyResults();
17560 this.clearTouchView();
17562 var rawValue = this.getRawValue();
17564 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17566 this.tickItems = [];
17568 this.store.data.each(function(d, rowIndex){
17569 var row = this.touchViewListGroup.createChild(template);
17571 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572 row.addClass(d.data.cls);
17575 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17578 html : d.data[this.displayField]
17581 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17585 row.removeClass('selected');
17586 if(!this.multiple && this.valueField &&
17587 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17590 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591 row.addClass('selected');
17594 if(this.multiple && this.valueField &&
17595 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17599 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600 this.tickItems.push(d.data);
17603 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17607 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17609 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17611 if(this.modalTitle.length){
17612 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17615 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17617 if(this.mobile_restrict_height && listHeight < bodyHeight){
17618 this.touchViewBodyEl.setHeight(listHeight);
17623 if(firstChecked && listHeight > bodyHeight){
17624 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17629 onTouchViewLoadException : function()
17631 this.hideTouchView();
17634 onTouchViewEmptyResults : function()
17636 this.clearTouchView();
17638 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17640 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17644 clearTouchView : function()
17646 this.touchViewListGroup.dom.innerHTML = '';
17649 onTouchViewClick : function(e, el, o)
17651 e.preventDefault();
17654 var rowIndex = o.rowIndex;
17656 var r = this.store.getAt(rowIndex);
17658 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17660 if(!this.multiple){
17661 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662 c.dom.removeAttribute('checked');
17665 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17667 this.setFromData(r.data);
17669 var close = this.closeTriggerEl();
17675 this.hideTouchView();
17677 this.fireEvent('select', this, r, rowIndex);
17682 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17688 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689 this.addItem(r.data);
17690 this.tickItems.push(r.data);
17694 getAutoCreateNativeIOS : function()
17697 cls: 'form-group' //input-group,
17702 cls : 'roo-ios-select'
17706 combobox.name = this.name;
17709 if (this.disabled) {
17710 combobox.disabled = true;
17713 var settings = this;
17715 ['xs','sm','md','lg'].map(function(size){
17716 if (settings[size]) {
17717 cfg.cls += ' col-' + size + '-' + settings[size];
17727 initIOSView : function()
17729 this.store.on('load', this.onIOSViewLoad, this);
17734 onIOSViewLoad : function()
17736 if(this.store.getCount() < 1){
17740 this.clearIOSView();
17742 if(this.allowBlank) {
17744 var default_text = '-- SELECT --';
17746 if(this.placeholder.length){
17747 default_text = this.placeholder;
17750 if(this.emptyTitle.length){
17751 default_text += ' - ' + this.emptyTitle + ' -';
17754 var opt = this.inputEl().createChild({
17757 html : default_text
17761 o[this.valueField] = 0;
17762 o[this.displayField] = default_text;
17764 this.ios_options.push({
17771 this.store.data.each(function(d, rowIndex){
17775 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776 html = d.data[this.displayField];
17781 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782 value = d.data[this.valueField];
17791 if(this.value == d.data[this.valueField]){
17792 option['selected'] = true;
17795 var opt = this.inputEl().createChild(option);
17797 this.ios_options.push({
17804 this.inputEl().on('change', function(){
17805 this.fireEvent('select', this);
17810 clearIOSView: function()
17812 this.inputEl().dom.innerHTML = '';
17814 this.ios_options = [];
17817 setIOSValue: function(v)
17821 if(!this.ios_options){
17825 Roo.each(this.ios_options, function(opts){
17827 opts.el.dom.removeAttribute('selected');
17829 if(opts.data[this.valueField] != v){
17833 opts.el.dom.setAttribute('selected', true);
17839 * @cfg {Boolean} grow
17843 * @cfg {Number} growMin
17847 * @cfg {Number} growMax
17856 Roo.apply(Roo.bootstrap.ComboBox, {
17860 cls: 'modal-header',
17882 cls: 'list-group-item',
17886 cls: 'roo-combobox-list-group-item-value'
17890 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17904 listItemCheckbox : {
17906 cls: 'list-group-item',
17910 cls: 'roo-combobox-list-group-item-value'
17914 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17930 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17935 cls: 'modal-footer',
17943 cls: 'col-xs-6 text-left',
17946 cls: 'btn btn-danger roo-touch-view-cancel',
17952 cls: 'col-xs-6 text-right',
17955 cls: 'btn btn-success roo-touch-view-ok',
17966 Roo.apply(Roo.bootstrap.ComboBox, {
17968 touchViewTemplate : {
17970 cls: 'modal fade roo-combobox-touch-view',
17974 cls: 'modal-dialog',
17975 style : 'position:fixed', // we have to fix position....
17979 cls: 'modal-content',
17981 Roo.bootstrap.ComboBox.header,
17982 Roo.bootstrap.ComboBox.body,
17983 Roo.bootstrap.ComboBox.footer
17992 * Ext JS Library 1.1.1
17993 * Copyright(c) 2006-2007, Ext JS, LLC.
17995 * Originally Released Under LGPL - original licence link has changed is not relivant.
17998 * <script type="text/javascript">
18003 * @extends Roo.util.Observable
18004 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18005 * This class also supports single and multi selection modes. <br>
18006 * Create a data model bound view:
18008 var store = new Roo.data.Store(...);
18010 var view = new Roo.View({
18012 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18014 singleSelect: true,
18015 selectedClass: "ydataview-selected",
18019 // listen for node click?
18020 view.on("click", function(vw, index, node, e){
18021 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18025 dataModel.load("foobar.xml");
18027 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18029 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18032 * Note: old style constructor is still suported (container, template, config)
18035 * Create a new View
18036 * @param {Object} config The config object
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18041 this.parent = false;
18043 if (typeof(depreciated_tpl) == 'undefined') {
18044 // new way.. - universal constructor.
18045 Roo.apply(this, config);
18046 this.el = Roo.get(this.el);
18049 this.el = Roo.get(config);
18050 this.tpl = depreciated_tpl;
18051 Roo.apply(this, depreciated_config);
18053 this.wrapEl = this.el.wrap().wrap();
18054 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18057 if(typeof(this.tpl) == "string"){
18058 this.tpl = new Roo.Template(this.tpl);
18060 // support xtype ctors..
18061 this.tpl = new Roo.factory(this.tpl, Roo);
18065 this.tpl.compile();
18070 * @event beforeclick
18071 * Fires before a click is processed. Returns false to cancel the default action.
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
18077 "beforeclick" : true,
18080 * Fires when a template node is 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
18089 * Fires when a template node is double 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
18097 * @event contextmenu
18098 * Fires when a template node is right clicked.
18099 * @param {Roo.View} this
18100 * @param {Number} index The index of the target node
18101 * @param {HTMLElement} node The target node
18102 * @param {Roo.EventObject} e The raw event object
18104 "contextmenu" : true,
18106 * @event selectionchange
18107 * Fires when the selected nodes change.
18108 * @param {Roo.View} this
18109 * @param {Array} selections Array of the selected nodes
18111 "selectionchange" : true,
18114 * @event beforeselect
18115 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116 * @param {Roo.View} this
18117 * @param {HTMLElement} node The node to be selected
18118 * @param {Array} selections Array of currently selected nodes
18120 "beforeselect" : true,
18122 * @event preparedata
18123 * Fires on every row to render, to allow you to change the data.
18124 * @param {Roo.View} this
18125 * @param {Object} data to be rendered (change this)
18127 "preparedata" : true
18135 "click": this.onClick,
18136 "dblclick": this.onDblClick,
18137 "contextmenu": this.onContextMenu,
18141 this.selections = [];
18143 this.cmp = new Roo.CompositeElementLite([]);
18145 this.store = Roo.factory(this.store, Roo.data);
18146 this.setStore(this.store, true);
18149 if ( this.footer && this.footer.xtype) {
18151 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18153 this.footer.dataSource = this.store;
18154 this.footer.container = fctr;
18155 this.footer = Roo.factory(this.footer, Roo);
18156 fctr.insertFirst(this.el);
18158 // this is a bit insane - as the paging toolbar seems to detach the el..
18159 // dom.parentNode.parentNode.parentNode
18160 // they get detached?
18164 Roo.View.superclass.constructor.call(this);
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18172 * @cfg {Roo.data.Store} store Data store to load data from.
18177 * @cfg {String|Roo.Element} el The container element.
18182 * @cfg {String|Roo.Template} tpl The template used by this View
18186 * @cfg {String} dataName the named area of the template to use as the data area
18187 * Works with domtemplates roo-name="name"
18191 * @cfg {String} selectedClass The css class to add to selected nodes
18193 selectedClass : "x-view-selected",
18195 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18200 * @cfg {String} text to display on mask (default Loading)
18204 * @cfg {Boolean} multiSelect Allow multiple selection
18206 multiSelect : false,
18208 * @cfg {Boolean} singleSelect Allow single selection
18210 singleSelect: false,
18213 * @cfg {Boolean} toggleSelect - selecting
18215 toggleSelect : false,
18218 * @cfg {Boolean} tickable - selecting
18223 * Returns the element this view is bound to.
18224 * @return {Roo.Element}
18226 getEl : function(){
18227 return this.wrapEl;
18233 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18235 refresh : function(){
18236 //Roo.log('refresh');
18239 // if we are using something like 'domtemplate', then
18240 // the what gets used is:
18241 // t.applySubtemplate(NAME, data, wrapping data..)
18242 // the outer template then get' applied with
18243 // the store 'extra data'
18244 // and the body get's added to the
18245 // roo-name="data" node?
18246 // <span class='roo-tpl-{name}'></span> ?????
18250 this.clearSelections();
18251 this.el.update("");
18253 var records = this.store.getRange();
18254 if(records.length < 1) {
18256 // is this valid?? = should it render a template??
18258 this.el.update(this.emptyText);
18262 if (this.dataName) {
18263 this.el.update(t.apply(this.store.meta)); //????
18264 el = this.el.child('.roo-tpl-' + this.dataName);
18267 for(var i = 0, len = records.length; i < len; i++){
18268 var data = this.prepareData(records[i].data, i, records[i]);
18269 this.fireEvent("preparedata", this, data, i, records[i]);
18271 var d = Roo.apply({}, data);
18274 Roo.apply(d, {'roo-id' : Roo.id()});
18278 Roo.each(this.parent.item, function(item){
18279 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18282 Roo.apply(d, {'roo-data-checked' : 'checked'});
18286 html[html.length] = Roo.util.Format.trim(
18288 t.applySubtemplate(this.dataName, d, this.store.meta) :
18295 el.update(html.join(""));
18296 this.nodes = el.dom.childNodes;
18297 this.updateIndexes(0);
18302 * Function to override to reformat the data that is sent to
18303 * the template for each node.
18304 * DEPRICATED - use the preparedata event handler.
18305 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306 * a JSON object for an UpdateManager bound view).
18308 prepareData : function(data, index, record)
18310 this.fireEvent("preparedata", this, data, index, record);
18314 onUpdate : function(ds, record){
18315 // Roo.log('on update');
18316 this.clearSelections();
18317 var index = this.store.indexOf(record);
18318 var n = this.nodes[index];
18319 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320 n.parentNode.removeChild(n);
18321 this.updateIndexes(index, index);
18327 onAdd : function(ds, records, index)
18329 //Roo.log(['on Add', ds, records, index] );
18330 this.clearSelections();
18331 if(this.nodes.length == 0){
18335 var n = this.nodes[index];
18336 for(var i = 0, len = records.length; i < len; i++){
18337 var d = this.prepareData(records[i].data, i, records[i]);
18339 this.tpl.insertBefore(n, d);
18342 this.tpl.append(this.el, d);
18345 this.updateIndexes(index);
18348 onRemove : function(ds, record, index){
18349 // Roo.log('onRemove');
18350 this.clearSelections();
18351 var el = this.dataName ?
18352 this.el.child('.roo-tpl-' + this.dataName) :
18355 el.dom.removeChild(this.nodes[index]);
18356 this.updateIndexes(index);
18360 * Refresh an individual node.
18361 * @param {Number} index
18363 refreshNode : function(index){
18364 this.onUpdate(this.store, this.store.getAt(index));
18367 updateIndexes : function(startIndex, endIndex){
18368 var ns = this.nodes;
18369 startIndex = startIndex || 0;
18370 endIndex = endIndex || ns.length - 1;
18371 for(var i = startIndex; i <= endIndex; i++){
18372 ns[i].nodeIndex = i;
18377 * Changes the data store this view uses and refresh the view.
18378 * @param {Store} store
18380 setStore : function(store, initial){
18381 if(!initial && this.store){
18382 this.store.un("datachanged", this.refresh);
18383 this.store.un("add", this.onAdd);
18384 this.store.un("remove", this.onRemove);
18385 this.store.un("update", this.onUpdate);
18386 this.store.un("clear", this.refresh);
18387 this.store.un("beforeload", this.onBeforeLoad);
18388 this.store.un("load", this.onLoad);
18389 this.store.un("loadexception", this.onLoad);
18393 store.on("datachanged", this.refresh, this);
18394 store.on("add", this.onAdd, this);
18395 store.on("remove", this.onRemove, this);
18396 store.on("update", this.onUpdate, this);
18397 store.on("clear", this.refresh, this);
18398 store.on("beforeload", this.onBeforeLoad, this);
18399 store.on("load", this.onLoad, this);
18400 store.on("loadexception", this.onLoad, this);
18408 * onbeforeLoad - masks the loading area.
18411 onBeforeLoad : function(store,opts)
18413 //Roo.log('onBeforeLoad');
18415 this.el.update("");
18417 this.el.mask(this.mask ? this.mask : "Loading" );
18419 onLoad : function ()
18426 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427 * @param {HTMLElement} node
18428 * @return {HTMLElement} The template node
18430 findItemFromChild : function(node){
18431 var el = this.dataName ?
18432 this.el.child('.roo-tpl-' + this.dataName,true) :
18435 if(!node || node.parentNode == el){
18438 var p = node.parentNode;
18439 while(p && p != el){
18440 if(p.parentNode == el){
18449 onClick : function(e){
18450 var item = this.findItemFromChild(e.getTarget());
18452 var index = this.indexOf(item);
18453 if(this.onItemClick(item, index, e) !== false){
18454 this.fireEvent("click", this, index, item, e);
18457 this.clearSelections();
18462 onContextMenu : function(e){
18463 var item = this.findItemFromChild(e.getTarget());
18465 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18470 onDblClick : function(e){
18471 var item = this.findItemFromChild(e.getTarget());
18473 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18477 onItemClick : function(item, index, e)
18479 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18482 if (this.toggleSelect) {
18483 var m = this.isSelected(item) ? 'unselect' : 'select';
18486 _t[m](item, true, false);
18489 if(this.multiSelect || this.singleSelect){
18490 if(this.multiSelect && e.shiftKey && this.lastSelection){
18491 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18493 this.select(item, this.multiSelect && e.ctrlKey);
18494 this.lastSelection = item;
18497 if(!this.tickable){
18498 e.preventDefault();
18506 * Get the number of selected nodes.
18509 getSelectionCount : function(){
18510 return this.selections.length;
18514 * Get the currently selected nodes.
18515 * @return {Array} An array of HTMLElements
18517 getSelectedNodes : function(){
18518 return this.selections;
18522 * Get the indexes of the selected nodes.
18525 getSelectedIndexes : function(){
18526 var indexes = [], s = this.selections;
18527 for(var i = 0, len = s.length; i < len; i++){
18528 indexes.push(s[i].nodeIndex);
18534 * Clear all selections
18535 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18537 clearSelections : function(suppressEvent){
18538 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539 this.cmp.elements = this.selections;
18540 this.cmp.removeClass(this.selectedClass);
18541 this.selections = [];
18542 if(!suppressEvent){
18543 this.fireEvent("selectionchange", this, this.selections);
18549 * Returns true if the passed node is selected
18550 * @param {HTMLElement/Number} node The node or node index
18551 * @return {Boolean}
18553 isSelected : function(node){
18554 var s = this.selections;
18558 node = this.getNode(node);
18559 return s.indexOf(node) !== -1;
18564 * @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
18565 * @param {Boolean} keepExisting (optional) true to keep existing selections
18566 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18568 select : function(nodeInfo, keepExisting, suppressEvent){
18569 if(nodeInfo instanceof Array){
18571 this.clearSelections(true);
18573 for(var i = 0, len = nodeInfo.length; i < len; i++){
18574 this.select(nodeInfo[i], true, true);
18578 var node = this.getNode(nodeInfo);
18579 if(!node || this.isSelected(node)){
18580 return; // already selected.
18583 this.clearSelections(true);
18586 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587 Roo.fly(node).addClass(this.selectedClass);
18588 this.selections.push(node);
18589 if(!suppressEvent){
18590 this.fireEvent("selectionchange", this, this.selections);
18598 * @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
18599 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18602 unselect : function(nodeInfo, keepExisting, suppressEvent)
18604 if(nodeInfo instanceof Array){
18605 Roo.each(this.selections, function(s) {
18606 this.unselect(s, nodeInfo);
18610 var node = this.getNode(nodeInfo);
18611 if(!node || !this.isSelected(node)){
18612 //Roo.log("not selected");
18613 return; // not selected.
18617 Roo.each(this.selections, function(s) {
18619 Roo.fly(node).removeClass(this.selectedClass);
18626 this.selections= ns;
18627 this.fireEvent("selectionchange", this, this.selections);
18631 * Gets a template node.
18632 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633 * @return {HTMLElement} The node or null if it wasn't found
18635 getNode : function(nodeInfo){
18636 if(typeof nodeInfo == "string"){
18637 return document.getElementById(nodeInfo);
18638 }else if(typeof nodeInfo == "number"){
18639 return this.nodes[nodeInfo];
18645 * Gets a range template nodes.
18646 * @param {Number} startIndex
18647 * @param {Number} endIndex
18648 * @return {Array} An array of nodes
18650 getNodes : function(start, end){
18651 var ns = this.nodes;
18652 start = start || 0;
18653 end = typeof end == "undefined" ? ns.length - 1 : end;
18656 for(var i = start; i <= end; i++){
18660 for(var i = start; i >= end; i--){
18668 * Finds the index of the passed node
18669 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670 * @return {Number} The index of the node or -1
18672 indexOf : function(node){
18673 node = this.getNode(node);
18674 if(typeof node.nodeIndex == "number"){
18675 return node.nodeIndex;
18677 var ns = this.nodes;
18678 for(var i = 0, len = ns.length; i < len; i++){
18689 * based on jquery fullcalendar
18693 Roo.bootstrap = Roo.bootstrap || {};
18695 * @class Roo.bootstrap.Calendar
18696 * @extends Roo.bootstrap.Component
18697 * Bootstrap Calendar class
18698 * @cfg {Boolean} loadMask (true|false) default false
18699 * @cfg {Object} header generate the user specific header of the calendar, default false
18702 * Create a new Container
18703 * @param {Object} config The config object
18708 Roo.bootstrap.Calendar = function(config){
18709 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18713 * Fires when a date is selected
18714 * @param {DatePicker} this
18715 * @param {Date} date The selected date
18719 * @event monthchange
18720 * Fires when the displayed month changes
18721 * @param {DatePicker} this
18722 * @param {Date} date The selected month
18724 'monthchange': true,
18726 * @event evententer
18727 * Fires when mouse over an event
18728 * @param {Calendar} this
18729 * @param {event} Event
18731 'evententer': true,
18733 * @event eventleave
18734 * Fires when the mouse leaves an
18735 * @param {Calendar} this
18738 'eventleave': true,
18740 * @event eventclick
18741 * Fires when the mouse click an
18742 * @param {Calendar} this
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18754 * @cfg {Number} startDay
18755 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18763 getAutoCreate : function(){
18766 var fc_button = function(name, corner, style, content ) {
18767 return Roo.apply({},{
18769 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18771 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18774 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18785 style : 'width:100%',
18792 cls : 'fc-header-left',
18794 fc_button('prev', 'left', 'arrow', '‹' ),
18795 fc_button('next', 'right', 'arrow', '›' ),
18796 { tag: 'span', cls: 'fc-header-space' },
18797 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18805 cls : 'fc-header-center',
18809 cls: 'fc-header-title',
18812 html : 'month / year'
18820 cls : 'fc-header-right',
18822 /* fc_button('month', 'left', '', 'month' ),
18823 fc_button('week', '', '', 'week' ),
18824 fc_button('day', 'right', '', 'day' )
18836 header = this.header;
18839 var cal_heads = function() {
18841 // fixme - handle this.
18843 for (var i =0; i < Date.dayNames.length; i++) {
18844 var d = Date.dayNames[i];
18847 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848 html : d.substring(0,3)
18852 ret[0].cls += ' fc-first';
18853 ret[6].cls += ' fc-last';
18856 var cal_cell = function(n) {
18859 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18864 cls: 'fc-day-number',
18868 cls: 'fc-day-content',
18872 style: 'position: relative;' // height: 17px;
18884 var cal_rows = function() {
18887 for (var r = 0; r < 6; r++) {
18894 for (var i =0; i < Date.dayNames.length; i++) {
18895 var d = Date.dayNames[i];
18896 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18899 row.cn[0].cls+=' fc-first';
18900 row.cn[0].cn[0].style = 'min-height:90px';
18901 row.cn[6].cls+=' fc-last';
18905 ret[0].cls += ' fc-first';
18906 ret[4].cls += ' fc-prev-last';
18907 ret[5].cls += ' fc-last';
18914 cls: 'fc-border-separate',
18915 style : 'width:100%',
18923 cls : 'fc-first fc-last',
18941 cls : 'fc-content',
18942 style : "position: relative;",
18945 cls : 'fc-view fc-view-month fc-grid',
18946 style : 'position: relative',
18947 unselectable : 'on',
18950 cls : 'fc-event-container',
18951 style : 'position:absolute;z-index:8;top:0;left:0;'
18969 initEvents : function()
18972 throw "can not find store for calendar";
18978 style: "text-align:center",
18982 style: "background-color:white;width:50%;margin:250 auto",
18986 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18997 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18999 var size = this.el.select('.fc-content', true).first().getSize();
19000 this.maskEl.setSize(size.width, size.height);
19001 this.maskEl.enableDisplayMode("block");
19002 if(!this.loadMask){
19003 this.maskEl.hide();
19006 this.store = Roo.factory(this.store, Roo.data);
19007 this.store.on('load', this.onLoad, this);
19008 this.store.on('beforeload', this.onBeforeLoad, this);
19012 this.cells = this.el.select('.fc-day',true);
19013 //Roo.log(this.cells);
19014 this.textNodes = this.el.query('.fc-day-number');
19015 this.cells.addClassOnOver('fc-state-hover');
19017 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19022 this.on('monthchange', this.onMonthChange, this);
19024 this.update(new Date().clearTime());
19027 resize : function() {
19028 var sz = this.el.getSize();
19030 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031 this.el.select('.fc-day-content div',true).setHeight(34);
19036 showPrevMonth : function(e){
19037 this.update(this.activeDate.add("mo", -1));
19039 showToday : function(e){
19040 this.update(new Date().clearTime());
19043 showNextMonth : function(e){
19044 this.update(this.activeDate.add("mo", 1));
19048 showPrevYear : function(){
19049 this.update(this.activeDate.add("y", -1));
19053 showNextYear : function(){
19054 this.update(this.activeDate.add("y", 1));
19059 update : function(date)
19061 var vd = this.activeDate;
19062 this.activeDate = date;
19063 // if(vd && this.el){
19064 // var t = date.getTime();
19065 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 // Roo.log('using add remove');
19068 // this.fireEvent('monthchange', this, date);
19070 // this.cells.removeClass("fc-state-highlight");
19071 // this.cells.each(function(c){
19072 // if(c.dateValue == t){
19073 // c.addClass("fc-state-highlight");
19074 // setTimeout(function(){
19075 // try{c.dom.firstChild.focus();}catch(e){}
19085 var days = date.getDaysInMonth();
19087 var firstOfMonth = date.getFirstDateOfMonth();
19088 var startingPos = firstOfMonth.getDay()-this.startDay;
19090 if(startingPos < this.startDay){
19094 var pm = date.add(Date.MONTH, -1);
19095 var prevStart = pm.getDaysInMonth()-startingPos;
19097 this.cells = this.el.select('.fc-day',true);
19098 this.textNodes = this.el.query('.fc-day-number');
19099 this.cells.addClassOnOver('fc-state-hover');
19101 var cells = this.cells.elements;
19102 var textEls = this.textNodes;
19104 Roo.each(cells, function(cell){
19105 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19108 days += startingPos;
19110 // convert everything to numbers so it's fast
19111 var day = 86400000;
19112 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19115 //Roo.log(prevStart);
19117 var today = new Date().clearTime().getTime();
19118 var sel = date.clearTime().getTime();
19119 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121 var ddMatch = this.disabledDatesRE;
19122 var ddText = this.disabledDatesText;
19123 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124 var ddaysText = this.disabledDaysText;
19125 var format = this.format;
19127 var setCellClass = function(cal, cell){
19131 //Roo.log('set Cell Class');
19133 var t = d.getTime();
19137 cell.dateValue = t;
19139 cell.className += " fc-today";
19140 cell.className += " fc-state-highlight";
19141 cell.title = cal.todayText;
19144 // disable highlight in other month..
19145 //cell.className += " fc-state-highlight";
19150 cell.className = " fc-state-disabled";
19151 cell.title = cal.minText;
19155 cell.className = " fc-state-disabled";
19156 cell.title = cal.maxText;
19160 if(ddays.indexOf(d.getDay()) != -1){
19161 cell.title = ddaysText;
19162 cell.className = " fc-state-disabled";
19165 if(ddMatch && format){
19166 var fvalue = d.dateFormat(format);
19167 if(ddMatch.test(fvalue)){
19168 cell.title = ddText.replace("%0", fvalue);
19169 cell.className = " fc-state-disabled";
19173 if (!cell.initialClassName) {
19174 cell.initialClassName = cell.dom.className;
19177 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19182 for(; i < startingPos; i++) {
19183 textEls[i].innerHTML = (++prevStart);
19184 d.setDate(d.getDate()+1);
19186 cells[i].className = "fc-past fc-other-month";
19187 setCellClass(this, cells[i]);
19192 for(; i < days; i++){
19193 intDay = i - startingPos + 1;
19194 textEls[i].innerHTML = (intDay);
19195 d.setDate(d.getDate()+1);
19197 cells[i].className = ''; // "x-date-active";
19198 setCellClass(this, cells[i]);
19202 for(; i < 42; i++) {
19203 textEls[i].innerHTML = (++extraDays);
19204 d.setDate(d.getDate()+1);
19206 cells[i].className = "fc-future fc-other-month";
19207 setCellClass(this, cells[i]);
19210 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19212 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19214 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19217 if(totalRows != 6){
19218 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19222 this.fireEvent('monthchange', this, date);
19226 if(!this.internalRender){
19227 var main = this.el.dom.firstChild;
19228 var w = main.offsetWidth;
19229 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230 Roo.fly(main).setWidth(w);
19231 this.internalRender = true;
19232 // opera does not respect the auto grow header center column
19233 // then, after it gets a width opera refuses to recalculate
19234 // without a second pass
19235 if(Roo.isOpera && !this.secondPass){
19236 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237 this.secondPass = true;
19238 this.update.defer(10, this, [date]);
19245 findCell : function(dt) {
19246 dt = dt.clearTime().getTime();
19248 this.cells.each(function(c){
19249 //Roo.log("check " +c.dateValue + '?=' + dt);
19250 if(c.dateValue == dt){
19260 findCells : function(ev) {
19261 var s = ev.start.clone().clearTime().getTime();
19263 var e= ev.end.clone().clearTime().getTime();
19266 this.cells.each(function(c){
19267 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19269 if(c.dateValue > e){
19272 if(c.dateValue < s){
19281 // findBestRow: function(cells)
19285 // for (var i =0 ; i < cells.length;i++) {
19286 // ret = Math.max(cells[i].rows || 0,ret);
19293 addItem : function(ev)
19295 // look for vertical location slot in
19296 var cells = this.findCells(ev);
19298 // ev.row = this.findBestRow(cells);
19300 // work out the location.
19304 for(var i =0; i < cells.length; i++) {
19306 cells[i].row = cells[0].row;
19309 cells[i].row = cells[i].row + 1;
19319 if (crow.start.getY() == cells[i].getY()) {
19321 crow.end = cells[i];
19338 cells[0].events.push(ev);
19340 this.calevents.push(ev);
19343 clearEvents: function() {
19345 if(!this.calevents){
19349 Roo.each(this.cells.elements, function(c){
19355 Roo.each(this.calevents, function(e) {
19356 Roo.each(e.els, function(el) {
19357 el.un('mouseenter' ,this.onEventEnter, this);
19358 el.un('mouseleave' ,this.onEventLeave, this);
19363 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369 renderEvents: function()
19373 this.cells.each(function(c) {
19382 if(c.row != c.events.length){
19383 r = 4 - (4 - (c.row - c.events.length));
19386 c.events = ev.slice(0, r);
19387 c.more = ev.slice(r);
19389 if(c.more.length && c.more.length == 1){
19390 c.events.push(c.more.pop());
19393 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19397 this.cells.each(function(c) {
19399 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19402 for (var e = 0; e < c.events.length; e++){
19403 var ev = c.events[e];
19404 var rows = ev.rows;
19406 for(var i = 0; i < rows.length; i++) {
19408 // how many rows should it span..
19411 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19414 unselectable : "on",
19417 cls: 'fc-event-inner',
19421 // cls: 'fc-event-time',
19422 // html : cells.length > 1 ? '' : ev.time
19426 cls: 'fc-event-title',
19427 html : String.format('{0}', ev.title)
19434 cls: 'ui-resizable-handle ui-resizable-e',
19435 html : '  '
19442 cfg.cls += ' fc-event-start';
19444 if ((i+1) == rows.length) {
19445 cfg.cls += ' fc-event-end';
19448 var ctr = _this.el.select('.fc-event-container',true).first();
19449 var cg = ctr.createChild(cfg);
19451 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19454 var r = (c.more.length) ? 1 : 0;
19455 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19456 cg.setWidth(ebox.right - sbox.x -2);
19458 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460 cg.on('click', _this.onEventClick, _this, ev);
19471 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472 style : 'position: absolute',
19473 unselectable : "on",
19476 cls: 'fc-event-inner',
19480 cls: 'fc-event-title',
19488 cls: 'ui-resizable-handle ui-resizable-e',
19489 html : '  '
19495 var ctr = _this.el.select('.fc-event-container',true).first();
19496 var cg = ctr.createChild(cfg);
19498 var sbox = c.select('.fc-day-content',true).first().getBox();
19499 var ebox = c.select('.fc-day-content',true).first().getBox();
19501 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19502 cg.setWidth(ebox.right - sbox.x -2);
19504 cg.on('click', _this.onMoreEventClick, _this, c.more);
19514 onEventEnter: function (e, el,event,d) {
19515 this.fireEvent('evententer', this, el, event);
19518 onEventLeave: function (e, el,event,d) {
19519 this.fireEvent('eventleave', this, el, event);
19522 onEventClick: function (e, el,event,d) {
19523 this.fireEvent('eventclick', this, el, event);
19526 onMonthChange: function () {
19530 onMoreEventClick: function(e, el, more)
19534 this.calpopover.placement = 'right';
19535 this.calpopover.setTitle('More');
19537 this.calpopover.setContent('');
19539 var ctr = this.calpopover.el.select('.popover-content', true).first();
19541 Roo.each(more, function(m){
19543 cls : 'fc-event-hori fc-event-draggable',
19546 var cg = ctr.createChild(cfg);
19548 cg.on('click', _this.onEventClick, _this, m);
19551 this.calpopover.show(el);
19556 onLoad: function ()
19558 this.calevents = [];
19561 if(this.store.getCount() > 0){
19562 this.store.data.each(function(d){
19565 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567 time : d.data.start_time,
19568 title : d.data.title,
19569 description : d.data.description,
19570 venue : d.data.venue
19575 this.renderEvents();
19577 if(this.calevents.length && this.loadMask){
19578 this.maskEl.hide();
19582 onBeforeLoad: function()
19584 this.clearEvents();
19586 this.maskEl.show();
19600 * @class Roo.bootstrap.Popover
19601 * @extends Roo.bootstrap.Component
19602 * Bootstrap Popover class
19603 * @cfg {String} html contents of the popover (or false to use children..)
19604 * @cfg {String} title of popover (or false to hide)
19605 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606 * @cfg {String} trigger click || hover (or false to trigger manually)
19607 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609 * - if false and it has a 'parent' then it will be automatically added to that element
19610 * - if string - Roo.get will be called
19611 * @cfg {Number} delay - delay before showing
19614 * Create a new Popover
19615 * @param {Object} config The config object
19618 Roo.bootstrap.Popover = function(config){
19619 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625 * After the popover show
19627 * @param {Roo.bootstrap.Popover} this
19632 * After the popover hide
19634 * @param {Roo.bootstrap.Popover} this
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19645 placement : 'right',
19646 trigger : 'hover', // hover
19652 can_build_overlaid : false,
19654 maskEl : false, // the mask element
19659 getChildContainer : function()
19661 return this.contentEl;
19664 getPopoverHeader : function()
19666 this.title = true; // flag not to hide it..
19667 return this.headerEl
19671 getAutoCreate : function(){
19674 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19675 style: 'display:block',
19681 cls : 'popover-inner ',
19685 cls: 'popover-title popover-header',
19686 html : this.title === false ? '' : this.title
19689 cls : 'popover-content popover-body ' + (this.cls || ''),
19690 html : this.html || ''
19701 * @param {string} the title
19703 setTitle: function(str)
19707 this.headerEl.dom.innerHTML = str;
19712 * @param {string} the body content
19714 setContent: function(str)
19717 if (this.contentEl) {
19718 this.contentEl.dom.innerHTML = str;
19722 // as it get's added to the bottom of the page.
19723 onRender : function(ct, position)
19725 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19730 var cfg = Roo.apply({}, this.getAutoCreate());
19734 cfg.cls += ' ' + this.cls;
19737 cfg.style = this.style;
19739 //Roo.log("adding to ");
19740 this.el = Roo.get(document.body).createChild(cfg, position);
19741 // Roo.log(this.el);
19744 this.contentEl = this.el.select('.popover-content',true).first();
19745 this.headerEl = this.el.select('.popover-title',true).first();
19748 if(typeof(this.items) != 'undefined'){
19749 var items = this.items;
19752 for(var i =0;i < items.length;i++) {
19753 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19757 this.items = nitems;
19759 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19760 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767 resizeMask : function()
19769 this.maskEl.setSize(
19770 Roo.lib.Dom.getViewWidth(true),
19771 Roo.lib.Dom.getViewHeight(true)
19775 initEvents : function()
19779 Roo.bootstrap.Popover.register(this);
19783 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19784 this.el.enableDisplayMode('block');
19786 if (this.over === false && !this.parent()) {
19789 if (this.triggers === false) {
19794 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19795 var triggers = this.trigger ? this.trigger.split(' ') : [];
19796 Roo.each(triggers, function(trigger) {
19798 if (trigger == 'click') {
19799 on_el.on('click', this.toggle, this);
19800 } else if (trigger != 'manual') {
19801 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19802 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19804 on_el.on(eventIn ,this.enter, this);
19805 on_el.on(eventOut, this.leave, this);
19816 toggle : function () {
19817 this.hoverState == 'in' ? this.leave() : this.enter();
19820 enter : function () {
19822 clearTimeout(this.timeout);
19824 this.hoverState = 'in';
19826 if (!this.delay || !this.delay.show) {
19831 this.timeout = setTimeout(function () {
19832 if (_t.hoverState == 'in') {
19835 }, this.delay.show)
19838 leave : function() {
19839 clearTimeout(this.timeout);
19841 this.hoverState = 'out';
19843 if (!this.delay || !this.delay.hide) {
19848 this.timeout = setTimeout(function () {
19849 if (_t.hoverState == 'out') {
19852 }, this.delay.hide)
19856 * @param {Roo.Element|string|false} - element to align and point to.
19858 show : function (on_el)
19861 on_el = on_el || false; // default to false
19863 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19864 on_el = this.parent().el;
19865 } else if (this.over) {
19866 Roo.get(this.over);
19872 this.render(document.body);
19876 this.el.removeClass([
19877 'fade','top','bottom', 'left', 'right','in',
19878 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19881 if (this.title === false) {
19882 this.headerEl.hide();
19886 var placement = typeof this.placement == 'function' ?
19887 this.placement.call(this, this.el, on_el) :
19891 var autoToken = /\s?auto?\s?/i; /// not sure how this was supposed to work? right auto ? what?
19893 // I think 'auto right' - but
19895 var autoPlace = autoToken.test(placement);
19897 placement = placement.replace(autoToken, '') || 'top';
19903 this.el.dom.style.display='block';
19905 //this.el.appendTo(on_el);
19907 var p = this.getPosition();
19908 var box = this.el.getBox();
19911 var align = Roo.bootstrap.Popover.alignment[placement];
19912 this.el.addClass(align[2]);
19917 this.el.alignTo(on_el, align[0],align[1]);
19919 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19920 var es = this.el.getSize();
19921 var x = Roo.lib.Dom.getViewWidth()/2;
19922 var y = Roo.lib.Dom.getViewHeight()/2;
19923 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19928 //var arrow = this.el.select('.arrow',true).first();
19929 //arrow.set(align[2],
19931 this.el.addClass('in');
19934 if (this.el.hasClass('fade')) {
19938 this.hoverState = 'in';
19941 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19942 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19943 this.maskEl.dom.style.display = 'block';
19944 this.maskEl.addClass('show');
19946 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19950 this.fireEvent('show', this);
19955 this.el.setXY([0,0]);
19956 this.el.removeClass('in');
19958 this.hoverState = null;
19959 this.maskEl.hide(); // always..
19960 this.fireEvent('hide', this);
19966 Roo.apply(Roo.bootstrap.Popover, {
19969 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19970 'right' : ['l-br', [10,0], 'right bs-popover-right'],
19971 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19972 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19977 clickHander : false,
19980 onMouseDown : function(e)
19982 if (!e.getTarget(".roo-popover")) {
19990 register : function(popup)
19992 if (!Roo.bootstrap.Popover.clickHandler) {
19993 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19995 // hide other popups.
19997 this.popups.push(popup);
19999 hideAll : function()
20001 this.popups.forEach(function(p) {
20009 * Card header - holder for the card header elements.
20014 * @class Roo.bootstrap.PopoverNav
20015 * @extends Roo.bootstrap.NavGroup
20016 * Bootstrap Popover header navigation class
20018 * Create a new Popover Header Navigation
20019 * @param {Object} config The config object
20022 Roo.bootstrap.PopoverNav = function(config){
20023 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20026 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavGroup, {
20029 container_method : 'getPopoverHeader'
20047 * @class Roo.bootstrap.Progress
20048 * @extends Roo.bootstrap.Component
20049 * Bootstrap Progress class
20050 * @cfg {Boolean} striped striped of the progress bar
20051 * @cfg {Boolean} active animated of the progress bar
20055 * Create a new Progress
20056 * @param {Object} config The config object
20059 Roo.bootstrap.Progress = function(config){
20060 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20063 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20068 getAutoCreate : function(){
20076 cfg.cls += ' progress-striped';
20080 cfg.cls += ' active';
20099 * @class Roo.bootstrap.ProgressBar
20100 * @extends Roo.bootstrap.Component
20101 * Bootstrap ProgressBar class
20102 * @cfg {Number} aria_valuenow aria-value now
20103 * @cfg {Number} aria_valuemin aria-value min
20104 * @cfg {Number} aria_valuemax aria-value max
20105 * @cfg {String} label label for the progress bar
20106 * @cfg {String} panel (success | info | warning | danger )
20107 * @cfg {String} role role of the progress bar
20108 * @cfg {String} sr_only text
20112 * Create a new ProgressBar
20113 * @param {Object} config The config object
20116 Roo.bootstrap.ProgressBar = function(config){
20117 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20120 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20124 aria_valuemax : 100,
20130 getAutoCreate : function()
20135 cls: 'progress-bar',
20136 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20148 cfg.role = this.role;
20151 if(this.aria_valuenow){
20152 cfg['aria-valuenow'] = this.aria_valuenow;
20155 if(this.aria_valuemin){
20156 cfg['aria-valuemin'] = this.aria_valuemin;
20159 if(this.aria_valuemax){
20160 cfg['aria-valuemax'] = this.aria_valuemax;
20163 if(this.label && !this.sr_only){
20164 cfg.html = this.label;
20168 cfg.cls += ' progress-bar-' + this.panel;
20174 update : function(aria_valuenow)
20176 this.aria_valuenow = aria_valuenow;
20178 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20193 * @class Roo.bootstrap.TabGroup
20194 * @extends Roo.bootstrap.Column
20195 * Bootstrap Column class
20196 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20197 * @cfg {Boolean} carousel true to make the group behave like a carousel
20198 * @cfg {Boolean} bullets show bullets for the panels
20199 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20200 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20201 * @cfg {Boolean} showarrow (true|false) show arrow default true
20204 * Create a new TabGroup
20205 * @param {Object} config The config object
20208 Roo.bootstrap.TabGroup = function(config){
20209 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20211 this.navId = Roo.id();
20214 Roo.bootstrap.TabGroup.register(this);
20218 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20221 transition : false,
20226 slideOnTouch : false,
20229 getAutoCreate : function()
20231 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20233 cfg.cls += ' tab-content';
20235 if (this.carousel) {
20236 cfg.cls += ' carousel slide';
20239 cls : 'carousel-inner',
20243 if(this.bullets && !Roo.isTouch){
20246 cls : 'carousel-bullets',
20250 if(this.bullets_cls){
20251 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20258 cfg.cn[0].cn.push(bullets);
20261 if(this.showarrow){
20262 cfg.cn[0].cn.push({
20264 class : 'carousel-arrow',
20268 class : 'carousel-prev',
20272 class : 'fa fa-chevron-left'
20278 class : 'carousel-next',
20282 class : 'fa fa-chevron-right'
20295 initEvents: function()
20297 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20298 // this.el.on("touchstart", this.onTouchStart, this);
20301 if(this.autoslide){
20304 this.slideFn = window.setInterval(function() {
20305 _this.showPanelNext();
20309 if(this.showarrow){
20310 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20311 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20317 // onTouchStart : function(e, el, o)
20319 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20323 // this.showPanelNext();
20327 getChildContainer : function()
20329 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20333 * register a Navigation item
20334 * @param {Roo.bootstrap.NavItem} the navitem to add
20336 register : function(item)
20338 this.tabs.push( item);
20339 item.navId = this.navId; // not really needed..
20344 getActivePanel : function()
20347 Roo.each(this.tabs, function(t) {
20357 getPanelByName : function(n)
20360 Roo.each(this.tabs, function(t) {
20361 if (t.tabId == n) {
20369 indexOfPanel : function(p)
20372 Roo.each(this.tabs, function(t,i) {
20373 if (t.tabId == p.tabId) {
20382 * show a specific panel
20383 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20384 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20386 showPanel : function (pan)
20388 if(this.transition || typeof(pan) == 'undefined'){
20389 Roo.log("waiting for the transitionend");
20393 if (typeof(pan) == 'number') {
20394 pan = this.tabs[pan];
20397 if (typeof(pan) == 'string') {
20398 pan = this.getPanelByName(pan);
20401 var cur = this.getActivePanel();
20404 Roo.log('pan or acitve pan is undefined');
20408 if (pan.tabId == this.getActivePanel().tabId) {
20412 if (false === cur.fireEvent('beforedeactivate')) {
20416 if(this.bullets > 0 && !Roo.isTouch){
20417 this.setActiveBullet(this.indexOfPanel(pan));
20420 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20422 //class="carousel-item carousel-item-next carousel-item-left"
20424 this.transition = true;
20425 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20426 var lr = dir == 'next' ? 'left' : 'right';
20427 pan.el.addClass(dir); // or prev
20428 pan.el.addClass('carousel-item-' + dir); // or prev
20429 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20430 cur.el.addClass(lr); // or right
20431 pan.el.addClass(lr);
20432 cur.el.addClass('carousel-item-' +lr); // or right
20433 pan.el.addClass('carousel-item-' +lr);
20437 cur.el.on('transitionend', function() {
20438 Roo.log("trans end?");
20440 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20441 pan.setActive(true);
20443 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20444 cur.setActive(false);
20446 _this.transition = false;
20448 }, this, { single: true } );
20453 cur.setActive(false);
20454 pan.setActive(true);
20459 showPanelNext : function()
20461 var i = this.indexOfPanel(this.getActivePanel());
20463 if (i >= this.tabs.length - 1 && !this.autoslide) {
20467 if (i >= this.tabs.length - 1 && this.autoslide) {
20471 this.showPanel(this.tabs[i+1]);
20474 showPanelPrev : function()
20476 var i = this.indexOfPanel(this.getActivePanel());
20478 if (i < 1 && !this.autoslide) {
20482 if (i < 1 && this.autoslide) {
20483 i = this.tabs.length;
20486 this.showPanel(this.tabs[i-1]);
20490 addBullet: function()
20492 if(!this.bullets || Roo.isTouch){
20495 var ctr = this.el.select('.carousel-bullets',true).first();
20496 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20497 var bullet = ctr.createChild({
20498 cls : 'bullet bullet-' + i
20499 },ctr.dom.lastChild);
20504 bullet.on('click', (function(e, el, o, ii, t){
20506 e.preventDefault();
20508 this.showPanel(ii);
20510 if(this.autoslide && this.slideFn){
20511 clearInterval(this.slideFn);
20512 this.slideFn = window.setInterval(function() {
20513 _this.showPanelNext();
20517 }).createDelegate(this, [i, bullet], true));
20522 setActiveBullet : function(i)
20528 Roo.each(this.el.select('.bullet', true).elements, function(el){
20529 el.removeClass('selected');
20532 var bullet = this.el.select('.bullet-' + i, true).first();
20538 bullet.addClass('selected');
20549 Roo.apply(Roo.bootstrap.TabGroup, {
20553 * register a Navigation Group
20554 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20556 register : function(navgrp)
20558 this.groups[navgrp.navId] = navgrp;
20562 * fetch a Navigation Group based on the navigation ID
20563 * if one does not exist , it will get created.
20564 * @param {string} the navgroup to add
20565 * @returns {Roo.bootstrap.NavGroup} the navgroup
20567 get: function(navId) {
20568 if (typeof(this.groups[navId]) == 'undefined') {
20569 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20571 return this.groups[navId] ;
20586 * @class Roo.bootstrap.TabPanel
20587 * @extends Roo.bootstrap.Component
20588 * Bootstrap TabPanel class
20589 * @cfg {Boolean} active panel active
20590 * @cfg {String} html panel content
20591 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20592 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20593 * @cfg {String} href click to link..
20594 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20598 * Create a new TabPanel
20599 * @param {Object} config The config object
20602 Roo.bootstrap.TabPanel = function(config){
20603 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20607 * Fires when the active status changes
20608 * @param {Roo.bootstrap.TabPanel} this
20609 * @param {Boolean} state the new state
20614 * @event beforedeactivate
20615 * Fires before a tab is de-activated - can be used to do validation on a form.
20616 * @param {Roo.bootstrap.TabPanel} this
20617 * @return {Boolean} false if there is an error
20620 'beforedeactivate': true
20623 this.tabId = this.tabId || Roo.id();
20627 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20634 touchSlide : false,
20635 getAutoCreate : function(){
20640 // item is needed for carousel - not sure if it has any effect otherwise
20641 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20642 html: this.html || ''
20646 cfg.cls += ' active';
20650 cfg.tabId = this.tabId;
20658 initEvents: function()
20660 var p = this.parent();
20662 this.navId = this.navId || p.navId;
20664 if (typeof(this.navId) != 'undefined') {
20665 // not really needed.. but just in case.. parent should be a NavGroup.
20666 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20670 var i = tg.tabs.length - 1;
20672 if(this.active && tg.bullets > 0 && i < tg.bullets){
20673 tg.setActiveBullet(i);
20677 this.el.on('click', this.onClick, this);
20679 if(Roo.isTouch && this.touchSlide){
20680 this.el.on("touchstart", this.onTouchStart, this);
20681 this.el.on("touchmove", this.onTouchMove, this);
20682 this.el.on("touchend", this.onTouchEnd, this);
20687 onRender : function(ct, position)
20689 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20692 setActive : function(state)
20694 Roo.log("panel - set active " + this.tabId + "=" + state);
20696 this.active = state;
20698 this.el.removeClass('active');
20700 } else if (!this.el.hasClass('active')) {
20701 this.el.addClass('active');
20704 this.fireEvent('changed', this, state);
20707 onClick : function(e)
20709 e.preventDefault();
20711 if(!this.href.length){
20715 window.location.href = this.href;
20724 onTouchStart : function(e)
20726 this.swiping = false;
20728 this.startX = e.browserEvent.touches[0].clientX;
20729 this.startY = e.browserEvent.touches[0].clientY;
20732 onTouchMove : function(e)
20734 this.swiping = true;
20736 this.endX = e.browserEvent.touches[0].clientX;
20737 this.endY = e.browserEvent.touches[0].clientY;
20740 onTouchEnd : function(e)
20747 var tabGroup = this.parent();
20749 if(this.endX > this.startX){ // swiping right
20750 tabGroup.showPanelPrev();
20754 if(this.startX > this.endX){ // swiping left
20755 tabGroup.showPanelNext();
20774 * @class Roo.bootstrap.DateField
20775 * @extends Roo.bootstrap.Input
20776 * Bootstrap DateField class
20777 * @cfg {Number} weekStart default 0
20778 * @cfg {String} viewMode default empty, (months|years)
20779 * @cfg {String} minViewMode default empty, (months|years)
20780 * @cfg {Number} startDate default -Infinity
20781 * @cfg {Number} endDate default Infinity
20782 * @cfg {Boolean} todayHighlight default false
20783 * @cfg {Boolean} todayBtn default false
20784 * @cfg {Boolean} calendarWeeks default false
20785 * @cfg {Object} daysOfWeekDisabled default empty
20786 * @cfg {Boolean} singleMode default false (true | false)
20788 * @cfg {Boolean} keyboardNavigation default true
20789 * @cfg {String} language default en
20792 * Create a new DateField
20793 * @param {Object} config The config object
20796 Roo.bootstrap.DateField = function(config){
20797 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20801 * Fires when this field show.
20802 * @param {Roo.bootstrap.DateField} this
20803 * @param {Mixed} date The date value
20808 * Fires when this field hide.
20809 * @param {Roo.bootstrap.DateField} this
20810 * @param {Mixed} date The date value
20815 * Fires when select a date.
20816 * @param {Roo.bootstrap.DateField} this
20817 * @param {Mixed} date The date value
20821 * @event beforeselect
20822 * Fires when before select a date.
20823 * @param {Roo.bootstrap.DateField} this
20824 * @param {Mixed} date The date value
20826 beforeselect : true
20830 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20833 * @cfg {String} format
20834 * The default date format string which can be overriden for localization support. The format must be
20835 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20839 * @cfg {String} altFormats
20840 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20841 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20843 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20851 todayHighlight : false,
20857 keyboardNavigation: true,
20859 calendarWeeks: false,
20861 startDate: -Infinity,
20865 daysOfWeekDisabled: [],
20869 singleMode : false,
20871 UTCDate: function()
20873 return new Date(Date.UTC.apply(Date, arguments));
20876 UTCToday: function()
20878 var today = new Date();
20879 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20882 getDate: function() {
20883 var d = this.getUTCDate();
20884 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20887 getUTCDate: function() {
20891 setDate: function(d) {
20892 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20895 setUTCDate: function(d) {
20897 this.setValue(this.formatDate(this.date));
20900 onRender: function(ct, position)
20903 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20905 this.language = this.language || 'en';
20906 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20907 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20909 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20910 this.format = this.format || 'm/d/y';
20911 this.isInline = false;
20912 this.isInput = true;
20913 this.component = this.el.select('.add-on', true).first() || false;
20914 this.component = (this.component && this.component.length === 0) ? false : this.component;
20915 this.hasInput = this.component && this.inputEl().length;
20917 if (typeof(this.minViewMode === 'string')) {
20918 switch (this.minViewMode) {
20920 this.minViewMode = 1;
20923 this.minViewMode = 2;
20926 this.minViewMode = 0;
20931 if (typeof(this.viewMode === 'string')) {
20932 switch (this.viewMode) {
20945 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20947 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20949 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20951 this.picker().on('mousedown', this.onMousedown, this);
20952 this.picker().on('click', this.onClick, this);
20954 this.picker().addClass('datepicker-dropdown');
20956 this.startViewMode = this.viewMode;
20958 if(this.singleMode){
20959 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20960 v.setVisibilityMode(Roo.Element.DISPLAY);
20964 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20965 v.setStyle('width', '189px');
20969 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20970 if(!this.calendarWeeks){
20975 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20976 v.attr('colspan', function(i, val){
20977 return parseInt(val) + 1;
20982 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20984 this.setStartDate(this.startDate);
20985 this.setEndDate(this.endDate);
20987 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20994 if(this.isInline) {
20999 picker : function()
21001 return this.pickerEl;
21002 // return this.el.select('.datepicker', true).first();
21005 fillDow: function()
21007 var dowCnt = this.weekStart;
21016 if(this.calendarWeeks){
21024 while (dowCnt < this.weekStart + 7) {
21028 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21032 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21035 fillMonths: function()
21038 var months = this.picker().select('>.datepicker-months td', true).first();
21040 months.dom.innerHTML = '';
21046 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21049 months.createChild(month);
21056 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;
21058 if (this.date < this.startDate) {
21059 this.viewDate = new Date(this.startDate);
21060 } else if (this.date > this.endDate) {
21061 this.viewDate = new Date(this.endDate);
21063 this.viewDate = new Date(this.date);
21071 var d = new Date(this.viewDate),
21072 year = d.getUTCFullYear(),
21073 month = d.getUTCMonth(),
21074 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21075 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21076 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21077 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21078 currentDate = this.date && this.date.valueOf(),
21079 today = this.UTCToday();
21081 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21083 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21085 // this.picker.select('>tfoot th.today').
21086 // .text(dates[this.language].today)
21087 // .toggle(this.todayBtn !== false);
21089 this.updateNavArrows();
21092 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21094 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21096 prevMonth.setUTCDate(day);
21098 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21100 var nextMonth = new Date(prevMonth);
21102 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21104 nextMonth = nextMonth.valueOf();
21106 var fillMonths = false;
21108 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21110 while(prevMonth.valueOf() <= nextMonth) {
21113 if (prevMonth.getUTCDay() === this.weekStart) {
21115 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21123 if(this.calendarWeeks){
21124 // ISO 8601: First week contains first thursday.
21125 // ISO also states week starts on Monday, but we can be more abstract here.
21127 // Start of current week: based on weekstart/current date
21128 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21129 // Thursday of this week
21130 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21131 // First Thursday of year, year from thursday
21132 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21133 // Calendar week: ms between thursdays, div ms per day, div 7 days
21134 calWeek = (th - yth) / 864e5 / 7 + 1;
21136 fillMonths.cn.push({
21144 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21146 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21149 if (this.todayHighlight &&
21150 prevMonth.getUTCFullYear() == today.getFullYear() &&
21151 prevMonth.getUTCMonth() == today.getMonth() &&
21152 prevMonth.getUTCDate() == today.getDate()) {
21153 clsName += ' today';
21156 if (currentDate && prevMonth.valueOf() === currentDate) {
21157 clsName += ' active';
21160 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21161 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21162 clsName += ' disabled';
21165 fillMonths.cn.push({
21167 cls: 'day ' + clsName,
21168 html: prevMonth.getDate()
21171 prevMonth.setDate(prevMonth.getDate()+1);
21174 var currentYear = this.date && this.date.getUTCFullYear();
21175 var currentMonth = this.date && this.date.getUTCMonth();
21177 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21179 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21180 v.removeClass('active');
21182 if(currentYear === year && k === currentMonth){
21183 v.addClass('active');
21186 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21187 v.addClass('disabled');
21193 year = parseInt(year/10, 10) * 10;
21195 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21197 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21200 for (var i = -1; i < 11; i++) {
21201 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21203 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21211 showMode: function(dir)
21214 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21217 Roo.each(this.picker().select('>div',true).elements, function(v){
21218 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21221 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21226 if(this.isInline) {
21230 this.picker().removeClass(['bottom', 'top']);
21232 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21234 * place to the top of element!
21238 this.picker().addClass('top');
21239 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21244 this.picker().addClass('bottom');
21246 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21249 parseDate : function(value)
21251 if(!value || value instanceof Date){
21254 var v = Date.parseDate(value, this.format);
21255 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21256 v = Date.parseDate(value, 'Y-m-d');
21258 if(!v && this.altFormats){
21259 if(!this.altFormatsArray){
21260 this.altFormatsArray = this.altFormats.split("|");
21262 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21263 v = Date.parseDate(value, this.altFormatsArray[i]);
21269 formatDate : function(date, fmt)
21271 return (!date || !(date instanceof Date)) ?
21272 date : date.dateFormat(fmt || this.format);
21275 onFocus : function()
21277 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21281 onBlur : function()
21283 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21285 var d = this.inputEl().getValue();
21292 showPopup : function()
21294 this.picker().show();
21298 this.fireEvent('showpopup', this, this.date);
21301 hidePopup : function()
21303 if(this.isInline) {
21306 this.picker().hide();
21307 this.viewMode = this.startViewMode;
21310 this.fireEvent('hidepopup', this, this.date);
21314 onMousedown: function(e)
21316 e.stopPropagation();
21317 e.preventDefault();
21322 Roo.bootstrap.DateField.superclass.keyup.call(this);
21326 setValue: function(v)
21328 if(this.fireEvent('beforeselect', this, v) !== false){
21329 var d = new Date(this.parseDate(v) ).clearTime();
21331 if(isNaN(d.getTime())){
21332 this.date = this.viewDate = '';
21333 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21337 v = this.formatDate(d);
21339 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21341 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21345 this.fireEvent('select', this, this.date);
21349 getValue: function()
21351 return this.formatDate(this.date);
21354 fireKey: function(e)
21356 if (!this.picker().isVisible()){
21357 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21363 var dateChanged = false,
21365 newDate, newViewDate;
21370 e.preventDefault();
21374 if (!this.keyboardNavigation) {
21377 dir = e.keyCode == 37 ? -1 : 1;
21380 newDate = this.moveYear(this.date, dir);
21381 newViewDate = this.moveYear(this.viewDate, dir);
21382 } else if (e.shiftKey){
21383 newDate = this.moveMonth(this.date, dir);
21384 newViewDate = this.moveMonth(this.viewDate, dir);
21386 newDate = new Date(this.date);
21387 newDate.setUTCDate(this.date.getUTCDate() + dir);
21388 newViewDate = new Date(this.viewDate);
21389 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21391 if (this.dateWithinRange(newDate)){
21392 this.date = newDate;
21393 this.viewDate = newViewDate;
21394 this.setValue(this.formatDate(this.date));
21396 e.preventDefault();
21397 dateChanged = true;
21402 if (!this.keyboardNavigation) {
21405 dir = e.keyCode == 38 ? -1 : 1;
21407 newDate = this.moveYear(this.date, dir);
21408 newViewDate = this.moveYear(this.viewDate, dir);
21409 } else if (e.shiftKey){
21410 newDate = this.moveMonth(this.date, dir);
21411 newViewDate = this.moveMonth(this.viewDate, dir);
21413 newDate = new Date(this.date);
21414 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21415 newViewDate = new Date(this.viewDate);
21416 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21418 if (this.dateWithinRange(newDate)){
21419 this.date = newDate;
21420 this.viewDate = newViewDate;
21421 this.setValue(this.formatDate(this.date));
21423 e.preventDefault();
21424 dateChanged = true;
21428 this.setValue(this.formatDate(this.date));
21430 e.preventDefault();
21433 this.setValue(this.formatDate(this.date));
21447 onClick: function(e)
21449 e.stopPropagation();
21450 e.preventDefault();
21452 var target = e.getTarget();
21454 if(target.nodeName.toLowerCase() === 'i'){
21455 target = Roo.get(target).dom.parentNode;
21458 var nodeName = target.nodeName;
21459 var className = target.className;
21460 var html = target.innerHTML;
21461 //Roo.log(nodeName);
21463 switch(nodeName.toLowerCase()) {
21465 switch(className) {
21471 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21472 switch(this.viewMode){
21474 this.viewDate = this.moveMonth(this.viewDate, dir);
21478 this.viewDate = this.moveYear(this.viewDate, dir);
21484 var date = new Date();
21485 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21487 this.setValue(this.formatDate(this.date));
21494 if (className.indexOf('disabled') < 0) {
21495 this.viewDate.setUTCDate(1);
21496 if (className.indexOf('month') > -1) {
21497 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21499 var year = parseInt(html, 10) || 0;
21500 this.viewDate.setUTCFullYear(year);
21504 if(this.singleMode){
21505 this.setValue(this.formatDate(this.viewDate));
21516 //Roo.log(className);
21517 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21518 var day = parseInt(html, 10) || 1;
21519 var year = this.viewDate.getUTCFullYear(),
21520 month = this.viewDate.getUTCMonth();
21522 if (className.indexOf('old') > -1) {
21529 } else if (className.indexOf('new') > -1) {
21537 //Roo.log([year,month,day]);
21538 this.date = this.UTCDate(year, month, day,0,0,0,0);
21539 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21541 //Roo.log(this.formatDate(this.date));
21542 this.setValue(this.formatDate(this.date));
21549 setStartDate: function(startDate)
21551 this.startDate = startDate || -Infinity;
21552 if (this.startDate !== -Infinity) {
21553 this.startDate = this.parseDate(this.startDate);
21556 this.updateNavArrows();
21559 setEndDate: function(endDate)
21561 this.endDate = endDate || Infinity;
21562 if (this.endDate !== Infinity) {
21563 this.endDate = this.parseDate(this.endDate);
21566 this.updateNavArrows();
21569 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21571 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21572 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21573 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21575 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21576 return parseInt(d, 10);
21579 this.updateNavArrows();
21582 updateNavArrows: function()
21584 if(this.singleMode){
21588 var d = new Date(this.viewDate),
21589 year = d.getUTCFullYear(),
21590 month = d.getUTCMonth();
21592 Roo.each(this.picker().select('.prev', true).elements, function(v){
21594 switch (this.viewMode) {
21597 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21603 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21610 Roo.each(this.picker().select('.next', true).elements, function(v){
21612 switch (this.viewMode) {
21615 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21621 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21629 moveMonth: function(date, dir)
21634 var new_date = new Date(date.valueOf()),
21635 day = new_date.getUTCDate(),
21636 month = new_date.getUTCMonth(),
21637 mag = Math.abs(dir),
21639 dir = dir > 0 ? 1 : -1;
21642 // If going back one month, make sure month is not current month
21643 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21645 return new_date.getUTCMonth() == month;
21647 // If going forward one month, make sure month is as expected
21648 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21650 return new_date.getUTCMonth() != new_month;
21652 new_month = month + dir;
21653 new_date.setUTCMonth(new_month);
21654 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21655 if (new_month < 0 || new_month > 11) {
21656 new_month = (new_month + 12) % 12;
21659 // For magnitudes >1, move one month at a time...
21660 for (var i=0; i<mag; i++) {
21661 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21662 new_date = this.moveMonth(new_date, dir);
21664 // ...then reset the day, keeping it in the new month
21665 new_month = new_date.getUTCMonth();
21666 new_date.setUTCDate(day);
21668 return new_month != new_date.getUTCMonth();
21671 // Common date-resetting loop -- if date is beyond end of month, make it
21674 new_date.setUTCDate(--day);
21675 new_date.setUTCMonth(new_month);
21680 moveYear: function(date, dir)
21682 return this.moveMonth(date, dir*12);
21685 dateWithinRange: function(date)
21687 return date >= this.startDate && date <= this.endDate;
21693 this.picker().remove();
21696 validateValue : function(value)
21698 if(this.getVisibilityEl().hasClass('hidden')){
21702 if(value.length < 1) {
21703 if(this.allowBlank){
21709 if(value.length < this.minLength){
21712 if(value.length > this.maxLength){
21716 var vt = Roo.form.VTypes;
21717 if(!vt[this.vtype](value, this)){
21721 if(typeof this.validator == "function"){
21722 var msg = this.validator(value);
21728 if(this.regex && !this.regex.test(value)){
21732 if(typeof(this.parseDate(value)) == 'undefined'){
21736 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21740 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21750 this.date = this.viewDate = '';
21752 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21757 Roo.apply(Roo.bootstrap.DateField, {
21768 html: '<i class="fa fa-arrow-left"/>'
21778 html: '<i class="fa fa-arrow-right"/>'
21820 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21821 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21822 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21823 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21824 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21837 navFnc: 'FullYear',
21842 navFnc: 'FullYear',
21847 Roo.apply(Roo.bootstrap.DateField, {
21851 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21855 cls: 'datepicker-days',
21859 cls: 'table-condensed',
21861 Roo.bootstrap.DateField.head,
21865 Roo.bootstrap.DateField.footer
21872 cls: 'datepicker-months',
21876 cls: 'table-condensed',
21878 Roo.bootstrap.DateField.head,
21879 Roo.bootstrap.DateField.content,
21880 Roo.bootstrap.DateField.footer
21887 cls: 'datepicker-years',
21891 cls: 'table-condensed',
21893 Roo.bootstrap.DateField.head,
21894 Roo.bootstrap.DateField.content,
21895 Roo.bootstrap.DateField.footer
21914 * @class Roo.bootstrap.TimeField
21915 * @extends Roo.bootstrap.Input
21916 * Bootstrap DateField class
21920 * Create a new TimeField
21921 * @param {Object} config The config object
21924 Roo.bootstrap.TimeField = function(config){
21925 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21929 * Fires when this field show.
21930 * @param {Roo.bootstrap.DateField} thisthis
21931 * @param {Mixed} date The date value
21936 * Fires when this field hide.
21937 * @param {Roo.bootstrap.DateField} this
21938 * @param {Mixed} date The date value
21943 * Fires when select a date.
21944 * @param {Roo.bootstrap.DateField} this
21945 * @param {Mixed} date The date value
21951 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21954 * @cfg {String} format
21955 * The default time format string which can be overriden for localization support. The format must be
21956 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21960 onRender: function(ct, position)
21963 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21965 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21967 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21969 this.pop = this.picker().select('>.datepicker-time',true).first();
21970 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21972 this.picker().on('mousedown', this.onMousedown, this);
21973 this.picker().on('click', this.onClick, this);
21975 this.picker().addClass('datepicker-dropdown');
21980 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21981 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21982 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21983 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21984 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21985 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21989 fireKey: function(e){
21990 if (!this.picker().isVisible()){
21991 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21997 e.preventDefault();
22005 this.onTogglePeriod();
22008 this.onIncrementMinutes();
22011 this.onDecrementMinutes();
22020 onClick: function(e) {
22021 e.stopPropagation();
22022 e.preventDefault();
22025 picker : function()
22027 return this.el.select('.datepicker', true).first();
22030 fillTime: function()
22032 var time = this.pop.select('tbody', true).first();
22034 time.dom.innerHTML = '';
22049 cls: 'hours-up glyphicon glyphicon-chevron-up'
22069 cls: 'minutes-up glyphicon glyphicon-chevron-up'
22090 cls: 'timepicker-hour',
22105 cls: 'timepicker-minute',
22120 cls: 'btn btn-primary period',
22142 cls: 'hours-down glyphicon glyphicon-chevron-down'
22162 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22180 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22187 var hours = this.time.getHours();
22188 var minutes = this.time.getMinutes();
22201 hours = hours - 12;
22205 hours = '0' + hours;
22209 minutes = '0' + minutes;
22212 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22213 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22214 this.pop.select('button', true).first().dom.innerHTML = period;
22220 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22222 var cls = ['bottom'];
22224 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22231 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22236 this.picker().addClass(cls.join('-'));
22240 Roo.each(cls, function(c){
22242 _this.picker().setTop(_this.inputEl().getHeight());
22246 _this.picker().setTop(0 - _this.picker().getHeight());
22251 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22255 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22262 onFocus : function()
22264 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22268 onBlur : function()
22270 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22276 this.picker().show();
22281 this.fireEvent('show', this, this.date);
22286 this.picker().hide();
22289 this.fireEvent('hide', this, this.date);
22292 setTime : function()
22295 this.setValue(this.time.format(this.format));
22297 this.fireEvent('select', this, this.date);
22302 onMousedown: function(e){
22303 e.stopPropagation();
22304 e.preventDefault();
22307 onIncrementHours: function()
22309 Roo.log('onIncrementHours');
22310 this.time = this.time.add(Date.HOUR, 1);
22315 onDecrementHours: function()
22317 Roo.log('onDecrementHours');
22318 this.time = this.time.add(Date.HOUR, -1);
22322 onIncrementMinutes: function()
22324 Roo.log('onIncrementMinutes');
22325 this.time = this.time.add(Date.MINUTE, 1);
22329 onDecrementMinutes: function()
22331 Roo.log('onDecrementMinutes');
22332 this.time = this.time.add(Date.MINUTE, -1);
22336 onTogglePeriod: function()
22338 Roo.log('onTogglePeriod');
22339 this.time = this.time.add(Date.HOUR, 12);
22346 Roo.apply(Roo.bootstrap.TimeField, {
22376 cls: 'btn btn-info ok',
22388 Roo.apply(Roo.bootstrap.TimeField, {
22392 cls: 'datepicker dropdown-menu',
22396 cls: 'datepicker-time',
22400 cls: 'table-condensed',
22402 Roo.bootstrap.TimeField.content,
22403 Roo.bootstrap.TimeField.footer
22422 * @class Roo.bootstrap.MonthField
22423 * @extends Roo.bootstrap.Input
22424 * Bootstrap MonthField class
22426 * @cfg {String} language default en
22429 * Create a new MonthField
22430 * @param {Object} config The config object
22433 Roo.bootstrap.MonthField = function(config){
22434 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22439 * Fires when this field show.
22440 * @param {Roo.bootstrap.MonthField} this
22441 * @param {Mixed} date The date value
22446 * Fires when this field hide.
22447 * @param {Roo.bootstrap.MonthField} this
22448 * @param {Mixed} date The date value
22453 * Fires when select a date.
22454 * @param {Roo.bootstrap.MonthField} this
22455 * @param {String} oldvalue The old value
22456 * @param {String} newvalue The new value
22462 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22464 onRender: function(ct, position)
22467 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22469 this.language = this.language || 'en';
22470 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22471 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22473 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22474 this.isInline = false;
22475 this.isInput = true;
22476 this.component = this.el.select('.add-on', true).first() || false;
22477 this.component = (this.component && this.component.length === 0) ? false : this.component;
22478 this.hasInput = this.component && this.inputEL().length;
22480 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22482 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22484 this.picker().on('mousedown', this.onMousedown, this);
22485 this.picker().on('click', this.onClick, this);
22487 this.picker().addClass('datepicker-dropdown');
22489 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22490 v.setStyle('width', '189px');
22497 if(this.isInline) {
22503 setValue: function(v, suppressEvent)
22505 var o = this.getValue();
22507 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22511 if(suppressEvent !== true){
22512 this.fireEvent('select', this, o, v);
22517 getValue: function()
22522 onClick: function(e)
22524 e.stopPropagation();
22525 e.preventDefault();
22527 var target = e.getTarget();
22529 if(target.nodeName.toLowerCase() === 'i'){
22530 target = Roo.get(target).dom.parentNode;
22533 var nodeName = target.nodeName;
22534 var className = target.className;
22535 var html = target.innerHTML;
22537 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22541 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22543 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22549 picker : function()
22551 return this.pickerEl;
22554 fillMonths: function()
22557 var months = this.picker().select('>.datepicker-months td', true).first();
22559 months.dom.innerHTML = '';
22565 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22568 months.createChild(month);
22577 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22578 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22581 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22582 e.removeClass('active');
22584 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22585 e.addClass('active');
22592 if(this.isInline) {
22596 this.picker().removeClass(['bottom', 'top']);
22598 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22600 * place to the top of element!
22604 this.picker().addClass('top');
22605 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22610 this.picker().addClass('bottom');
22612 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22615 onFocus : function()
22617 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22621 onBlur : function()
22623 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22625 var d = this.inputEl().getValue();
22634 this.picker().show();
22635 this.picker().select('>.datepicker-months', true).first().show();
22639 this.fireEvent('show', this, this.date);
22644 if(this.isInline) {
22647 this.picker().hide();
22648 this.fireEvent('hide', this, this.date);
22652 onMousedown: function(e)
22654 e.stopPropagation();
22655 e.preventDefault();
22660 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22664 fireKey: function(e)
22666 if (!this.picker().isVisible()){
22667 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22678 e.preventDefault();
22682 dir = e.keyCode == 37 ? -1 : 1;
22684 this.vIndex = this.vIndex + dir;
22686 if(this.vIndex < 0){
22690 if(this.vIndex > 11){
22694 if(isNaN(this.vIndex)){
22698 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22704 dir = e.keyCode == 38 ? -1 : 1;
22706 this.vIndex = this.vIndex + dir * 4;
22708 if(this.vIndex < 0){
22712 if(this.vIndex > 11){
22716 if(isNaN(this.vIndex)){
22720 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22725 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22726 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22730 e.preventDefault();
22733 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22734 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22750 this.picker().remove();
22755 Roo.apply(Roo.bootstrap.MonthField, {
22774 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22775 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22780 Roo.apply(Roo.bootstrap.MonthField, {
22784 cls: 'datepicker dropdown-menu roo-dynamic',
22788 cls: 'datepicker-months',
22792 cls: 'table-condensed',
22794 Roo.bootstrap.DateField.content
22814 * @class Roo.bootstrap.CheckBox
22815 * @extends Roo.bootstrap.Input
22816 * Bootstrap CheckBox class
22818 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22819 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22820 * @cfg {String} boxLabel The text that appears beside the checkbox
22821 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22822 * @cfg {Boolean} checked initnal the element
22823 * @cfg {Boolean} inline inline the element (default false)
22824 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22825 * @cfg {String} tooltip label tooltip
22828 * Create a new CheckBox
22829 * @param {Object} config The config object
22832 Roo.bootstrap.CheckBox = function(config){
22833 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22838 * Fires when the element is checked or unchecked.
22839 * @param {Roo.bootstrap.CheckBox} this This input
22840 * @param {Boolean} checked The new checked value
22845 * Fires when the element is click.
22846 * @param {Roo.bootstrap.CheckBox} this This input
22853 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22855 inputType: 'checkbox',
22864 // checkbox success does not make any sense really..
22869 getAutoCreate : function()
22871 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22877 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22880 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22886 type : this.inputType,
22887 value : this.inputValue,
22888 cls : 'roo-' + this.inputType, //'form-box',
22889 placeholder : this.placeholder || ''
22893 if(this.inputType != 'radio'){
22897 cls : 'roo-hidden-value',
22898 value : this.checked ? this.inputValue : this.valueOff
22903 if (this.weight) { // Validity check?
22904 cfg.cls += " " + this.inputType + "-" + this.weight;
22907 if (this.disabled) {
22908 input.disabled=true;
22912 input.checked = this.checked;
22917 input.name = this.name;
22919 if(this.inputType != 'radio'){
22920 hidden.name = this.name;
22921 input.name = '_hidden_' + this.name;
22926 input.cls += ' input-' + this.size;
22931 ['xs','sm','md','lg'].map(function(size){
22932 if (settings[size]) {
22933 cfg.cls += ' col-' + size + '-' + settings[size];
22937 var inputblock = input;
22939 if (this.before || this.after) {
22942 cls : 'input-group',
22947 inputblock.cn.push({
22949 cls : 'input-group-addon',
22954 inputblock.cn.push(input);
22956 if(this.inputType != 'radio'){
22957 inputblock.cn.push(hidden);
22961 inputblock.cn.push({
22963 cls : 'input-group-addon',
22969 var boxLabelCfg = false;
22975 //'for': id, // box label is handled by onclick - so no for...
22977 html: this.boxLabel
22980 boxLabelCfg.tooltip = this.tooltip;
22986 if (align ==='left' && this.fieldLabel.length) {
22987 // Roo.log("left and has label");
22992 cls : 'control-label',
22993 html : this.fieldLabel
23004 cfg.cn[1].cn.push(boxLabelCfg);
23007 if(this.labelWidth > 12){
23008 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23011 if(this.labelWidth < 13 && this.labelmd == 0){
23012 this.labelmd = this.labelWidth;
23015 if(this.labellg > 0){
23016 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23017 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23020 if(this.labelmd > 0){
23021 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23022 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23025 if(this.labelsm > 0){
23026 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23027 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23030 if(this.labelxs > 0){
23031 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23032 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23035 } else if ( this.fieldLabel.length) {
23036 // Roo.log(" label");
23040 tag: this.boxLabel ? 'span' : 'label',
23042 cls: 'control-label box-input-label',
23043 //cls : 'input-group-addon',
23044 html : this.fieldLabel
23051 cfg.cn.push(boxLabelCfg);
23056 // Roo.log(" no label && no align");
23057 cfg.cn = [ inputblock ] ;
23059 cfg.cn.push(boxLabelCfg);
23067 if(this.inputType != 'radio'){
23068 cfg.cn.push(hidden);
23076 * return the real input element.
23078 inputEl: function ()
23080 return this.el.select('input.roo-' + this.inputType,true).first();
23082 hiddenEl: function ()
23084 return this.el.select('input.roo-hidden-value',true).first();
23087 labelEl: function()
23089 return this.el.select('label.control-label',true).first();
23091 /* depricated... */
23095 return this.labelEl();
23098 boxLabelEl: function()
23100 return this.el.select('label.box-label',true).first();
23103 initEvents : function()
23105 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23107 this.inputEl().on('click', this.onClick, this);
23109 if (this.boxLabel) {
23110 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23113 this.startValue = this.getValue();
23116 Roo.bootstrap.CheckBox.register(this);
23120 onClick : function(e)
23122 if(this.fireEvent('click', this, e) !== false){
23123 this.setChecked(!this.checked);
23128 setChecked : function(state,suppressEvent)
23130 this.startValue = this.getValue();
23132 if(this.inputType == 'radio'){
23134 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23135 e.dom.checked = false;
23138 this.inputEl().dom.checked = true;
23140 this.inputEl().dom.value = this.inputValue;
23142 if(suppressEvent !== true){
23143 this.fireEvent('check', this, true);
23151 this.checked = state;
23153 this.inputEl().dom.checked = state;
23156 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23158 if(suppressEvent !== true){
23159 this.fireEvent('check', this, state);
23165 getValue : function()
23167 if(this.inputType == 'radio'){
23168 return this.getGroupValue();
23171 return this.hiddenEl().dom.value;
23175 getGroupValue : function()
23177 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23181 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23184 setValue : function(v,suppressEvent)
23186 if(this.inputType == 'radio'){
23187 this.setGroupValue(v, suppressEvent);
23191 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23196 setGroupValue : function(v, suppressEvent)
23198 this.startValue = this.getValue();
23200 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23201 e.dom.checked = false;
23203 if(e.dom.value == v){
23204 e.dom.checked = true;
23208 if(suppressEvent !== true){
23209 this.fireEvent('check', this, true);
23217 validate : function()
23219 if(this.getVisibilityEl().hasClass('hidden')){
23225 (this.inputType == 'radio' && this.validateRadio()) ||
23226 (this.inputType == 'checkbox' && this.validateCheckbox())
23232 this.markInvalid();
23236 validateRadio : function()
23238 if(this.getVisibilityEl().hasClass('hidden')){
23242 if(this.allowBlank){
23248 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23249 if(!e.dom.checked){
23261 validateCheckbox : function()
23264 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23265 //return (this.getValue() == this.inputValue) ? true : false;
23268 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23276 for(var i in group){
23277 if(group[i].el.isVisible(true)){
23285 for(var i in group){
23290 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23297 * Mark this field as valid
23299 markValid : function()
23303 this.fireEvent('valid', this);
23305 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23308 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23315 if(this.inputType == 'radio'){
23316 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23317 var fg = e.findParent('.form-group', false, true);
23318 if (Roo.bootstrap.version == 3) {
23319 fg.removeClass([_this.invalidClass, _this.validClass]);
23320 fg.addClass(_this.validClass);
23322 fg.removeClass(['is-valid', 'is-invalid']);
23323 fg.addClass('is-valid');
23331 var fg = this.el.findParent('.form-group', false, true);
23332 if (Roo.bootstrap.version == 3) {
23333 fg.removeClass([this.invalidClass, this.validClass]);
23334 fg.addClass(this.validClass);
23336 fg.removeClass(['is-valid', 'is-invalid']);
23337 fg.addClass('is-valid');
23342 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23348 for(var i in group){
23349 var fg = group[i].el.findParent('.form-group', false, true);
23350 if (Roo.bootstrap.version == 3) {
23351 fg.removeClass([this.invalidClass, this.validClass]);
23352 fg.addClass(this.validClass);
23354 fg.removeClass(['is-valid', 'is-invalid']);
23355 fg.addClass('is-valid');
23361 * Mark this field as invalid
23362 * @param {String} msg The validation message
23364 markInvalid : function(msg)
23366 if(this.allowBlank){
23372 this.fireEvent('invalid', this, msg);
23374 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23377 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23381 label.markInvalid();
23384 if(this.inputType == 'radio'){
23386 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23387 var fg = e.findParent('.form-group', false, true);
23388 if (Roo.bootstrap.version == 3) {
23389 fg.removeClass([_this.invalidClass, _this.validClass]);
23390 fg.addClass(_this.invalidClass);
23392 fg.removeClass(['is-invalid', 'is-valid']);
23393 fg.addClass('is-invalid');
23401 var fg = this.el.findParent('.form-group', false, true);
23402 if (Roo.bootstrap.version == 3) {
23403 fg.removeClass([_this.invalidClass, _this.validClass]);
23404 fg.addClass(_this.invalidClass);
23406 fg.removeClass(['is-invalid', 'is-valid']);
23407 fg.addClass('is-invalid');
23412 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23418 for(var i in group){
23419 var fg = group[i].el.findParent('.form-group', false, true);
23420 if (Roo.bootstrap.version == 3) {
23421 fg.removeClass([_this.invalidClass, _this.validClass]);
23422 fg.addClass(_this.invalidClass);
23424 fg.removeClass(['is-invalid', 'is-valid']);
23425 fg.addClass('is-invalid');
23431 clearInvalid : function()
23433 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23435 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23437 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23439 if (label && label.iconEl) {
23440 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23441 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23445 disable : function()
23447 if(this.inputType != 'radio'){
23448 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23455 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23456 _this.getActionEl().addClass(this.disabledClass);
23457 e.dom.disabled = true;
23461 this.disabled = true;
23462 this.fireEvent("disable", this);
23466 enable : function()
23468 if(this.inputType != 'radio'){
23469 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23476 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23477 _this.getActionEl().removeClass(this.disabledClass);
23478 e.dom.disabled = false;
23482 this.disabled = false;
23483 this.fireEvent("enable", this);
23487 setBoxLabel : function(v)
23492 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23498 Roo.apply(Roo.bootstrap.CheckBox, {
23503 * register a CheckBox Group
23504 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23506 register : function(checkbox)
23508 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23509 this.groups[checkbox.groupId] = {};
23512 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23516 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23520 * fetch a CheckBox Group based on the group ID
23521 * @param {string} the group ID
23522 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23524 get: function(groupId) {
23525 if (typeof(this.groups[groupId]) == 'undefined') {
23529 return this.groups[groupId] ;
23542 * @class Roo.bootstrap.Radio
23543 * @extends Roo.bootstrap.Component
23544 * Bootstrap Radio class
23545 * @cfg {String} boxLabel - the label associated
23546 * @cfg {String} value - the value of radio
23549 * Create a new Radio
23550 * @param {Object} config The config object
23552 Roo.bootstrap.Radio = function(config){
23553 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23557 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23563 getAutoCreate : function()
23567 cls : 'form-group radio',
23572 html : this.boxLabel
23580 initEvents : function()
23582 this.parent().register(this);
23584 this.el.on('click', this.onClick, this);
23588 onClick : function(e)
23590 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23591 this.setChecked(true);
23595 setChecked : function(state, suppressEvent)
23597 this.parent().setValue(this.value, suppressEvent);
23601 setBoxLabel : function(v)
23606 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23621 * @class Roo.bootstrap.SecurePass
23622 * @extends Roo.bootstrap.Input
23623 * Bootstrap SecurePass class
23627 * Create a new SecurePass
23628 * @param {Object} config The config object
23631 Roo.bootstrap.SecurePass = function (config) {
23632 // these go here, so the translation tool can replace them..
23634 PwdEmpty: "Please type a password, and then retype it to confirm.",
23635 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23636 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23637 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23638 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23639 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23640 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23641 TooWeak: "Your password is Too Weak."
23643 this.meterLabel = "Password strength:";
23644 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23645 this.meterClass = [
23646 "roo-password-meter-tooweak",
23647 "roo-password-meter-weak",
23648 "roo-password-meter-medium",
23649 "roo-password-meter-strong",
23650 "roo-password-meter-grey"
23655 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23658 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23660 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23662 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23663 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23664 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23665 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23666 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23667 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23668 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23678 * @cfg {String/Object} Label for the strength meter (defaults to
23679 * 'Password strength:')
23684 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23685 * ['Weak', 'Medium', 'Strong'])
23688 pwdStrengths: false,
23701 initEvents: function ()
23703 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23705 if (this.el.is('input[type=password]') && Roo.isSafari) {
23706 this.el.on('keydown', this.SafariOnKeyDown, this);
23709 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23712 onRender: function (ct, position)
23714 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23715 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23716 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23718 this.trigger.createChild({
23723 cls: 'roo-password-meter-grey col-xs-12',
23726 //width: this.meterWidth + 'px'
23730 cls: 'roo-password-meter-text'
23736 if (this.hideTrigger) {
23737 this.trigger.setDisplayed(false);
23739 this.setSize(this.width || '', this.height || '');
23742 onDestroy: function ()
23744 if (this.trigger) {
23745 this.trigger.removeAllListeners();
23746 this.trigger.remove();
23749 this.wrap.remove();
23751 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23754 checkStrength: function ()
23756 var pwd = this.inputEl().getValue();
23757 if (pwd == this._lastPwd) {
23762 if (this.ClientSideStrongPassword(pwd)) {
23764 } else if (this.ClientSideMediumPassword(pwd)) {
23766 } else if (this.ClientSideWeakPassword(pwd)) {
23772 Roo.log('strength1: ' + strength);
23774 //var pm = this.trigger.child('div/div/div').dom;
23775 var pm = this.trigger.child('div/div');
23776 pm.removeClass(this.meterClass);
23777 pm.addClass(this.meterClass[strength]);
23780 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23782 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23784 this._lastPwd = pwd;
23788 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23790 this._lastPwd = '';
23792 var pm = this.trigger.child('div/div');
23793 pm.removeClass(this.meterClass);
23794 pm.addClass('roo-password-meter-grey');
23797 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23800 this.inputEl().dom.type='password';
23803 validateValue: function (value)
23805 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23808 if (value.length == 0) {
23809 if (this.allowBlank) {
23810 this.clearInvalid();
23814 this.markInvalid(this.errors.PwdEmpty);
23815 this.errorMsg = this.errors.PwdEmpty;
23823 if (!value.match(/[\x21-\x7e]+/)) {
23824 this.markInvalid(this.errors.PwdBadChar);
23825 this.errorMsg = this.errors.PwdBadChar;
23828 if (value.length < 6) {
23829 this.markInvalid(this.errors.PwdShort);
23830 this.errorMsg = this.errors.PwdShort;
23833 if (value.length > 16) {
23834 this.markInvalid(this.errors.PwdLong);
23835 this.errorMsg = this.errors.PwdLong;
23839 if (this.ClientSideStrongPassword(value)) {
23841 } else if (this.ClientSideMediumPassword(value)) {
23843 } else if (this.ClientSideWeakPassword(value)) {
23850 if (strength < 2) {
23851 //this.markInvalid(this.errors.TooWeak);
23852 this.errorMsg = this.errors.TooWeak;
23857 console.log('strength2: ' + strength);
23859 //var pm = this.trigger.child('div/div/div').dom;
23861 var pm = this.trigger.child('div/div');
23862 pm.removeClass(this.meterClass);
23863 pm.addClass(this.meterClass[strength]);
23865 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23867 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23869 this.errorMsg = '';
23873 CharacterSetChecks: function (type)
23876 this.fResult = false;
23879 isctype: function (character, type)
23882 case this.kCapitalLetter:
23883 if (character >= 'A' && character <= 'Z') {
23888 case this.kSmallLetter:
23889 if (character >= 'a' && character <= 'z') {
23895 if (character >= '0' && character <= '9') {
23900 case this.kPunctuation:
23901 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23912 IsLongEnough: function (pwd, size)
23914 return !(pwd == null || isNaN(size) || pwd.length < size);
23917 SpansEnoughCharacterSets: function (word, nb)
23919 if (!this.IsLongEnough(word, nb))
23924 var characterSetChecks = new Array(
23925 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23926 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23929 for (var index = 0; index < word.length; ++index) {
23930 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23931 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23932 characterSetChecks[nCharSet].fResult = true;
23939 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23940 if (characterSetChecks[nCharSet].fResult) {
23945 if (nCharSets < nb) {
23951 ClientSideStrongPassword: function (pwd)
23953 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23956 ClientSideMediumPassword: function (pwd)
23958 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23961 ClientSideWeakPassword: function (pwd)
23963 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23966 })//<script type="text/javascript">
23969 * Based Ext JS Library 1.1.1
23970 * Copyright(c) 2006-2007, Ext JS, LLC.
23976 * @class Roo.HtmlEditorCore
23977 * @extends Roo.Component
23978 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23980 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23983 Roo.HtmlEditorCore = function(config){
23986 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23991 * @event initialize
23992 * Fires when the editor is fully initialized (including the iframe)
23993 * @param {Roo.HtmlEditorCore} this
23998 * Fires when the editor is first receives the focus. Any insertion must wait
23999 * until after this event.
24000 * @param {Roo.HtmlEditorCore} this
24004 * @event beforesync
24005 * Fires before the textarea is updated with content from the editor iframe. Return false
24006 * to cancel the sync.
24007 * @param {Roo.HtmlEditorCore} this
24008 * @param {String} html
24012 * @event beforepush
24013 * Fires before the iframe editor is updated with content from the textarea. Return false
24014 * to cancel the push.
24015 * @param {Roo.HtmlEditorCore} this
24016 * @param {String} html
24021 * Fires when the textarea is updated with content from the editor iframe.
24022 * @param {Roo.HtmlEditorCore} this
24023 * @param {String} html
24028 * Fires when the iframe editor is updated with content from the textarea.
24029 * @param {Roo.HtmlEditorCore} this
24030 * @param {String} html
24035 * @event editorevent
24036 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24037 * @param {Roo.HtmlEditorCore} this
24043 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24045 // defaults : white / black...
24046 this.applyBlacklists();
24053 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24057 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24063 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24068 * @cfg {Number} height (in pixels)
24072 * @cfg {Number} width (in pixels)
24077 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24080 stylesheets: false,
24085 // private properties
24086 validationEvent : false,
24088 initialized : false,
24090 sourceEditMode : false,
24091 onFocus : Roo.emptyFn,
24093 hideMode:'offsets',
24097 // blacklist + whitelisted elements..
24104 * Protected method that will not generally be called directly. It
24105 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24106 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24108 getDocMarkup : function(){
24112 // inherit styels from page...??
24113 if (this.stylesheets === false) {
24115 Roo.get(document.head).select('style').each(function(node) {
24116 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24119 Roo.get(document.head).select('link').each(function(node) {
24120 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24123 } else if (!this.stylesheets.length) {
24125 st = '<style type="text/css">' +
24126 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24129 for (var i in this.stylesheets) {
24130 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24135 st += '<style type="text/css">' +
24136 'IMG { cursor: pointer } ' +
24139 var cls = 'roo-htmleditor-body';
24141 if(this.bodyCls.length){
24142 cls += ' ' + this.bodyCls;
24145 return '<html><head>' + st +
24146 //<style type="text/css">' +
24147 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24149 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24153 onRender : function(ct, position)
24156 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24157 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24160 this.el.dom.style.border = '0 none';
24161 this.el.dom.setAttribute('tabIndex', -1);
24162 this.el.addClass('x-hidden hide');
24166 if(Roo.isIE){ // fix IE 1px bogus margin
24167 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24171 this.frameId = Roo.id();
24175 var iframe = this.owner.wrap.createChild({
24177 cls: 'form-control', // bootstrap..
24179 name: this.frameId,
24180 frameBorder : 'no',
24181 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24186 this.iframe = iframe.dom;
24188 this.assignDocWin();
24190 this.doc.designMode = 'on';
24193 this.doc.write(this.getDocMarkup());
24197 var task = { // must defer to wait for browser to be ready
24199 //console.log("run task?" + this.doc.readyState);
24200 this.assignDocWin();
24201 if(this.doc.body || this.doc.readyState == 'complete'){
24203 this.doc.designMode="on";
24207 Roo.TaskMgr.stop(task);
24208 this.initEditor.defer(10, this);
24215 Roo.TaskMgr.start(task);
24220 onResize : function(w, h)
24222 Roo.log('resize: ' +w + ',' + h );
24223 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24227 if(typeof w == 'number'){
24229 this.iframe.style.width = w + 'px';
24231 if(typeof h == 'number'){
24233 this.iframe.style.height = h + 'px';
24235 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24242 * Toggles the editor between standard and source edit mode.
24243 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24245 toggleSourceEdit : function(sourceEditMode){
24247 this.sourceEditMode = sourceEditMode === true;
24249 if(this.sourceEditMode){
24251 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24254 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24255 //this.iframe.className = '';
24258 //this.setSize(this.owner.wrap.getSize());
24259 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24266 * Protected method that will not generally be called directly. If you need/want
24267 * custom HTML cleanup, this is the method you should override.
24268 * @param {String} html The HTML to be cleaned
24269 * return {String} The cleaned HTML
24271 cleanHtml : function(html){
24272 html = String(html);
24273 if(html.length > 5){
24274 if(Roo.isSafari){ // strip safari nonsense
24275 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24278 if(html == ' '){
24285 * HTML Editor -> Textarea
24286 * Protected method that will not generally be called directly. Syncs the contents
24287 * of the editor iframe with the textarea.
24289 syncValue : function(){
24290 if(this.initialized){
24291 var bd = (this.doc.body || this.doc.documentElement);
24292 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24293 var html = bd.innerHTML;
24295 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24296 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24298 html = '<div style="'+m[0]+'">' + html + '</div>';
24301 html = this.cleanHtml(html);
24302 // fix up the special chars.. normaly like back quotes in word...
24303 // however we do not want to do this with chinese..
24304 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24306 var cc = match.charCodeAt();
24308 // Get the character value, handling surrogate pairs
24309 if (match.length == 2) {
24310 // It's a surrogate pair, calculate the Unicode code point
24311 var high = match.charCodeAt(0) - 0xD800;
24312 var low = match.charCodeAt(1) - 0xDC00;
24313 cc = (high * 0x400) + low + 0x10000;
24315 (cc >= 0x4E00 && cc < 0xA000 ) ||
24316 (cc >= 0x3400 && cc < 0x4E00 ) ||
24317 (cc >= 0xf900 && cc < 0xfb00 )
24322 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24323 return "&#" + cc + ";";
24330 if(this.owner.fireEvent('beforesync', this, html) !== false){
24331 this.el.dom.value = html;
24332 this.owner.fireEvent('sync', this, html);
24338 * Protected method that will not generally be called directly. Pushes the value of the textarea
24339 * into the iframe editor.
24341 pushValue : function(){
24342 if(this.initialized){
24343 var v = this.el.dom.value.trim();
24345 // if(v.length < 1){
24349 if(this.owner.fireEvent('beforepush', this, v) !== false){
24350 var d = (this.doc.body || this.doc.documentElement);
24352 this.cleanUpPaste();
24353 this.el.dom.value = d.innerHTML;
24354 this.owner.fireEvent('push', this, v);
24360 deferFocus : function(){
24361 this.focus.defer(10, this);
24365 focus : function(){
24366 if(this.win && !this.sourceEditMode){
24373 assignDocWin: function()
24375 var iframe = this.iframe;
24378 this.doc = iframe.contentWindow.document;
24379 this.win = iframe.contentWindow;
24381 // if (!Roo.get(this.frameId)) {
24384 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24385 // this.win = Roo.get(this.frameId).dom.contentWindow;
24387 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24391 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24392 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24397 initEditor : function(){
24398 //console.log("INIT EDITOR");
24399 this.assignDocWin();
24403 this.doc.designMode="on";
24405 this.doc.write(this.getDocMarkup());
24408 var dbody = (this.doc.body || this.doc.documentElement);
24409 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24410 // this copies styles from the containing element into thsi one..
24411 // not sure why we need all of this..
24412 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24414 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24415 //ss['background-attachment'] = 'fixed'; // w3c
24416 dbody.bgProperties = 'fixed'; // ie
24417 //Roo.DomHelper.applyStyles(dbody, ss);
24418 Roo.EventManager.on(this.doc, {
24419 //'mousedown': this.onEditorEvent,
24420 'mouseup': this.onEditorEvent,
24421 'dblclick': this.onEditorEvent,
24422 'click': this.onEditorEvent,
24423 'keyup': this.onEditorEvent,
24428 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24430 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24431 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24433 this.initialized = true;
24435 this.owner.fireEvent('initialize', this);
24440 onDestroy : function(){
24446 //for (var i =0; i < this.toolbars.length;i++) {
24447 // // fixme - ask toolbars for heights?
24448 // this.toolbars[i].onDestroy();
24451 //this.wrap.dom.innerHTML = '';
24452 //this.wrap.remove();
24457 onFirstFocus : function(){
24459 this.assignDocWin();
24462 this.activated = true;
24465 if(Roo.isGecko){ // prevent silly gecko errors
24467 var s = this.win.getSelection();
24468 if(!s.focusNode || s.focusNode.nodeType != 3){
24469 var r = s.getRangeAt(0);
24470 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24475 this.execCmd('useCSS', true);
24476 this.execCmd('styleWithCSS', false);
24479 this.owner.fireEvent('activate', this);
24483 adjustFont: function(btn){
24484 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24485 //if(Roo.isSafari){ // safari
24488 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24489 if(Roo.isSafari){ // safari
24490 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24491 v = (v < 10) ? 10 : v;
24492 v = (v > 48) ? 48 : v;
24493 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24498 v = Math.max(1, v+adjust);
24500 this.execCmd('FontSize', v );
24503 onEditorEvent : function(e)
24505 this.owner.fireEvent('editorevent', this, e);
24506 // this.updateToolbar();
24507 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24510 insertTag : function(tg)
24512 // could be a bit smarter... -> wrap the current selected tRoo..
24513 if (tg.toLowerCase() == 'span' ||
24514 tg.toLowerCase() == 'code' ||
24515 tg.toLowerCase() == 'sup' ||
24516 tg.toLowerCase() == 'sub'
24519 range = this.createRange(this.getSelection());
24520 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24521 wrappingNode.appendChild(range.extractContents());
24522 range.insertNode(wrappingNode);
24529 this.execCmd("formatblock", tg);
24533 insertText : function(txt)
24537 var range = this.createRange();
24538 range.deleteContents();
24539 //alert(Sender.getAttribute('label'));
24541 range.insertNode(this.doc.createTextNode(txt));
24547 * Executes a Midas editor command on the editor document and performs necessary focus and
24548 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24549 * @param {String} cmd The Midas command
24550 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24552 relayCmd : function(cmd, value){
24554 this.execCmd(cmd, value);
24555 this.owner.fireEvent('editorevent', this);
24556 //this.updateToolbar();
24557 this.owner.deferFocus();
24561 * Executes a Midas editor command directly on the editor document.
24562 * For visual commands, you should use {@link #relayCmd} instead.
24563 * <b>This should only be called after the editor is initialized.</b>
24564 * @param {String} cmd The Midas command
24565 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24567 execCmd : function(cmd, value){
24568 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24575 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24577 * @param {String} text | dom node..
24579 insertAtCursor : function(text)
24582 if(!this.activated){
24588 var r = this.doc.selection.createRange();
24599 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24603 // from jquery ui (MIT licenced)
24605 var win = this.win;
24607 if (win.getSelection && win.getSelection().getRangeAt) {
24608 range = win.getSelection().getRangeAt(0);
24609 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24610 range.insertNode(node);
24611 } else if (win.document.selection && win.document.selection.createRange) {
24612 // no firefox support
24613 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24614 win.document.selection.createRange().pasteHTML(txt);
24616 // no firefox support
24617 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24618 this.execCmd('InsertHTML', txt);
24627 mozKeyPress : function(e){
24629 var c = e.getCharCode(), cmd;
24632 c = String.fromCharCode(c).toLowerCase();
24646 this.cleanUpPaste.defer(100, this);
24654 e.preventDefault();
24662 fixKeys : function(){ // load time branching for fastest keydown performance
24664 return function(e){
24665 var k = e.getKey(), r;
24668 r = this.doc.selection.createRange();
24671 r.pasteHTML('    ');
24678 r = this.doc.selection.createRange();
24680 var target = r.parentElement();
24681 if(!target || target.tagName.toLowerCase() != 'li'){
24683 r.pasteHTML('<br />');
24689 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24690 this.cleanUpPaste.defer(100, this);
24696 }else if(Roo.isOpera){
24697 return function(e){
24698 var k = e.getKey();
24702 this.execCmd('InsertHTML','    ');
24705 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24706 this.cleanUpPaste.defer(100, this);
24711 }else if(Roo.isSafari){
24712 return function(e){
24713 var k = e.getKey();
24717 this.execCmd('InsertText','\t');
24721 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24722 this.cleanUpPaste.defer(100, this);
24730 getAllAncestors: function()
24732 var p = this.getSelectedNode();
24735 a.push(p); // push blank onto stack..
24736 p = this.getParentElement();
24740 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24744 a.push(this.doc.body);
24748 lastSelNode : false,
24751 getSelection : function()
24753 this.assignDocWin();
24754 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24757 getSelectedNode: function()
24759 // this may only work on Gecko!!!
24761 // should we cache this!!!!
24766 var range = this.createRange(this.getSelection()).cloneRange();
24769 var parent = range.parentElement();
24771 var testRange = range.duplicate();
24772 testRange.moveToElementText(parent);
24773 if (testRange.inRange(range)) {
24776 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24779 parent = parent.parentElement;
24784 // is ancestor a text element.
24785 var ac = range.commonAncestorContainer;
24786 if (ac.nodeType == 3) {
24787 ac = ac.parentNode;
24790 var ar = ac.childNodes;
24793 var other_nodes = [];
24794 var has_other_nodes = false;
24795 for (var i=0;i<ar.length;i++) {
24796 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24799 // fullly contained node.
24801 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24806 // probably selected..
24807 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24808 other_nodes.push(ar[i]);
24812 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24817 has_other_nodes = true;
24819 if (!nodes.length && other_nodes.length) {
24820 nodes= other_nodes;
24822 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24828 createRange: function(sel)
24830 // this has strange effects when using with
24831 // top toolbar - not sure if it's a great idea.
24832 //this.editor.contentWindow.focus();
24833 if (typeof sel != "undefined") {
24835 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24837 return this.doc.createRange();
24840 return this.doc.createRange();
24843 getParentElement: function()
24846 this.assignDocWin();
24847 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24849 var range = this.createRange(sel);
24852 var p = range.commonAncestorContainer;
24853 while (p.nodeType == 3) { // text node
24864 * Range intersection.. the hard stuff...
24868 * [ -- selected range --- ]
24872 * if end is before start or hits it. fail.
24873 * if start is after end or hits it fail.
24875 * if either hits (but other is outside. - then it's not
24881 // @see http://www.thismuchiknow.co.uk/?p=64.
24882 rangeIntersectsNode : function(range, node)
24884 var nodeRange = node.ownerDocument.createRange();
24886 nodeRange.selectNode(node);
24888 nodeRange.selectNodeContents(node);
24891 var rangeStartRange = range.cloneRange();
24892 rangeStartRange.collapse(true);
24894 var rangeEndRange = range.cloneRange();
24895 rangeEndRange.collapse(false);
24897 var nodeStartRange = nodeRange.cloneRange();
24898 nodeStartRange.collapse(true);
24900 var nodeEndRange = nodeRange.cloneRange();
24901 nodeEndRange.collapse(false);
24903 return rangeStartRange.compareBoundaryPoints(
24904 Range.START_TO_START, nodeEndRange) == -1 &&
24905 rangeEndRange.compareBoundaryPoints(
24906 Range.START_TO_START, nodeStartRange) == 1;
24910 rangeCompareNode : function(range, node)
24912 var nodeRange = node.ownerDocument.createRange();
24914 nodeRange.selectNode(node);
24916 nodeRange.selectNodeContents(node);
24920 range.collapse(true);
24922 nodeRange.collapse(true);
24924 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24925 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24927 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24929 var nodeIsBefore = ss == 1;
24930 var nodeIsAfter = ee == -1;
24932 if (nodeIsBefore && nodeIsAfter) {
24935 if (!nodeIsBefore && nodeIsAfter) {
24936 return 1; //right trailed.
24939 if (nodeIsBefore && !nodeIsAfter) {
24940 return 2; // left trailed.
24946 // private? - in a new class?
24947 cleanUpPaste : function()
24949 // cleans up the whole document..
24950 Roo.log('cleanuppaste');
24952 this.cleanUpChildren(this.doc.body);
24953 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24954 if (clean != this.doc.body.innerHTML) {
24955 this.doc.body.innerHTML = clean;
24960 cleanWordChars : function(input) {// change the chars to hex code
24961 var he = Roo.HtmlEditorCore;
24963 var output = input;
24964 Roo.each(he.swapCodes, function(sw) {
24965 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24967 output = output.replace(swapper, sw[1]);
24974 cleanUpChildren : function (n)
24976 if (!n.childNodes.length) {
24979 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24980 this.cleanUpChild(n.childNodes[i]);
24987 cleanUpChild : function (node)
24990 //console.log(node);
24991 if (node.nodeName == "#text") {
24992 // clean up silly Windows -- stuff?
24995 if (node.nodeName == "#comment") {
24996 node.parentNode.removeChild(node);
24997 // clean up silly Windows -- stuff?
25000 var lcname = node.tagName.toLowerCase();
25001 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25002 // whitelist of tags..
25004 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25006 node.parentNode.removeChild(node);
25011 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25013 // spans with no attributes - just remove them..
25014 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25015 remove_keep_children = true;
25018 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25019 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25021 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25022 // remove_keep_children = true;
25025 if (remove_keep_children) {
25026 this.cleanUpChildren(node);
25027 // inserts everything just before this node...
25028 while (node.childNodes.length) {
25029 var cn = node.childNodes[0];
25030 node.removeChild(cn);
25031 node.parentNode.insertBefore(cn, node);
25033 node.parentNode.removeChild(node);
25037 if (!node.attributes || !node.attributes.length) {
25042 this.cleanUpChildren(node);
25046 function cleanAttr(n,v)
25049 if (v.match(/^\./) || v.match(/^\//)) {
25052 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25055 if (v.match(/^#/)) {
25058 if (v.match(/^\{/)) { // allow template editing.
25061 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25062 node.removeAttribute(n);
25066 var cwhite = this.cwhite;
25067 var cblack = this.cblack;
25069 function cleanStyle(n,v)
25071 if (v.match(/expression/)) { //XSS?? should we even bother..
25072 node.removeAttribute(n);
25076 var parts = v.split(/;/);
25079 Roo.each(parts, function(p) {
25080 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25084 var l = p.split(':').shift().replace(/\s+/g,'');
25085 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25087 if ( cwhite.length && cblack.indexOf(l) > -1) {
25088 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25089 //node.removeAttribute(n);
25093 // only allow 'c whitelisted system attributes'
25094 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25095 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25096 //node.removeAttribute(n);
25106 if (clean.length) {
25107 node.setAttribute(n, clean.join(';'));
25109 node.removeAttribute(n);
25115 for (var i = node.attributes.length-1; i > -1 ; i--) {
25116 var a = node.attributes[i];
25119 if (a.name.toLowerCase().substr(0,2)=='on') {
25120 node.removeAttribute(a.name);
25123 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25124 node.removeAttribute(a.name);
25127 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25128 cleanAttr(a.name,a.value); // fixme..
25131 if (a.name == 'style') {
25132 cleanStyle(a.name,a.value);
25135 /// clean up MS crap..
25136 // tecnically this should be a list of valid class'es..
25139 if (a.name == 'class') {
25140 if (a.value.match(/^Mso/)) {
25141 node.removeAttribute('class');
25144 if (a.value.match(/^body$/)) {
25145 node.removeAttribute('class');
25156 this.cleanUpChildren(node);
25162 * Clean up MS wordisms...
25164 cleanWord : function(node)
25167 this.cleanWord(this.doc.body);
25172 node.nodeName == 'SPAN' &&
25173 !node.hasAttributes() &&
25174 node.childNodes.length == 1 &&
25175 node.firstChild.nodeName == "#text"
25177 var textNode = node.firstChild;
25178 node.removeChild(textNode);
25179 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25180 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25182 node.parentNode.insertBefore(textNode, node);
25183 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25184 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25186 node.parentNode.removeChild(node);
25189 if (node.nodeName == "#text") {
25190 // clean up silly Windows -- stuff?
25193 if (node.nodeName == "#comment") {
25194 node.parentNode.removeChild(node);
25195 // clean up silly Windows -- stuff?
25199 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25200 node.parentNode.removeChild(node);
25203 //Roo.log(node.tagName);
25204 // remove - but keep children..
25205 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25206 //Roo.log('-- removed');
25207 while (node.childNodes.length) {
25208 var cn = node.childNodes[0];
25209 node.removeChild(cn);
25210 node.parentNode.insertBefore(cn, node);
25211 // move node to parent - and clean it..
25212 this.cleanWord(cn);
25214 node.parentNode.removeChild(node);
25215 /// no need to iterate chidlren = it's got none..
25216 //this.iterateChildren(node, this.cleanWord);
25220 if (node.className.length) {
25222 var cn = node.className.split(/\W+/);
25224 Roo.each(cn, function(cls) {
25225 if (cls.match(/Mso[a-zA-Z]+/)) {
25230 node.className = cna.length ? cna.join(' ') : '';
25232 node.removeAttribute("class");
25236 if (node.hasAttribute("lang")) {
25237 node.removeAttribute("lang");
25240 if (node.hasAttribute("style")) {
25242 var styles = node.getAttribute("style").split(";");
25244 Roo.each(styles, function(s) {
25245 if (!s.match(/:/)) {
25248 var kv = s.split(":");
25249 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25252 // what ever is left... we allow.
25255 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25256 if (!nstyle.length) {
25257 node.removeAttribute('style');
25260 this.iterateChildren(node, this.cleanWord);
25266 * iterateChildren of a Node, calling fn each time, using this as the scole..
25267 * @param {DomNode} node node to iterate children of.
25268 * @param {Function} fn method of this class to call on each item.
25270 iterateChildren : function(node, fn)
25272 if (!node.childNodes.length) {
25275 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25276 fn.call(this, node.childNodes[i])
25282 * cleanTableWidths.
25284 * Quite often pasting from word etc.. results in tables with column and widths.
25285 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25288 cleanTableWidths : function(node)
25293 this.cleanTableWidths(this.doc.body);
25298 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25301 Roo.log(node.tagName);
25302 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25303 this.iterateChildren(node, this.cleanTableWidths);
25306 if (node.hasAttribute('width')) {
25307 node.removeAttribute('width');
25311 if (node.hasAttribute("style")) {
25314 var styles = node.getAttribute("style").split(";");
25316 Roo.each(styles, function(s) {
25317 if (!s.match(/:/)) {
25320 var kv = s.split(":");
25321 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25324 // what ever is left... we allow.
25327 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25328 if (!nstyle.length) {
25329 node.removeAttribute('style');
25333 this.iterateChildren(node, this.cleanTableWidths);
25341 domToHTML : function(currentElement, depth, nopadtext) {
25343 depth = depth || 0;
25344 nopadtext = nopadtext || false;
25346 if (!currentElement) {
25347 return this.domToHTML(this.doc.body);
25350 //Roo.log(currentElement);
25352 var allText = false;
25353 var nodeName = currentElement.nodeName;
25354 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25356 if (nodeName == '#text') {
25358 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25363 if (nodeName != 'BODY') {
25366 // Prints the node tagName, such as <A>, <IMG>, etc
25369 for(i = 0; i < currentElement.attributes.length;i++) {
25371 var aname = currentElement.attributes.item(i).name;
25372 if (!currentElement.attributes.item(i).value.length) {
25375 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25378 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25387 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25390 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25395 // Traverse the tree
25397 var currentElementChild = currentElement.childNodes.item(i);
25398 var allText = true;
25399 var innerHTML = '';
25401 while (currentElementChild) {
25402 // Formatting code (indent the tree so it looks nice on the screen)
25403 var nopad = nopadtext;
25404 if (lastnode == 'SPAN') {
25408 if (currentElementChild.nodeName == '#text') {
25409 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25410 toadd = nopadtext ? toadd : toadd.trim();
25411 if (!nopad && toadd.length > 80) {
25412 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25414 innerHTML += toadd;
25417 currentElementChild = currentElement.childNodes.item(i);
25423 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25425 // Recursively traverse the tree structure of the child node
25426 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25427 lastnode = currentElementChild.nodeName;
25429 currentElementChild=currentElement.childNodes.item(i);
25435 // The remaining code is mostly for formatting the tree
25436 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25441 ret+= "</"+tagName+">";
25447 applyBlacklists : function()
25449 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25450 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25454 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25455 if (b.indexOf(tag) > -1) {
25458 this.white.push(tag);
25462 Roo.each(w, function(tag) {
25463 if (b.indexOf(tag) > -1) {
25466 if (this.white.indexOf(tag) > -1) {
25469 this.white.push(tag);
25474 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25475 if (w.indexOf(tag) > -1) {
25478 this.black.push(tag);
25482 Roo.each(b, function(tag) {
25483 if (w.indexOf(tag) > -1) {
25486 if (this.black.indexOf(tag) > -1) {
25489 this.black.push(tag);
25494 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25495 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25499 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25500 if (b.indexOf(tag) > -1) {
25503 this.cwhite.push(tag);
25507 Roo.each(w, function(tag) {
25508 if (b.indexOf(tag) > -1) {
25511 if (this.cwhite.indexOf(tag) > -1) {
25514 this.cwhite.push(tag);
25519 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25520 if (w.indexOf(tag) > -1) {
25523 this.cblack.push(tag);
25527 Roo.each(b, function(tag) {
25528 if (w.indexOf(tag) > -1) {
25531 if (this.cblack.indexOf(tag) > -1) {
25534 this.cblack.push(tag);
25539 setStylesheets : function(stylesheets)
25541 if(typeof(stylesheets) == 'string'){
25542 Roo.get(this.iframe.contentDocument.head).createChild({
25544 rel : 'stylesheet',
25553 Roo.each(stylesheets, function(s) {
25558 Roo.get(_this.iframe.contentDocument.head).createChild({
25560 rel : 'stylesheet',
25569 removeStylesheets : function()
25573 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25578 setStyle : function(style)
25580 Roo.get(this.iframe.contentDocument.head).createChild({
25589 // hide stuff that is not compatible
25603 * @event specialkey
25607 * @cfg {String} fieldClass @hide
25610 * @cfg {String} focusClass @hide
25613 * @cfg {String} autoCreate @hide
25616 * @cfg {String} inputType @hide
25619 * @cfg {String} invalidClass @hide
25622 * @cfg {String} invalidText @hide
25625 * @cfg {String} msgFx @hide
25628 * @cfg {String} validateOnBlur @hide
25632 Roo.HtmlEditorCore.white = [
25633 'area', 'br', 'img', 'input', 'hr', 'wbr',
25635 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25636 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25637 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25638 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25639 'table', 'ul', 'xmp',
25641 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25644 'dir', 'menu', 'ol', 'ul', 'dl',
25650 Roo.HtmlEditorCore.black = [
25651 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25653 'base', 'basefont', 'bgsound', 'blink', 'body',
25654 'frame', 'frameset', 'head', 'html', 'ilayer',
25655 'iframe', 'layer', 'link', 'meta', 'object',
25656 'script', 'style' ,'title', 'xml' // clean later..
25658 Roo.HtmlEditorCore.clean = [
25659 'script', 'style', 'title', 'xml'
25661 Roo.HtmlEditorCore.remove = [
25666 Roo.HtmlEditorCore.ablack = [
25670 Roo.HtmlEditorCore.aclean = [
25671 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25675 Roo.HtmlEditorCore.pwhite= [
25676 'http', 'https', 'mailto'
25679 // white listed style attributes.
25680 Roo.HtmlEditorCore.cwhite= [
25681 // 'text-align', /// default is to allow most things..
25687 // black listed style attributes.
25688 Roo.HtmlEditorCore.cblack= [
25689 // 'font-size' -- this can be set by the project
25693 Roo.HtmlEditorCore.swapCodes =[
25712 * @class Roo.bootstrap.HtmlEditor
25713 * @extends Roo.bootstrap.TextArea
25714 * Bootstrap HtmlEditor class
25717 * Create a new HtmlEditor
25718 * @param {Object} config The config object
25721 Roo.bootstrap.HtmlEditor = function(config){
25722 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25723 if (!this.toolbars) {
25724 this.toolbars = [];
25727 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25730 * @event initialize
25731 * Fires when the editor is fully initialized (including the iframe)
25732 * @param {HtmlEditor} this
25737 * Fires when the editor is first receives the focus. Any insertion must wait
25738 * until after this event.
25739 * @param {HtmlEditor} this
25743 * @event beforesync
25744 * Fires before the textarea is updated with content from the editor iframe. Return false
25745 * to cancel the sync.
25746 * @param {HtmlEditor} this
25747 * @param {String} html
25751 * @event beforepush
25752 * Fires before the iframe editor is updated with content from the textarea. Return false
25753 * to cancel the push.
25754 * @param {HtmlEditor} this
25755 * @param {String} html
25760 * Fires when the textarea is updated with content from the editor iframe.
25761 * @param {HtmlEditor} this
25762 * @param {String} html
25767 * Fires when the iframe editor is updated with content from the textarea.
25768 * @param {HtmlEditor} this
25769 * @param {String} html
25773 * @event editmodechange
25774 * Fires when the editor switches edit modes
25775 * @param {HtmlEditor} this
25776 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25778 editmodechange: true,
25780 * @event editorevent
25781 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25782 * @param {HtmlEditor} this
25786 * @event firstfocus
25787 * Fires when on first focus - needed by toolbars..
25788 * @param {HtmlEditor} this
25793 * Auto save the htmlEditor value as a file into Events
25794 * @param {HtmlEditor} this
25798 * @event savedpreview
25799 * preview the saved version of htmlEditor
25800 * @param {HtmlEditor} this
25807 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25811 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25816 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25821 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25826 * @cfg {Number} height (in pixels)
25830 * @cfg {Number} width (in pixels)
25835 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25838 stylesheets: false,
25843 // private properties
25844 validationEvent : false,
25846 initialized : false,
25849 onFocus : Roo.emptyFn,
25851 hideMode:'offsets',
25853 tbContainer : false,
25857 toolbarContainer :function() {
25858 return this.wrap.select('.x-html-editor-tb',true).first();
25862 * Protected method that will not generally be called directly. It
25863 * is called when the editor creates its toolbar. Override this method if you need to
25864 * add custom toolbar buttons.
25865 * @param {HtmlEditor} editor
25867 createToolbar : function(){
25868 Roo.log('renewing');
25869 Roo.log("create toolbars");
25871 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25872 this.toolbars[0].render(this.toolbarContainer());
25876 // if (!editor.toolbars || !editor.toolbars.length) {
25877 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25880 // for (var i =0 ; i < editor.toolbars.length;i++) {
25881 // editor.toolbars[i] = Roo.factory(
25882 // typeof(editor.toolbars[i]) == 'string' ?
25883 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25884 // Roo.bootstrap.HtmlEditor);
25885 // editor.toolbars[i].init(editor);
25891 onRender : function(ct, position)
25893 // Roo.log("Call onRender: " + this.xtype);
25895 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25897 this.wrap = this.inputEl().wrap({
25898 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25901 this.editorcore.onRender(ct, position);
25903 if (this.resizable) {
25904 this.resizeEl = new Roo.Resizable(this.wrap, {
25908 minHeight : this.height,
25909 height: this.height,
25910 handles : this.resizable,
25913 resize : function(r, w, h) {
25914 _t.onResize(w,h); // -something
25920 this.createToolbar(this);
25923 if(!this.width && this.resizable){
25924 this.setSize(this.wrap.getSize());
25926 if (this.resizeEl) {
25927 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25928 // should trigger onReize..
25934 onResize : function(w, h)
25936 Roo.log('resize: ' +w + ',' + h );
25937 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25941 if(this.inputEl() ){
25942 if(typeof w == 'number'){
25943 var aw = w - this.wrap.getFrameWidth('lr');
25944 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25947 if(typeof h == 'number'){
25948 var tbh = -11; // fixme it needs to tool bar size!
25949 for (var i =0; i < this.toolbars.length;i++) {
25950 // fixme - ask toolbars for heights?
25951 tbh += this.toolbars[i].el.getHeight();
25952 //if (this.toolbars[i].footer) {
25953 // tbh += this.toolbars[i].footer.el.getHeight();
25961 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25962 ah -= 5; // knock a few pixes off for look..
25963 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25967 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25968 this.editorcore.onResize(ew,eh);
25973 * Toggles the editor between standard and source edit mode.
25974 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25976 toggleSourceEdit : function(sourceEditMode)
25978 this.editorcore.toggleSourceEdit(sourceEditMode);
25980 if(this.editorcore.sourceEditMode){
25981 Roo.log('editor - showing textarea');
25984 // Roo.log(this.syncValue());
25986 this.inputEl().removeClass(['hide', 'x-hidden']);
25987 this.inputEl().dom.removeAttribute('tabIndex');
25988 this.inputEl().focus();
25990 Roo.log('editor - hiding textarea');
25992 // Roo.log(this.pushValue());
25995 this.inputEl().addClass(['hide', 'x-hidden']);
25996 this.inputEl().dom.setAttribute('tabIndex', -1);
25997 //this.deferFocus();
26000 if(this.resizable){
26001 this.setSize(this.wrap.getSize());
26004 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26007 // private (for BoxComponent)
26008 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26010 // private (for BoxComponent)
26011 getResizeEl : function(){
26015 // private (for BoxComponent)
26016 getPositionEl : function(){
26021 initEvents : function(){
26022 this.originalValue = this.getValue();
26026 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26029 // markInvalid : Roo.emptyFn,
26031 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26034 // clearInvalid : Roo.emptyFn,
26036 setValue : function(v){
26037 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26038 this.editorcore.pushValue();
26043 deferFocus : function(){
26044 this.focus.defer(10, this);
26048 focus : function(){
26049 this.editorcore.focus();
26055 onDestroy : function(){
26061 for (var i =0; i < this.toolbars.length;i++) {
26062 // fixme - ask toolbars for heights?
26063 this.toolbars[i].onDestroy();
26066 this.wrap.dom.innerHTML = '';
26067 this.wrap.remove();
26072 onFirstFocus : function(){
26073 //Roo.log("onFirstFocus");
26074 this.editorcore.onFirstFocus();
26075 for (var i =0; i < this.toolbars.length;i++) {
26076 this.toolbars[i].onFirstFocus();
26082 syncValue : function()
26084 this.editorcore.syncValue();
26087 pushValue : function()
26089 this.editorcore.pushValue();
26093 // hide stuff that is not compatible
26107 * @event specialkey
26111 * @cfg {String} fieldClass @hide
26114 * @cfg {String} focusClass @hide
26117 * @cfg {String} autoCreate @hide
26120 * @cfg {String} inputType @hide
26124 * @cfg {String} invalidText @hide
26127 * @cfg {String} msgFx @hide
26130 * @cfg {String} validateOnBlur @hide
26139 Roo.namespace('Roo.bootstrap.htmleditor');
26141 * @class Roo.bootstrap.HtmlEditorToolbar1
26147 new Roo.bootstrap.HtmlEditor({
26150 new Roo.bootstrap.HtmlEditorToolbar1({
26151 disable : { fonts: 1 , format: 1, ..., ... , ...],
26157 * @cfg {Object} disable List of elements to disable..
26158 * @cfg {Array} btns List of additional buttons.
26162 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26165 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26168 Roo.apply(this, config);
26170 // default disabled, based on 'good practice'..
26171 this.disable = this.disable || {};
26172 Roo.applyIf(this.disable, {
26175 specialElements : true
26177 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26179 this.editor = config.editor;
26180 this.editorcore = config.editor.editorcore;
26182 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26184 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26185 // dont call parent... till later.
26187 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26192 editorcore : false,
26197 "h1","h2","h3","h4","h5","h6",
26199 "abbr", "acronym", "address", "cite", "samp", "var",
26203 onRender : function(ct, position)
26205 // Roo.log("Call onRender: " + this.xtype);
26207 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26209 this.el.dom.style.marginBottom = '0';
26211 var editorcore = this.editorcore;
26212 var editor= this.editor;
26215 var btn = function(id,cmd , toggle, handler, html){
26217 var event = toggle ? 'toggle' : 'click';
26222 xns: Roo.bootstrap,
26226 enableToggle:toggle !== false,
26228 pressed : toggle ? false : null,
26231 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26232 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26238 // var cb_box = function...
26243 xns: Roo.bootstrap,
26248 xns: Roo.bootstrap,
26252 Roo.each(this.formats, function(f) {
26253 style.menu.items.push({
26255 xns: Roo.bootstrap,
26256 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26261 editorcore.insertTag(this.tagname);
26268 children.push(style);
26270 btn('bold',false,true);
26271 btn('italic',false,true);
26272 btn('align-left', 'justifyleft',true);
26273 btn('align-center', 'justifycenter',true);
26274 btn('align-right' , 'justifyright',true);
26275 btn('link', false, false, function(btn) {
26276 //Roo.log("create link?");
26277 var url = prompt(this.createLinkText, this.defaultLinkValue);
26278 if(url && url != 'http:/'+'/'){
26279 this.editorcore.relayCmd('createlink', url);
26282 btn('list','insertunorderedlist',true);
26283 btn('pencil', false,true, function(btn){
26285 this.toggleSourceEdit(btn.pressed);
26288 if (this.editor.btns.length > 0) {
26289 for (var i = 0; i<this.editor.btns.length; i++) {
26290 children.push(this.editor.btns[i]);
26298 xns: Roo.bootstrap,
26303 xns: Roo.bootstrap,
26308 cog.menu.items.push({
26310 xns: Roo.bootstrap,
26311 html : Clean styles,
26316 editorcore.insertTag(this.tagname);
26325 this.xtype = 'NavSimplebar';
26327 for(var i=0;i< children.length;i++) {
26329 this.buttons.add(this.addxtypeChild(children[i]));
26333 editor.on('editorevent', this.updateToolbar, this);
26335 onBtnClick : function(id)
26337 this.editorcore.relayCmd(id);
26338 this.editorcore.focus();
26342 * Protected method that will not generally be called directly. It triggers
26343 * a toolbar update by reading the markup state of the current selection in the editor.
26345 updateToolbar: function(){
26347 if(!this.editorcore.activated){
26348 this.editor.onFirstFocus(); // is this neeed?
26352 var btns = this.buttons;
26353 var doc = this.editorcore.doc;
26354 btns.get('bold').setActive(doc.queryCommandState('bold'));
26355 btns.get('italic').setActive(doc.queryCommandState('italic'));
26356 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26358 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26359 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26360 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26362 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26363 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26366 var ans = this.editorcore.getAllAncestors();
26367 if (this.formatCombo) {
26370 var store = this.formatCombo.store;
26371 this.formatCombo.setValue("");
26372 for (var i =0; i < ans.length;i++) {
26373 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26375 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26383 // hides menus... - so this cant be on a menu...
26384 Roo.bootstrap.MenuMgr.hideAll();
26386 Roo.bootstrap.MenuMgr.hideAll();
26387 //this.editorsyncValue();
26389 onFirstFocus: function() {
26390 this.buttons.each(function(item){
26394 toggleSourceEdit : function(sourceEditMode){
26397 if(sourceEditMode){
26398 Roo.log("disabling buttons");
26399 this.buttons.each( function(item){
26400 if(item.cmd != 'pencil'){
26406 Roo.log("enabling buttons");
26407 if(this.editorcore.initialized){
26408 this.buttons.each( function(item){
26414 Roo.log("calling toggole on editor");
26415 // tell the editor that it's been pressed..
26416 this.editor.toggleSourceEdit(sourceEditMode);
26430 * @class Roo.bootstrap.Markdown
26431 * @extends Roo.bootstrap.TextArea
26432 * Bootstrap Showdown editable area
26433 * @cfg {string} content
26436 * Create a new Showdown
26439 Roo.bootstrap.Markdown = function(config){
26440 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26444 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26448 initEvents : function()
26451 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26452 this.markdownEl = this.el.createChild({
26453 cls : 'roo-markdown-area'
26455 this.inputEl().addClass('d-none');
26456 if (this.getValue() == '') {
26457 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26460 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26462 this.markdownEl.on('click', this.toggleTextEdit, this);
26463 this.on('blur', this.toggleTextEdit, this);
26464 this.on('specialkey', this.resizeTextArea, this);
26467 toggleTextEdit : function()
26469 var sh = this.markdownEl.getHeight();
26470 this.inputEl().addClass('d-none');
26471 this.markdownEl.addClass('d-none');
26472 if (!this.editing) {
26474 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26475 this.inputEl().removeClass('d-none');
26476 this.inputEl().focus();
26477 this.editing = true;
26480 // show showdown...
26481 this.updateMarkdown();
26482 this.markdownEl.removeClass('d-none');
26483 this.editing = false;
26486 updateMarkdown : function()
26488 if (this.getValue() == '') {
26489 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26493 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26496 resizeTextArea: function () {
26499 Roo.log([sh, this.getValue().split("\n").length * 30]);
26500 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26502 setValue : function(val)
26504 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26505 if (!this.editing) {
26506 this.updateMarkdown();
26512 if (!this.editing) {
26513 this.toggleTextEdit();
26521 * @class Roo.bootstrap.Table.AbstractSelectionModel
26522 * @extends Roo.util.Observable
26523 * Abstract base class for grid SelectionModels. It provides the interface that should be
26524 * implemented by descendant classes. This class should not be directly instantiated.
26527 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26528 this.locked = false;
26529 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26533 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26534 /** @ignore Called by the grid automatically. Do not call directly. */
26535 init : function(grid){
26541 * Locks the selections.
26544 this.locked = true;
26548 * Unlocks the selections.
26550 unlock : function(){
26551 this.locked = false;
26555 * Returns true if the selections are locked.
26556 * @return {Boolean}
26558 isLocked : function(){
26559 return this.locked;
26563 initEvents : function ()
26569 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26570 * @class Roo.bootstrap.Table.RowSelectionModel
26571 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26572 * It supports multiple selections and keyboard selection/navigation.
26574 * @param {Object} config
26577 Roo.bootstrap.Table.RowSelectionModel = function(config){
26578 Roo.apply(this, config);
26579 this.selections = new Roo.util.MixedCollection(false, function(o){
26584 this.lastActive = false;
26588 * @event selectionchange
26589 * Fires when the selection changes
26590 * @param {SelectionModel} this
26592 "selectionchange" : true,
26594 * @event afterselectionchange
26595 * Fires after the selection changes (eg. by key press or clicking)
26596 * @param {SelectionModel} this
26598 "afterselectionchange" : true,
26600 * @event beforerowselect
26601 * Fires when a row is selected being selected, return false to cancel.
26602 * @param {SelectionModel} this
26603 * @param {Number} rowIndex The selected index
26604 * @param {Boolean} keepExisting False if other selections will be cleared
26606 "beforerowselect" : true,
26609 * Fires when a row is selected.
26610 * @param {SelectionModel} this
26611 * @param {Number} rowIndex The selected index
26612 * @param {Roo.data.Record} r The record
26614 "rowselect" : true,
26616 * @event rowdeselect
26617 * Fires when a row is deselected.
26618 * @param {SelectionModel} this
26619 * @param {Number} rowIndex The selected index
26621 "rowdeselect" : true
26623 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26624 this.locked = false;
26627 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26629 * @cfg {Boolean} singleSelect
26630 * True to allow selection of only one row at a time (defaults to false)
26632 singleSelect : false,
26635 initEvents : function()
26638 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26639 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26640 //}else{ // allow click to work like normal
26641 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26643 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26644 this.grid.on("rowclick", this.handleMouseDown, this);
26646 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26647 "up" : function(e){
26649 this.selectPrevious(e.shiftKey);
26650 }else if(this.last !== false && this.lastActive !== false){
26651 var last = this.last;
26652 this.selectRange(this.last, this.lastActive-1);
26653 this.grid.getView().focusRow(this.lastActive);
26654 if(last !== false){
26658 this.selectFirstRow();
26660 this.fireEvent("afterselectionchange", this);
26662 "down" : function(e){
26664 this.selectNext(e.shiftKey);
26665 }else if(this.last !== false && this.lastActive !== false){
26666 var last = this.last;
26667 this.selectRange(this.last, this.lastActive+1);
26668 this.grid.getView().focusRow(this.lastActive);
26669 if(last !== false){
26673 this.selectFirstRow();
26675 this.fireEvent("afterselectionchange", this);
26679 this.grid.store.on('load', function(){
26680 this.selections.clear();
26683 var view = this.grid.view;
26684 view.on("refresh", this.onRefresh, this);
26685 view.on("rowupdated", this.onRowUpdated, this);
26686 view.on("rowremoved", this.onRemove, this);
26691 onRefresh : function()
26693 var ds = this.grid.store, i, v = this.grid.view;
26694 var s = this.selections;
26695 s.each(function(r){
26696 if((i = ds.indexOfId(r.id)) != -1){
26705 onRemove : function(v, index, r){
26706 this.selections.remove(r);
26710 onRowUpdated : function(v, index, r){
26711 if(this.isSelected(r)){
26712 v.onRowSelect(index);
26718 * @param {Array} records The records to select
26719 * @param {Boolean} keepExisting (optional) True to keep existing selections
26721 selectRecords : function(records, keepExisting)
26724 this.clearSelections();
26726 var ds = this.grid.store;
26727 for(var i = 0, len = records.length; i < len; i++){
26728 this.selectRow(ds.indexOf(records[i]), true);
26733 * Gets the number of selected rows.
26736 getCount : function(){
26737 return this.selections.length;
26741 * Selects the first row in the grid.
26743 selectFirstRow : function(){
26748 * Select the last row.
26749 * @param {Boolean} keepExisting (optional) True to keep existing selections
26751 selectLastRow : function(keepExisting){
26752 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26753 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26757 * Selects the row immediately following the last selected row.
26758 * @param {Boolean} keepExisting (optional) True to keep existing selections
26760 selectNext : function(keepExisting)
26762 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26763 this.selectRow(this.last+1, keepExisting);
26764 this.grid.getView().focusRow(this.last);
26769 * Selects the row that precedes the last selected row.
26770 * @param {Boolean} keepExisting (optional) True to keep existing selections
26772 selectPrevious : function(keepExisting){
26774 this.selectRow(this.last-1, keepExisting);
26775 this.grid.getView().focusRow(this.last);
26780 * Returns the selected records
26781 * @return {Array} Array of selected records
26783 getSelections : function(){
26784 return [].concat(this.selections.items);
26788 * Returns the first selected record.
26791 getSelected : function(){
26792 return this.selections.itemAt(0);
26797 * Clears all selections.
26799 clearSelections : function(fast)
26805 var ds = this.grid.store;
26806 var s = this.selections;
26807 s.each(function(r){
26808 this.deselectRow(ds.indexOfId(r.id));
26812 this.selections.clear();
26819 * Selects all rows.
26821 selectAll : function(){
26825 this.selections.clear();
26826 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26827 this.selectRow(i, true);
26832 * Returns True if there is a selection.
26833 * @return {Boolean}
26835 hasSelection : function(){
26836 return this.selections.length > 0;
26840 * Returns True if the specified row is selected.
26841 * @param {Number/Record} record The record or index of the record to check
26842 * @return {Boolean}
26844 isSelected : function(index){
26845 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26846 return (r && this.selections.key(r.id) ? true : false);
26850 * Returns True if the specified record id is selected.
26851 * @param {String} id The id of record to check
26852 * @return {Boolean}
26854 isIdSelected : function(id){
26855 return (this.selections.key(id) ? true : false);
26860 handleMouseDBClick : function(e, t){
26864 handleMouseDown : function(e, t)
26866 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26867 if(this.isLocked() || rowIndex < 0 ){
26870 if(e.shiftKey && this.last !== false){
26871 var last = this.last;
26872 this.selectRange(last, rowIndex, e.ctrlKey);
26873 this.last = last; // reset the last
26877 var isSelected = this.isSelected(rowIndex);
26878 //Roo.log("select row:" + rowIndex);
26880 this.deselectRow(rowIndex);
26882 this.selectRow(rowIndex, true);
26886 if(e.button !== 0 && isSelected){
26887 alert('rowIndex 2: ' + rowIndex);
26888 view.focusRow(rowIndex);
26889 }else if(e.ctrlKey && isSelected){
26890 this.deselectRow(rowIndex);
26891 }else if(!isSelected){
26892 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26893 view.focusRow(rowIndex);
26897 this.fireEvent("afterselectionchange", this);
26900 handleDragableRowClick : function(grid, rowIndex, e)
26902 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26903 this.selectRow(rowIndex, false);
26904 grid.view.focusRow(rowIndex);
26905 this.fireEvent("afterselectionchange", this);
26910 * Selects multiple rows.
26911 * @param {Array} rows Array of the indexes of the row to select
26912 * @param {Boolean} keepExisting (optional) True to keep existing selections
26914 selectRows : function(rows, keepExisting){
26916 this.clearSelections();
26918 for(var i = 0, len = rows.length; i < len; i++){
26919 this.selectRow(rows[i], true);
26924 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26925 * @param {Number} startRow The index of the first row in the range
26926 * @param {Number} endRow The index of the last row in the range
26927 * @param {Boolean} keepExisting (optional) True to retain existing selections
26929 selectRange : function(startRow, endRow, keepExisting){
26934 this.clearSelections();
26936 if(startRow <= endRow){
26937 for(var i = startRow; i <= endRow; i++){
26938 this.selectRow(i, true);
26941 for(var i = startRow; i >= endRow; i--){
26942 this.selectRow(i, true);
26948 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26949 * @param {Number} startRow The index of the first row in the range
26950 * @param {Number} endRow The index of the last row in the range
26952 deselectRange : function(startRow, endRow, preventViewNotify){
26956 for(var i = startRow; i <= endRow; i++){
26957 this.deselectRow(i, preventViewNotify);
26963 * @param {Number} row The index of the row to select
26964 * @param {Boolean} keepExisting (optional) True to keep existing selections
26966 selectRow : function(index, keepExisting, preventViewNotify)
26968 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26971 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26972 if(!keepExisting || this.singleSelect){
26973 this.clearSelections();
26976 var r = this.grid.store.getAt(index);
26977 //console.log('selectRow - record id :' + r.id);
26979 this.selections.add(r);
26980 this.last = this.lastActive = index;
26981 if(!preventViewNotify){
26982 var proxy = new Roo.Element(
26983 this.grid.getRowDom(index)
26985 proxy.addClass('bg-info info');
26987 this.fireEvent("rowselect", this, index, r);
26988 this.fireEvent("selectionchange", this);
26994 * @param {Number} row The index of the row to deselect
26996 deselectRow : function(index, preventViewNotify)
27001 if(this.last == index){
27004 if(this.lastActive == index){
27005 this.lastActive = false;
27008 var r = this.grid.store.getAt(index);
27013 this.selections.remove(r);
27014 //.console.log('deselectRow - record id :' + r.id);
27015 if(!preventViewNotify){
27017 var proxy = new Roo.Element(
27018 this.grid.getRowDom(index)
27020 proxy.removeClass('bg-info info');
27022 this.fireEvent("rowdeselect", this, index);
27023 this.fireEvent("selectionchange", this);
27027 restoreLast : function(){
27029 this.last = this._last;
27034 acceptsNav : function(row, col, cm){
27035 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27039 onEditorKey : function(field, e){
27040 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27045 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27047 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27049 }else if(k == e.ENTER && !e.ctrlKey){
27053 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27055 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27057 }else if(k == e.ESC){
27061 g.startEditing(newCell[0], newCell[1]);
27067 * Ext JS Library 1.1.1
27068 * Copyright(c) 2006-2007, Ext JS, LLC.
27070 * Originally Released Under LGPL - original licence link has changed is not relivant.
27073 * <script type="text/javascript">
27077 * @class Roo.bootstrap.PagingToolbar
27078 * @extends Roo.bootstrap.NavSimplebar
27079 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27081 * Create a new PagingToolbar
27082 * @param {Object} config The config object
27083 * @param {Roo.data.Store} store
27085 Roo.bootstrap.PagingToolbar = function(config)
27087 // old args format still supported... - xtype is prefered..
27088 // created from xtype...
27090 this.ds = config.dataSource;
27092 if (config.store && !this.ds) {
27093 this.store= Roo.factory(config.store, Roo.data);
27094 this.ds = this.store;
27095 this.ds.xmodule = this.xmodule || false;
27098 this.toolbarItems = [];
27099 if (config.items) {
27100 this.toolbarItems = config.items;
27103 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27108 this.bind(this.ds);
27111 if (Roo.bootstrap.version == 4) {
27112 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27114 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27121 * @cfg {Roo.data.Store} dataSource
27122 * The underlying data store providing the paged data
27125 * @cfg {String/HTMLElement/Element} container
27126 * container The id or element that will contain the toolbar
27129 * @cfg {Boolean} displayInfo
27130 * True to display the displayMsg (defaults to false)
27133 * @cfg {Number} pageSize
27134 * The number of records to display per page (defaults to 20)
27138 * @cfg {String} displayMsg
27139 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27141 displayMsg : 'Displaying {0} - {1} of {2}',
27143 * @cfg {String} emptyMsg
27144 * The message to display when no records are found (defaults to "No data to display")
27146 emptyMsg : 'No data to display',
27148 * Customizable piece of the default paging text (defaults to "Page")
27151 beforePageText : "Page",
27153 * Customizable piece of the default paging text (defaults to "of %0")
27156 afterPageText : "of {0}",
27158 * Customizable piece of the default paging text (defaults to "First Page")
27161 firstText : "First Page",
27163 * Customizable piece of the default paging text (defaults to "Previous Page")
27166 prevText : "Previous Page",
27168 * Customizable piece of the default paging text (defaults to "Next Page")
27171 nextText : "Next Page",
27173 * Customizable piece of the default paging text (defaults to "Last Page")
27176 lastText : "Last Page",
27178 * Customizable piece of the default paging text (defaults to "Refresh")
27181 refreshText : "Refresh",
27185 onRender : function(ct, position)
27187 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27188 this.navgroup.parentId = this.id;
27189 this.navgroup.onRender(this.el, null);
27190 // add the buttons to the navgroup
27192 if(this.displayInfo){
27193 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27194 this.displayEl = this.el.select('.x-paging-info', true).first();
27195 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27196 // this.displayEl = navel.el.select('span',true).first();
27202 Roo.each(_this.buttons, function(e){ // this might need to use render????
27203 Roo.factory(e).render(_this.el);
27207 Roo.each(_this.toolbarItems, function(e) {
27208 _this.navgroup.addItem(e);
27212 this.first = this.navgroup.addItem({
27213 tooltip: this.firstText,
27214 cls: "prev btn-outline-secondary",
27215 html : ' <i class="fa fa-step-backward"></i>',
27217 preventDefault: true,
27218 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27221 this.prev = this.navgroup.addItem({
27222 tooltip: this.prevText,
27223 cls: "prev btn-outline-secondary",
27224 html : ' <i class="fa fa-backward"></i>',
27226 preventDefault: true,
27227 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27229 //this.addSeparator();
27232 var field = this.navgroup.addItem( {
27234 cls : 'x-paging-position btn-outline-secondary',
27236 html : this.beforePageText +
27237 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27238 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27241 this.field = field.el.select('input', true).first();
27242 this.field.on("keydown", this.onPagingKeydown, this);
27243 this.field.on("focus", function(){this.dom.select();});
27246 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27247 //this.field.setHeight(18);
27248 //this.addSeparator();
27249 this.next = this.navgroup.addItem({
27250 tooltip: this.nextText,
27251 cls: "next btn-outline-secondary",
27252 html : ' <i class="fa fa-forward"></i>',
27254 preventDefault: true,
27255 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27257 this.last = this.navgroup.addItem({
27258 tooltip: this.lastText,
27259 html : ' <i class="fa fa-step-forward"></i>',
27260 cls: "next btn-outline-secondary",
27262 preventDefault: true,
27263 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27265 //this.addSeparator();
27266 this.loading = this.navgroup.addItem({
27267 tooltip: this.refreshText,
27268 cls: "btn-outline-secondary",
27269 html : ' <i class="fa fa-refresh"></i>',
27270 preventDefault: true,
27271 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27277 updateInfo : function(){
27278 if(this.displayEl){
27279 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27280 var msg = count == 0 ?
27284 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27286 this.displayEl.update(msg);
27291 onLoad : function(ds, r, o)
27293 this.cursor = o.params.start ? o.params.start : 0;
27295 var d = this.getPageData(),
27300 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27301 this.field.dom.value = ap;
27302 this.first.setDisabled(ap == 1);
27303 this.prev.setDisabled(ap == 1);
27304 this.next.setDisabled(ap == ps);
27305 this.last.setDisabled(ap == ps);
27306 this.loading.enable();
27311 getPageData : function(){
27312 var total = this.ds.getTotalCount();
27315 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27316 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27321 onLoadError : function(){
27322 this.loading.enable();
27326 onPagingKeydown : function(e){
27327 var k = e.getKey();
27328 var d = this.getPageData();
27330 var v = this.field.dom.value, pageNum;
27331 if(!v || isNaN(pageNum = parseInt(v, 10))){
27332 this.field.dom.value = d.activePage;
27335 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27336 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27339 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))
27341 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27342 this.field.dom.value = pageNum;
27343 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27346 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27348 var v = this.field.dom.value, pageNum;
27349 var increment = (e.shiftKey) ? 10 : 1;
27350 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27353 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27354 this.field.dom.value = d.activePage;
27357 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27359 this.field.dom.value = parseInt(v, 10) + increment;
27360 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27361 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27368 beforeLoad : function(){
27370 this.loading.disable();
27375 onClick : function(which){
27384 ds.load({params:{start: 0, limit: this.pageSize}});
27387 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27390 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27393 var total = ds.getTotalCount();
27394 var extra = total % this.pageSize;
27395 var lastStart = extra ? (total - extra) : total-this.pageSize;
27396 ds.load({params:{start: lastStart, limit: this.pageSize}});
27399 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27405 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27406 * @param {Roo.data.Store} store The data store to unbind
27408 unbind : function(ds){
27409 ds.un("beforeload", this.beforeLoad, this);
27410 ds.un("load", this.onLoad, this);
27411 ds.un("loadexception", this.onLoadError, this);
27412 ds.un("remove", this.updateInfo, this);
27413 ds.un("add", this.updateInfo, this);
27414 this.ds = undefined;
27418 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27419 * @param {Roo.data.Store} store The data store to bind
27421 bind : function(ds){
27422 ds.on("beforeload", this.beforeLoad, this);
27423 ds.on("load", this.onLoad, this);
27424 ds.on("loadexception", this.onLoadError, this);
27425 ds.on("remove", this.updateInfo, this);
27426 ds.on("add", this.updateInfo, this);
27437 * @class Roo.bootstrap.MessageBar
27438 * @extends Roo.bootstrap.Component
27439 * Bootstrap MessageBar class
27440 * @cfg {String} html contents of the MessageBar
27441 * @cfg {String} weight (info | success | warning | danger) default info
27442 * @cfg {String} beforeClass insert the bar before the given class
27443 * @cfg {Boolean} closable (true | false) default false
27444 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27447 * Create a new Element
27448 * @param {Object} config The config object
27451 Roo.bootstrap.MessageBar = function(config){
27452 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27455 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27461 beforeClass: 'bootstrap-sticky-wrap',
27463 getAutoCreate : function(){
27467 cls: 'alert alert-dismissable alert-' + this.weight,
27472 html: this.html || ''
27478 cfg.cls += ' alert-messages-fixed';
27492 onRender : function(ct, position)
27494 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27497 var cfg = Roo.apply({}, this.getAutoCreate());
27501 cfg.cls += ' ' + this.cls;
27504 cfg.style = this.style;
27506 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27508 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27511 this.el.select('>button.close').on('click', this.hide, this);
27517 if (!this.rendered) {
27523 this.fireEvent('show', this);
27529 if (!this.rendered) {
27535 this.fireEvent('hide', this);
27538 update : function()
27540 // var e = this.el.dom.firstChild;
27542 // if(this.closable){
27543 // e = e.nextSibling;
27546 // e.data = this.html || '';
27548 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27564 * @class Roo.bootstrap.Graph
27565 * @extends Roo.bootstrap.Component
27566 * Bootstrap Graph class
27570 @cfg {String} graphtype bar | vbar | pie
27571 @cfg {number} g_x coodinator | centre x (pie)
27572 @cfg {number} g_y coodinator | centre y (pie)
27573 @cfg {number} g_r radius (pie)
27574 @cfg {number} g_height height of the chart (respected by all elements in the set)
27575 @cfg {number} g_width width of the chart (respected by all elements in the set)
27576 @cfg {Object} title The title of the chart
27579 -opts (object) options for the chart
27581 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27582 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27584 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.
27585 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27587 o stretch (boolean)
27589 -opts (object) options for the pie
27592 o startAngle (number)
27593 o endAngle (number)
27597 * Create a new Input
27598 * @param {Object} config The config object
27601 Roo.bootstrap.Graph = function(config){
27602 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27608 * The img click event for the img.
27609 * @param {Roo.EventObject} e
27615 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27626 //g_colors: this.colors,
27633 getAutoCreate : function(){
27644 onRender : function(ct,position){
27647 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27649 if (typeof(Raphael) == 'undefined') {
27650 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27654 this.raphael = Raphael(this.el.dom);
27656 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27657 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27658 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27659 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27661 r.text(160, 10, "Single Series Chart").attr(txtattr);
27662 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27663 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27664 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27666 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27667 r.barchart(330, 10, 300, 220, data1);
27668 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27669 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27672 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27673 // r.barchart(30, 30, 560, 250, xdata, {
27674 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27675 // axis : "0 0 1 1",
27676 // axisxlabels : xdata
27677 // //yvalues : cols,
27680 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27682 // this.load(null,xdata,{
27683 // axis : "0 0 1 1",
27684 // axisxlabels : xdata
27689 load : function(graphtype,xdata,opts)
27691 this.raphael.clear();
27693 graphtype = this.graphtype;
27698 var r = this.raphael,
27699 fin = function () {
27700 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27702 fout = function () {
27703 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27705 pfin = function() {
27706 this.sector.stop();
27707 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27710 this.label[0].stop();
27711 this.label[0].attr({ r: 7.5 });
27712 this.label[1].attr({ "font-weight": 800 });
27715 pfout = function() {
27716 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27719 this.label[0].animate({ r: 5 }, 500, "bounce");
27720 this.label[1].attr({ "font-weight": 400 });
27726 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27729 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27732 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27733 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27735 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27742 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27747 setTitle: function(o)
27752 initEvents: function() {
27755 this.el.on('click', this.onClick, this);
27759 onClick : function(e)
27761 Roo.log('img onclick');
27762 this.fireEvent('click', this, e);
27774 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27777 * @class Roo.bootstrap.dash.NumberBox
27778 * @extends Roo.bootstrap.Component
27779 * Bootstrap NumberBox class
27780 * @cfg {String} headline Box headline
27781 * @cfg {String} content Box content
27782 * @cfg {String} icon Box icon
27783 * @cfg {String} footer Footer text
27784 * @cfg {String} fhref Footer href
27787 * Create a new NumberBox
27788 * @param {Object} config The config object
27792 Roo.bootstrap.dash.NumberBox = function(config){
27793 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27797 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27806 getAutoCreate : function(){
27810 cls : 'small-box ',
27818 cls : 'roo-headline',
27819 html : this.headline
27823 cls : 'roo-content',
27824 html : this.content
27838 cls : 'ion ' + this.icon
27847 cls : 'small-box-footer',
27848 href : this.fhref || '#',
27852 cfg.cn.push(footer);
27859 onRender : function(ct,position){
27860 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27867 setHeadline: function (value)
27869 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27872 setFooter: function (value, href)
27874 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27877 this.el.select('a.small-box-footer',true).first().attr('href', href);
27882 setContent: function (value)
27884 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27887 initEvents: function()
27901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27904 * @class Roo.bootstrap.dash.TabBox
27905 * @extends Roo.bootstrap.Component
27906 * Bootstrap TabBox class
27907 * @cfg {String} title Title of the TabBox
27908 * @cfg {String} icon Icon of the TabBox
27909 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27910 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27913 * Create a new TabBox
27914 * @param {Object} config The config object
27918 Roo.bootstrap.dash.TabBox = function(config){
27919 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27924 * When a pane is added
27925 * @param {Roo.bootstrap.dash.TabPane} pane
27929 * @event activatepane
27930 * When a pane is activated
27931 * @param {Roo.bootstrap.dash.TabPane} pane
27933 "activatepane" : true
27941 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27946 tabScrollable : false,
27948 getChildContainer : function()
27950 return this.el.select('.tab-content', true).first();
27953 getAutoCreate : function(){
27957 cls: 'pull-left header',
27965 cls: 'fa ' + this.icon
27971 cls: 'nav nav-tabs pull-right',
27977 if(this.tabScrollable){
27984 cls: 'nav nav-tabs pull-right',
27995 cls: 'nav-tabs-custom',
28000 cls: 'tab-content no-padding',
28008 initEvents : function()
28010 //Roo.log('add add pane handler');
28011 this.on('addpane', this.onAddPane, this);
28014 * Updates the box title
28015 * @param {String} html to set the title to.
28017 setTitle : function(value)
28019 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28021 onAddPane : function(pane)
28023 this.panes.push(pane);
28024 //Roo.log('addpane');
28026 // tabs are rendere left to right..
28027 if(!this.showtabs){
28031 var ctr = this.el.select('.nav-tabs', true).first();
28034 var existing = ctr.select('.nav-tab',true);
28035 var qty = existing.getCount();;
28038 var tab = ctr.createChild({
28040 cls : 'nav-tab' + (qty ? '' : ' active'),
28048 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28051 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28053 pane.el.addClass('active');
28058 onTabClick : function(ev,un,ob,pane)
28060 //Roo.log('tab - prev default');
28061 ev.preventDefault();
28064 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28065 pane.tab.addClass('active');
28066 //Roo.log(pane.title);
28067 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28068 // technically we should have a deactivate event.. but maybe add later.
28069 // and it should not de-activate the selected tab...
28070 this.fireEvent('activatepane', pane);
28071 pane.el.addClass('active');
28072 pane.fireEvent('activate');
28077 getActivePane : function()
28080 Roo.each(this.panes, function(p) {
28081 if(p.el.hasClass('active')){
28102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28104 * @class Roo.bootstrap.TabPane
28105 * @extends Roo.bootstrap.Component
28106 * Bootstrap TabPane class
28107 * @cfg {Boolean} active (false | true) Default false
28108 * @cfg {String} title title of panel
28112 * Create a new TabPane
28113 * @param {Object} config The config object
28116 Roo.bootstrap.dash.TabPane = function(config){
28117 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28123 * When a pane is activated
28124 * @param {Roo.bootstrap.dash.TabPane} pane
28131 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28136 // the tabBox that this is attached to.
28139 getAutoCreate : function()
28147 cfg.cls += ' active';
28152 initEvents : function()
28154 //Roo.log('trigger add pane handler');
28155 this.parent().fireEvent('addpane', this)
28159 * Updates the tab title
28160 * @param {String} html to set the title to.
28162 setTitle: function(str)
28168 this.tab.select('a', true).first().dom.innerHTML = str;
28185 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28188 * @class Roo.bootstrap.menu.Menu
28189 * @extends Roo.bootstrap.Component
28190 * Bootstrap Menu class - container for Menu
28191 * @cfg {String} html Text of the menu
28192 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28193 * @cfg {String} icon Font awesome icon
28194 * @cfg {String} pos Menu align to (top | bottom) default bottom
28198 * Create a new Menu
28199 * @param {Object} config The config object
28203 Roo.bootstrap.menu.Menu = function(config){
28204 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28208 * @event beforeshow
28209 * Fires before this menu is displayed
28210 * @param {Roo.bootstrap.menu.Menu} this
28214 * @event beforehide
28215 * Fires before this menu is hidden
28216 * @param {Roo.bootstrap.menu.Menu} this
28221 * Fires after this menu is displayed
28222 * @param {Roo.bootstrap.menu.Menu} this
28227 * Fires after this menu is hidden
28228 * @param {Roo.bootstrap.menu.Menu} this
28233 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28234 * @param {Roo.bootstrap.menu.Menu} this
28235 * @param {Roo.EventObject} e
28242 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28246 weight : 'default',
28251 getChildContainer : function() {
28252 if(this.isSubMenu){
28256 return this.el.select('ul.dropdown-menu', true).first();
28259 getAutoCreate : function()
28264 cls : 'roo-menu-text',
28272 cls : 'fa ' + this.icon
28283 cls : 'dropdown-button btn btn-' + this.weight,
28288 cls : 'dropdown-toggle btn btn-' + this.weight,
28298 cls : 'dropdown-menu'
28304 if(this.pos == 'top'){
28305 cfg.cls += ' dropup';
28308 if(this.isSubMenu){
28311 cls : 'dropdown-menu'
28318 onRender : function(ct, position)
28320 this.isSubMenu = ct.hasClass('dropdown-submenu');
28322 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28325 initEvents : function()
28327 if(this.isSubMenu){
28331 this.hidden = true;
28333 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28334 this.triggerEl.on('click', this.onTriggerPress, this);
28336 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28337 this.buttonEl.on('click', this.onClick, this);
28343 if(this.isSubMenu){
28347 return this.el.select('ul.dropdown-menu', true).first();
28350 onClick : function(e)
28352 this.fireEvent("click", this, e);
28355 onTriggerPress : function(e)
28357 if (this.isVisible()) {
28364 isVisible : function(){
28365 return !this.hidden;
28370 this.fireEvent("beforeshow", this);
28372 this.hidden = false;
28373 this.el.addClass('open');
28375 Roo.get(document).on("mouseup", this.onMouseUp, this);
28377 this.fireEvent("show", this);
28384 this.fireEvent("beforehide", this);
28386 this.hidden = true;
28387 this.el.removeClass('open');
28389 Roo.get(document).un("mouseup", this.onMouseUp);
28391 this.fireEvent("hide", this);
28394 onMouseUp : function()
28408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28411 * @class Roo.bootstrap.menu.Item
28412 * @extends Roo.bootstrap.Component
28413 * Bootstrap MenuItem class
28414 * @cfg {Boolean} submenu (true | false) default false
28415 * @cfg {String} html text of the item
28416 * @cfg {String} href the link
28417 * @cfg {Boolean} disable (true | false) default false
28418 * @cfg {Boolean} preventDefault (true | false) default true
28419 * @cfg {String} icon Font awesome icon
28420 * @cfg {String} pos Submenu align to (left | right) default right
28424 * Create a new Item
28425 * @param {Object} config The config object
28429 Roo.bootstrap.menu.Item = function(config){
28430 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28434 * Fires when the mouse is hovering over this menu
28435 * @param {Roo.bootstrap.menu.Item} this
28436 * @param {Roo.EventObject} e
28441 * Fires when the mouse exits this menu
28442 * @param {Roo.bootstrap.menu.Item} this
28443 * @param {Roo.EventObject} e
28449 * The raw click event for the entire grid.
28450 * @param {Roo.EventObject} e
28456 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28461 preventDefault: true,
28466 getAutoCreate : function()
28471 cls : 'roo-menu-item-text',
28479 cls : 'fa ' + this.icon
28488 href : this.href || '#',
28495 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28499 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28501 if(this.pos == 'left'){
28502 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28509 initEvents : function()
28511 this.el.on('mouseover', this.onMouseOver, this);
28512 this.el.on('mouseout', this.onMouseOut, this);
28514 this.el.select('a', true).first().on('click', this.onClick, this);
28518 onClick : function(e)
28520 if(this.preventDefault){
28521 e.preventDefault();
28524 this.fireEvent("click", this, e);
28527 onMouseOver : function(e)
28529 if(this.submenu && this.pos == 'left'){
28530 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28533 this.fireEvent("mouseover", this, e);
28536 onMouseOut : function(e)
28538 this.fireEvent("mouseout", this, e);
28550 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28553 * @class Roo.bootstrap.menu.Separator
28554 * @extends Roo.bootstrap.Component
28555 * Bootstrap Separator class
28558 * Create a new Separator
28559 * @param {Object} config The config object
28563 Roo.bootstrap.menu.Separator = function(config){
28564 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28567 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28569 getAutoCreate : function(){
28590 * @class Roo.bootstrap.Tooltip
28591 * Bootstrap Tooltip class
28592 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28593 * to determine which dom element triggers the tooltip.
28595 * It needs to add support for additional attributes like tooltip-position
28598 * Create a new Toolti
28599 * @param {Object} config The config object
28602 Roo.bootstrap.Tooltip = function(config){
28603 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28605 this.alignment = Roo.bootstrap.Tooltip.alignment;
28607 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28608 this.alignment = config.alignment;
28613 Roo.apply(Roo.bootstrap.Tooltip, {
28615 * @function init initialize tooltip monitoring.
28619 currentTip : false,
28620 currentRegion : false,
28626 Roo.get(document).on('mouseover', this.enter ,this);
28627 Roo.get(document).on('mouseout', this.leave, this);
28630 this.currentTip = new Roo.bootstrap.Tooltip();
28633 enter : function(ev)
28635 var dom = ev.getTarget();
28637 //Roo.log(['enter',dom]);
28638 var el = Roo.fly(dom);
28639 if (this.currentEl) {
28641 //Roo.log(this.currentEl);
28642 //Roo.log(this.currentEl.contains(dom));
28643 if (this.currentEl == el) {
28646 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28652 if (this.currentTip.el) {
28653 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28657 if(!el || el.dom == document){
28663 // you can not look for children, as if el is the body.. then everythign is the child..
28664 if (!el.attr('tooltip')) { //
28665 if (!el.select("[tooltip]").elements.length) {
28668 // is the mouse over this child...?
28669 bindEl = el.select("[tooltip]").first();
28670 var xy = ev.getXY();
28671 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28672 //Roo.log("not in region.");
28675 //Roo.log("child element over..");
28678 this.currentEl = bindEl;
28679 this.currentTip.bind(bindEl);
28680 this.currentRegion = Roo.lib.Region.getRegion(dom);
28681 this.currentTip.enter();
28684 leave : function(ev)
28686 var dom = ev.getTarget();
28687 //Roo.log(['leave',dom]);
28688 if (!this.currentEl) {
28693 if (dom != this.currentEl.dom) {
28696 var xy = ev.getXY();
28697 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28700 // only activate leave if mouse cursor is outside... bounding box..
28705 if (this.currentTip) {
28706 this.currentTip.leave();
28708 //Roo.log('clear currentEl');
28709 this.currentEl = false;
28714 'left' : ['r-l', [-2,0], 'right'],
28715 'right' : ['l-r', [2,0], 'left'],
28716 'bottom' : ['t-b', [0,2], 'top'],
28717 'top' : [ 'b-t', [0,-2], 'bottom']
28723 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28728 delay : null, // can be { show : 300 , hide: 500}
28732 hoverState : null, //???
28734 placement : 'bottom',
28738 getAutoCreate : function(){
28745 cls : 'tooltip-arrow arrow'
28748 cls : 'tooltip-inner'
28755 bind : function(el)
28760 initEvents : function()
28762 this.arrowEl = this.el.select('.arrow', true).first();
28763 this.innerEl = this.el.select('.tooltip-inner', true).first();
28766 enter : function () {
28768 if (this.timeout != null) {
28769 clearTimeout(this.timeout);
28772 this.hoverState = 'in';
28773 //Roo.log("enter - show");
28774 if (!this.delay || !this.delay.show) {
28779 this.timeout = setTimeout(function () {
28780 if (_t.hoverState == 'in') {
28783 }, this.delay.show);
28787 clearTimeout(this.timeout);
28789 this.hoverState = 'out';
28790 if (!this.delay || !this.delay.hide) {
28796 this.timeout = setTimeout(function () {
28797 //Roo.log("leave - timeout");
28799 if (_t.hoverState == 'out') {
28801 Roo.bootstrap.Tooltip.currentEl = false;
28806 show : function (msg)
28809 this.render(document.body);
28812 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28814 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28816 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28818 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28819 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28821 var placement = typeof this.placement == 'function' ?
28822 this.placement.call(this, this.el, on_el) :
28825 var autoToken = /\s?auto?\s?/i;
28826 var autoPlace = autoToken.test(placement);
28828 placement = placement.replace(autoToken, '') || 'top';
28832 //this.el.setXY([0,0]);
28834 //this.el.dom.style.display='block';
28836 //this.el.appendTo(on_el);
28838 var p = this.getPosition();
28839 var box = this.el.getBox();
28845 var align = this.alignment[placement];
28847 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28849 if(placement == 'top' || placement == 'bottom'){
28851 placement = 'right';
28854 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28855 placement = 'left';
28858 var scroll = Roo.select('body', true).first().getScroll();
28860 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28864 align = this.alignment[placement];
28866 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28870 this.el.alignTo(this.bindEl, align[0],align[1]);
28871 //var arrow = this.el.select('.arrow',true).first();
28872 //arrow.set(align[2],
28874 this.el.addClass(placement);
28875 this.el.addClass("bs-tooltip-"+ placement);
28877 this.el.addClass('in fade show');
28879 this.hoverState = null;
28881 if (this.el.hasClass('fade')) {
28896 //this.el.setXY([0,0]);
28897 this.el.removeClass(['show', 'in']);
28913 * @class Roo.bootstrap.LocationPicker
28914 * @extends Roo.bootstrap.Component
28915 * Bootstrap LocationPicker class
28916 * @cfg {Number} latitude Position when init default 0
28917 * @cfg {Number} longitude Position when init default 0
28918 * @cfg {Number} zoom default 15
28919 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28920 * @cfg {Boolean} mapTypeControl default false
28921 * @cfg {Boolean} disableDoubleClickZoom default false
28922 * @cfg {Boolean} scrollwheel default true
28923 * @cfg {Boolean} streetViewControl default false
28924 * @cfg {Number} radius default 0
28925 * @cfg {String} locationName
28926 * @cfg {Boolean} draggable default true
28927 * @cfg {Boolean} enableAutocomplete default false
28928 * @cfg {Boolean} enableReverseGeocode default true
28929 * @cfg {String} markerTitle
28932 * Create a new LocationPicker
28933 * @param {Object} config The config object
28937 Roo.bootstrap.LocationPicker = function(config){
28939 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28944 * Fires when the picker initialized.
28945 * @param {Roo.bootstrap.LocationPicker} this
28946 * @param {Google Location} location
28950 * @event positionchanged
28951 * Fires when the picker position changed.
28952 * @param {Roo.bootstrap.LocationPicker} this
28953 * @param {Google Location} location
28955 positionchanged : true,
28958 * Fires when the map resize.
28959 * @param {Roo.bootstrap.LocationPicker} this
28964 * Fires when the map show.
28965 * @param {Roo.bootstrap.LocationPicker} this
28970 * Fires when the map hide.
28971 * @param {Roo.bootstrap.LocationPicker} this
28976 * Fires when click the map.
28977 * @param {Roo.bootstrap.LocationPicker} this
28978 * @param {Map event} e
28982 * @event mapRightClick
28983 * Fires when right click the map.
28984 * @param {Roo.bootstrap.LocationPicker} this
28985 * @param {Map event} e
28987 mapRightClick : true,
28989 * @event markerClick
28990 * Fires when click the marker.
28991 * @param {Roo.bootstrap.LocationPicker} this
28992 * @param {Map event} e
28994 markerClick : true,
28996 * @event markerRightClick
28997 * Fires when right click the marker.
28998 * @param {Roo.bootstrap.LocationPicker} this
28999 * @param {Map event} e
29001 markerRightClick : true,
29003 * @event OverlayViewDraw
29004 * Fires when OverlayView Draw
29005 * @param {Roo.bootstrap.LocationPicker} this
29007 OverlayViewDraw : true,
29009 * @event OverlayViewOnAdd
29010 * Fires when OverlayView Draw
29011 * @param {Roo.bootstrap.LocationPicker} this
29013 OverlayViewOnAdd : true,
29015 * @event OverlayViewOnRemove
29016 * Fires when OverlayView Draw
29017 * @param {Roo.bootstrap.LocationPicker} this
29019 OverlayViewOnRemove : true,
29021 * @event OverlayViewShow
29022 * Fires when OverlayView Draw
29023 * @param {Roo.bootstrap.LocationPicker} this
29024 * @param {Pixel} cpx
29026 OverlayViewShow : true,
29028 * @event OverlayViewHide
29029 * Fires when OverlayView Draw
29030 * @param {Roo.bootstrap.LocationPicker} this
29032 OverlayViewHide : true,
29034 * @event loadexception
29035 * Fires when load google lib failed.
29036 * @param {Roo.bootstrap.LocationPicker} this
29038 loadexception : true
29043 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29045 gMapContext: false,
29051 mapTypeControl: false,
29052 disableDoubleClickZoom: false,
29054 streetViewControl: false,
29058 enableAutocomplete: false,
29059 enableReverseGeocode: true,
29062 getAutoCreate: function()
29067 cls: 'roo-location-picker'
29073 initEvents: function(ct, position)
29075 if(!this.el.getWidth() || this.isApplied()){
29079 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29084 initial: function()
29086 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29087 this.fireEvent('loadexception', this);
29091 if(!this.mapTypeId){
29092 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29095 this.gMapContext = this.GMapContext();
29097 this.initOverlayView();
29099 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29103 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29104 _this.setPosition(_this.gMapContext.marker.position);
29107 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29108 _this.fireEvent('mapClick', this, event);
29112 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29113 _this.fireEvent('mapRightClick', this, event);
29117 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29118 _this.fireEvent('markerClick', this, event);
29122 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29123 _this.fireEvent('markerRightClick', this, event);
29127 this.setPosition(this.gMapContext.location);
29129 this.fireEvent('initial', this, this.gMapContext.location);
29132 initOverlayView: function()
29136 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29140 _this.fireEvent('OverlayViewDraw', _this);
29145 _this.fireEvent('OverlayViewOnAdd', _this);
29148 onRemove: function()
29150 _this.fireEvent('OverlayViewOnRemove', _this);
29153 show: function(cpx)
29155 _this.fireEvent('OverlayViewShow', _this, cpx);
29160 _this.fireEvent('OverlayViewHide', _this);
29166 fromLatLngToContainerPixel: function(event)
29168 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29171 isApplied: function()
29173 return this.getGmapContext() == false ? false : true;
29176 getGmapContext: function()
29178 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29181 GMapContext: function()
29183 var position = new google.maps.LatLng(this.latitude, this.longitude);
29185 var _map = new google.maps.Map(this.el.dom, {
29188 mapTypeId: this.mapTypeId,
29189 mapTypeControl: this.mapTypeControl,
29190 disableDoubleClickZoom: this.disableDoubleClickZoom,
29191 scrollwheel: this.scrollwheel,
29192 streetViewControl: this.streetViewControl,
29193 locationName: this.locationName,
29194 draggable: this.draggable,
29195 enableAutocomplete: this.enableAutocomplete,
29196 enableReverseGeocode: this.enableReverseGeocode
29199 var _marker = new google.maps.Marker({
29200 position: position,
29202 title: this.markerTitle,
29203 draggable: this.draggable
29210 location: position,
29211 radius: this.radius,
29212 locationName: this.locationName,
29213 addressComponents: {
29214 formatted_address: null,
29215 addressLine1: null,
29216 addressLine2: null,
29218 streetNumber: null,
29222 stateOrProvince: null
29225 domContainer: this.el.dom,
29226 geodecoder: new google.maps.Geocoder()
29230 drawCircle: function(center, radius, options)
29232 if (this.gMapContext.circle != null) {
29233 this.gMapContext.circle.setMap(null);
29237 options = Roo.apply({}, options, {
29238 strokeColor: "#0000FF",
29239 strokeOpacity: .35,
29241 fillColor: "#0000FF",
29245 options.map = this.gMapContext.map;
29246 options.radius = radius;
29247 options.center = center;
29248 this.gMapContext.circle = new google.maps.Circle(options);
29249 return this.gMapContext.circle;
29255 setPosition: function(location)
29257 this.gMapContext.location = location;
29258 this.gMapContext.marker.setPosition(location);
29259 this.gMapContext.map.panTo(location);
29260 this.drawCircle(location, this.gMapContext.radius, {});
29264 if (this.gMapContext.settings.enableReverseGeocode) {
29265 this.gMapContext.geodecoder.geocode({
29266 latLng: this.gMapContext.location
29267 }, function(results, status) {
29269 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29270 _this.gMapContext.locationName = results[0].formatted_address;
29271 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29273 _this.fireEvent('positionchanged', this, location);
29280 this.fireEvent('positionchanged', this, location);
29285 google.maps.event.trigger(this.gMapContext.map, "resize");
29287 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29289 this.fireEvent('resize', this);
29292 setPositionByLatLng: function(latitude, longitude)
29294 this.setPosition(new google.maps.LatLng(latitude, longitude));
29297 getCurrentPosition: function()
29300 latitude: this.gMapContext.location.lat(),
29301 longitude: this.gMapContext.location.lng()
29305 getAddressName: function()
29307 return this.gMapContext.locationName;
29310 getAddressComponents: function()
29312 return this.gMapContext.addressComponents;
29315 address_component_from_google_geocode: function(address_components)
29319 for (var i = 0; i < address_components.length; i++) {
29320 var component = address_components[i];
29321 if (component.types.indexOf("postal_code") >= 0) {
29322 result.postalCode = component.short_name;
29323 } else if (component.types.indexOf("street_number") >= 0) {
29324 result.streetNumber = component.short_name;
29325 } else if (component.types.indexOf("route") >= 0) {
29326 result.streetName = component.short_name;
29327 } else if (component.types.indexOf("neighborhood") >= 0) {
29328 result.city = component.short_name;
29329 } else if (component.types.indexOf("locality") >= 0) {
29330 result.city = component.short_name;
29331 } else if (component.types.indexOf("sublocality") >= 0) {
29332 result.district = component.short_name;
29333 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29334 result.stateOrProvince = component.short_name;
29335 } else if (component.types.indexOf("country") >= 0) {
29336 result.country = component.short_name;
29340 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29341 result.addressLine2 = "";
29345 setZoomLevel: function(zoom)
29347 this.gMapContext.map.setZoom(zoom);
29360 this.fireEvent('show', this);
29371 this.fireEvent('hide', this);
29376 Roo.apply(Roo.bootstrap.LocationPicker, {
29378 OverlayView : function(map, options)
29380 options = options || {};
29387 * @class Roo.bootstrap.Alert
29388 * @extends Roo.bootstrap.Component
29389 * Bootstrap Alert class - shows an alert area box
29391 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29392 Enter a valid email address
29395 * @cfg {String} title The title of alert
29396 * @cfg {String} html The content of alert
29397 * @cfg {String} weight ( success | info | warning | danger )
29398 * @cfg {String} faicon font-awesomeicon
29401 * Create a new alert
29402 * @param {Object} config The config object
29406 Roo.bootstrap.Alert = function(config){
29407 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29411 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29418 getAutoCreate : function()
29427 cls : 'roo-alert-icon'
29432 cls : 'roo-alert-title',
29437 cls : 'roo-alert-text',
29444 cfg.cn[0].cls += ' fa ' + this.faicon;
29448 cfg.cls += ' alert-' + this.weight;
29454 initEvents: function()
29456 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29459 setTitle : function(str)
29461 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29464 setText : function(str)
29466 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29469 setWeight : function(weight)
29472 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29475 this.weight = weight;
29477 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29480 setIcon : function(icon)
29483 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29486 this.faicon = icon;
29488 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29509 * @class Roo.bootstrap.UploadCropbox
29510 * @extends Roo.bootstrap.Component
29511 * Bootstrap UploadCropbox class
29512 * @cfg {String} emptyText show when image has been loaded
29513 * @cfg {String} rotateNotify show when image too small to rotate
29514 * @cfg {Number} errorTimeout default 3000
29515 * @cfg {Number} minWidth default 300
29516 * @cfg {Number} minHeight default 300
29517 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29518 * @cfg {Boolean} isDocument (true|false) default false
29519 * @cfg {String} url action url
29520 * @cfg {String} paramName default 'imageUpload'
29521 * @cfg {String} method default POST
29522 * @cfg {Boolean} loadMask (true|false) default true
29523 * @cfg {Boolean} loadingText default 'Loading...'
29526 * Create a new UploadCropbox
29527 * @param {Object} config The config object
29530 Roo.bootstrap.UploadCropbox = function(config){
29531 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29535 * @event beforeselectfile
29536 * Fire before select file
29537 * @param {Roo.bootstrap.UploadCropbox} this
29539 "beforeselectfile" : true,
29542 * Fire after initEvent
29543 * @param {Roo.bootstrap.UploadCropbox} this
29548 * Fire after initEvent
29549 * @param {Roo.bootstrap.UploadCropbox} this
29550 * @param {String} data
29555 * Fire when preparing the file data
29556 * @param {Roo.bootstrap.UploadCropbox} this
29557 * @param {Object} file
29562 * Fire when get exception
29563 * @param {Roo.bootstrap.UploadCropbox} this
29564 * @param {XMLHttpRequest} xhr
29566 "exception" : true,
29568 * @event beforeloadcanvas
29569 * Fire before load the canvas
29570 * @param {Roo.bootstrap.UploadCropbox} this
29571 * @param {String} src
29573 "beforeloadcanvas" : true,
29576 * Fire when trash image
29577 * @param {Roo.bootstrap.UploadCropbox} this
29582 * Fire when download the image
29583 * @param {Roo.bootstrap.UploadCropbox} this
29587 * @event footerbuttonclick
29588 * Fire when footerbuttonclick
29589 * @param {Roo.bootstrap.UploadCropbox} this
29590 * @param {String} type
29592 "footerbuttonclick" : true,
29596 * @param {Roo.bootstrap.UploadCropbox} this
29601 * Fire when rotate the image
29602 * @param {Roo.bootstrap.UploadCropbox} this
29603 * @param {String} pos
29608 * Fire when inspect the file
29609 * @param {Roo.bootstrap.UploadCropbox} this
29610 * @param {Object} file
29615 * Fire when xhr upload the file
29616 * @param {Roo.bootstrap.UploadCropbox} this
29617 * @param {Object} data
29622 * Fire when arrange the file data
29623 * @param {Roo.bootstrap.UploadCropbox} this
29624 * @param {Object} formData
29629 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29632 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29634 emptyText : 'Click to upload image',
29635 rotateNotify : 'Image is too small to rotate',
29636 errorTimeout : 3000,
29650 cropType : 'image/jpeg',
29652 canvasLoaded : false,
29653 isDocument : false,
29655 paramName : 'imageUpload',
29657 loadingText : 'Loading...',
29660 getAutoCreate : function()
29664 cls : 'roo-upload-cropbox',
29668 cls : 'roo-upload-cropbox-selector',
29673 cls : 'roo-upload-cropbox-body',
29674 style : 'cursor:pointer',
29678 cls : 'roo-upload-cropbox-preview'
29682 cls : 'roo-upload-cropbox-thumb'
29686 cls : 'roo-upload-cropbox-empty-notify',
29687 html : this.emptyText
29691 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29692 html : this.rotateNotify
29698 cls : 'roo-upload-cropbox-footer',
29701 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29711 onRender : function(ct, position)
29713 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29715 if (this.buttons.length) {
29717 Roo.each(this.buttons, function(bb) {
29719 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29721 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29727 this.maskEl = this.el;
29731 initEvents : function()
29733 this.urlAPI = (window.createObjectURL && window) ||
29734 (window.URL && URL.revokeObjectURL && URL) ||
29735 (window.webkitURL && webkitURL);
29737 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29738 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29740 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29741 this.selectorEl.hide();
29743 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29744 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29746 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29747 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748 this.thumbEl.hide();
29750 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29751 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29753 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29754 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755 this.errorEl.hide();
29757 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29758 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29759 this.footerEl.hide();
29761 this.setThumbBoxSize();
29767 this.fireEvent('initial', this);
29774 window.addEventListener("resize", function() { _this.resize(); } );
29776 this.bodyEl.on('click', this.beforeSelectFile, this);
29779 this.bodyEl.on('touchstart', this.onTouchStart, this);
29780 this.bodyEl.on('touchmove', this.onTouchMove, this);
29781 this.bodyEl.on('touchend', this.onTouchEnd, this);
29785 this.bodyEl.on('mousedown', this.onMouseDown, this);
29786 this.bodyEl.on('mousemove', this.onMouseMove, this);
29787 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29788 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29789 Roo.get(document).on('mouseup', this.onMouseUp, this);
29792 this.selectorEl.on('change', this.onFileSelected, this);
29798 this.baseScale = 1;
29800 this.baseRotate = 1;
29801 this.dragable = false;
29802 this.pinching = false;
29805 this.cropData = false;
29806 this.notifyEl.dom.innerHTML = this.emptyText;
29808 this.selectorEl.dom.value = '';
29812 resize : function()
29814 if(this.fireEvent('resize', this) != false){
29815 this.setThumbBoxPosition();
29816 this.setCanvasPosition();
29820 onFooterButtonClick : function(e, el, o, type)
29823 case 'rotate-left' :
29824 this.onRotateLeft(e);
29826 case 'rotate-right' :
29827 this.onRotateRight(e);
29830 this.beforeSelectFile(e);
29845 this.fireEvent('footerbuttonclick', this, type);
29848 beforeSelectFile : function(e)
29850 e.preventDefault();
29852 if(this.fireEvent('beforeselectfile', this) != false){
29853 this.selectorEl.dom.click();
29857 onFileSelected : function(e)
29859 e.preventDefault();
29861 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29865 var file = this.selectorEl.dom.files[0];
29867 if(this.fireEvent('inspect', this, file) != false){
29868 this.prepare(file);
29873 trash : function(e)
29875 this.fireEvent('trash', this);
29878 download : function(e)
29880 this.fireEvent('download', this);
29883 loadCanvas : function(src)
29885 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29889 this.imageEl = document.createElement('img');
29893 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29895 this.imageEl.src = src;
29899 onLoadCanvas : function()
29901 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29902 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29904 this.bodyEl.un('click', this.beforeSelectFile, this);
29906 this.notifyEl.hide();
29907 this.thumbEl.show();
29908 this.footerEl.show();
29910 this.baseRotateLevel();
29912 if(this.isDocument){
29913 this.setThumbBoxSize();
29916 this.setThumbBoxPosition();
29918 this.baseScaleLevel();
29924 this.canvasLoaded = true;
29927 this.maskEl.unmask();
29932 setCanvasPosition : function()
29934 if(!this.canvasEl){
29938 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29939 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29941 this.previewEl.setLeft(pw);
29942 this.previewEl.setTop(ph);
29946 onMouseDown : function(e)
29950 this.dragable = true;
29951 this.pinching = false;
29953 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29954 this.dragable = false;
29958 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29959 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29963 onMouseMove : function(e)
29967 if(!this.canvasLoaded){
29971 if (!this.dragable){
29975 var minX = Math.ceil(this.thumbEl.getLeft(true));
29976 var minY = Math.ceil(this.thumbEl.getTop(true));
29978 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29979 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29981 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29982 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29984 x = x - this.mouseX;
29985 y = y - this.mouseY;
29987 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29988 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29990 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29991 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29993 this.previewEl.setLeft(bgX);
29994 this.previewEl.setTop(bgY);
29996 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29997 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30000 onMouseUp : function(e)
30004 this.dragable = false;
30007 onMouseWheel : function(e)
30011 this.startScale = this.scale;
30013 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30015 if(!this.zoomable()){
30016 this.scale = this.startScale;
30025 zoomable : function()
30027 var minScale = this.thumbEl.getWidth() / this.minWidth;
30029 if(this.minWidth < this.minHeight){
30030 minScale = this.thumbEl.getHeight() / this.minHeight;
30033 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30034 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30038 (this.rotate == 0 || this.rotate == 180) &&
30040 width > this.imageEl.OriginWidth ||
30041 height > this.imageEl.OriginHeight ||
30042 (width < this.minWidth && height < this.minHeight)
30050 (this.rotate == 90 || this.rotate == 270) &&
30052 width > this.imageEl.OriginWidth ||
30053 height > this.imageEl.OriginHeight ||
30054 (width < this.minHeight && height < this.minWidth)
30061 !this.isDocument &&
30062 (this.rotate == 0 || this.rotate == 180) &&
30064 width < this.minWidth ||
30065 width > this.imageEl.OriginWidth ||
30066 height < this.minHeight ||
30067 height > this.imageEl.OriginHeight
30074 !this.isDocument &&
30075 (this.rotate == 90 || this.rotate == 270) &&
30077 width < this.minHeight ||
30078 width > this.imageEl.OriginWidth ||
30079 height < this.minWidth ||
30080 height > this.imageEl.OriginHeight
30090 onRotateLeft : function(e)
30092 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30094 var minScale = this.thumbEl.getWidth() / this.minWidth;
30096 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30097 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30099 this.startScale = this.scale;
30101 while (this.getScaleLevel() < minScale){
30103 this.scale = this.scale + 1;
30105 if(!this.zoomable()){
30110 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30111 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30116 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30123 this.scale = this.startScale;
30125 this.onRotateFail();
30130 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30132 if(this.isDocument){
30133 this.setThumbBoxSize();
30134 this.setThumbBoxPosition();
30135 this.setCanvasPosition();
30140 this.fireEvent('rotate', this, 'left');
30144 onRotateRight : function(e)
30146 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30148 var minScale = this.thumbEl.getWidth() / this.minWidth;
30150 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30151 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30153 this.startScale = this.scale;
30155 while (this.getScaleLevel() < minScale){
30157 this.scale = this.scale + 1;
30159 if(!this.zoomable()){
30164 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30165 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30170 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30177 this.scale = this.startScale;
30179 this.onRotateFail();
30184 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30186 if(this.isDocument){
30187 this.setThumbBoxSize();
30188 this.setThumbBoxPosition();
30189 this.setCanvasPosition();
30194 this.fireEvent('rotate', this, 'right');
30197 onRotateFail : function()
30199 this.errorEl.show(true);
30203 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30208 this.previewEl.dom.innerHTML = '';
30210 var canvasEl = document.createElement("canvas");
30212 var contextEl = canvasEl.getContext("2d");
30214 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30215 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30216 var center = this.imageEl.OriginWidth / 2;
30218 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30219 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30220 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30221 center = this.imageEl.OriginHeight / 2;
30224 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30226 contextEl.translate(center, center);
30227 contextEl.rotate(this.rotate * Math.PI / 180);
30229 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30231 this.canvasEl = document.createElement("canvas");
30233 this.contextEl = this.canvasEl.getContext("2d");
30235 switch (this.rotate) {
30238 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30239 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30241 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30246 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30247 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30249 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30250 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);
30254 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30259 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30260 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30262 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30263 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);
30267 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);
30272 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30273 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30275 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30276 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30280 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);
30287 this.previewEl.appendChild(this.canvasEl);
30289 this.setCanvasPosition();
30294 if(!this.canvasLoaded){
30298 var imageCanvas = document.createElement("canvas");
30300 var imageContext = imageCanvas.getContext("2d");
30302 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30303 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30305 var center = imageCanvas.width / 2;
30307 imageContext.translate(center, center);
30309 imageContext.rotate(this.rotate * Math.PI / 180);
30311 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30313 var canvas = document.createElement("canvas");
30315 var context = canvas.getContext("2d");
30317 canvas.width = this.minWidth;
30318 canvas.height = this.minHeight;
30320 switch (this.rotate) {
30323 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30324 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30326 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30327 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30329 var targetWidth = this.minWidth - 2 * x;
30330 var targetHeight = this.minHeight - 2 * y;
30334 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30335 scale = targetWidth / width;
30338 if(x > 0 && y == 0){
30339 scale = targetHeight / height;
30342 if(x > 0 && y > 0){
30343 scale = targetWidth / width;
30345 if(width < height){
30346 scale = targetHeight / height;
30350 context.scale(scale, scale);
30352 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30353 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30355 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30356 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30358 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30363 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30364 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30366 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30367 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30369 var targetWidth = this.minWidth - 2 * x;
30370 var targetHeight = this.minHeight - 2 * y;
30374 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30375 scale = targetWidth / width;
30378 if(x > 0 && y == 0){
30379 scale = targetHeight / height;
30382 if(x > 0 && y > 0){
30383 scale = targetWidth / width;
30385 if(width < height){
30386 scale = targetHeight / height;
30390 context.scale(scale, scale);
30392 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30393 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30395 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30396 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30398 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30400 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30405 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30406 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30408 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30409 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30411 var targetWidth = this.minWidth - 2 * x;
30412 var targetHeight = this.minHeight - 2 * y;
30416 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30417 scale = targetWidth / width;
30420 if(x > 0 && y == 0){
30421 scale = targetHeight / height;
30424 if(x > 0 && y > 0){
30425 scale = targetWidth / width;
30427 if(width < height){
30428 scale = targetHeight / height;
30432 context.scale(scale, scale);
30434 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30435 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30437 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30438 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30440 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30441 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30443 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30448 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30449 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30451 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30452 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30454 var targetWidth = this.minWidth - 2 * x;
30455 var targetHeight = this.minHeight - 2 * y;
30459 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30460 scale = targetWidth / width;
30463 if(x > 0 && y == 0){
30464 scale = targetHeight / height;
30467 if(x > 0 && y > 0){
30468 scale = targetWidth / width;
30470 if(width < height){
30471 scale = targetHeight / height;
30475 context.scale(scale, scale);
30477 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30478 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30480 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30481 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30483 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30485 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30492 this.cropData = canvas.toDataURL(this.cropType);
30494 if(this.fireEvent('crop', this, this.cropData) !== false){
30495 this.process(this.file, this.cropData);
30502 setThumbBoxSize : function()
30506 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30507 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30508 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30510 this.minWidth = width;
30511 this.minHeight = height;
30513 if(this.rotate == 90 || this.rotate == 270){
30514 this.minWidth = height;
30515 this.minHeight = width;
30520 width = Math.ceil(this.minWidth * height / this.minHeight);
30522 if(this.minWidth > this.minHeight){
30524 height = Math.ceil(this.minHeight * width / this.minWidth);
30527 this.thumbEl.setStyle({
30528 width : width + 'px',
30529 height : height + 'px'
30536 setThumbBoxPosition : function()
30538 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30539 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30541 this.thumbEl.setLeft(x);
30542 this.thumbEl.setTop(y);
30546 baseRotateLevel : function()
30548 this.baseRotate = 1;
30551 typeof(this.exif) != 'undefined' &&
30552 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30553 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30555 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30558 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30562 baseScaleLevel : function()
30566 if(this.isDocument){
30568 if(this.baseRotate == 6 || this.baseRotate == 8){
30570 height = this.thumbEl.getHeight();
30571 this.baseScale = height / this.imageEl.OriginWidth;
30573 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30574 width = this.thumbEl.getWidth();
30575 this.baseScale = width / this.imageEl.OriginHeight;
30581 height = this.thumbEl.getHeight();
30582 this.baseScale = height / this.imageEl.OriginHeight;
30584 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30585 width = this.thumbEl.getWidth();
30586 this.baseScale = width / this.imageEl.OriginWidth;
30592 if(this.baseRotate == 6 || this.baseRotate == 8){
30594 width = this.thumbEl.getHeight();
30595 this.baseScale = width / this.imageEl.OriginHeight;
30597 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30598 height = this.thumbEl.getWidth();
30599 this.baseScale = height / this.imageEl.OriginHeight;
30602 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30603 height = this.thumbEl.getWidth();
30604 this.baseScale = height / this.imageEl.OriginHeight;
30606 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30607 width = this.thumbEl.getHeight();
30608 this.baseScale = width / this.imageEl.OriginWidth;
30615 width = this.thumbEl.getWidth();
30616 this.baseScale = width / this.imageEl.OriginWidth;
30618 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30619 height = this.thumbEl.getHeight();
30620 this.baseScale = height / this.imageEl.OriginHeight;
30623 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30625 height = this.thumbEl.getHeight();
30626 this.baseScale = height / this.imageEl.OriginHeight;
30628 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30629 width = this.thumbEl.getWidth();
30630 this.baseScale = width / this.imageEl.OriginWidth;
30638 getScaleLevel : function()
30640 return this.baseScale * Math.pow(1.1, this.scale);
30643 onTouchStart : function(e)
30645 if(!this.canvasLoaded){
30646 this.beforeSelectFile(e);
30650 var touches = e.browserEvent.touches;
30656 if(touches.length == 1){
30657 this.onMouseDown(e);
30661 if(touches.length != 2){
30667 for(var i = 0, finger; finger = touches[i]; i++){
30668 coords.push(finger.pageX, finger.pageY);
30671 var x = Math.pow(coords[0] - coords[2], 2);
30672 var y = Math.pow(coords[1] - coords[3], 2);
30674 this.startDistance = Math.sqrt(x + y);
30676 this.startScale = this.scale;
30678 this.pinching = true;
30679 this.dragable = false;
30683 onTouchMove : function(e)
30685 if(!this.pinching && !this.dragable){
30689 var touches = e.browserEvent.touches;
30696 this.onMouseMove(e);
30702 for(var i = 0, finger; finger = touches[i]; i++){
30703 coords.push(finger.pageX, finger.pageY);
30706 var x = Math.pow(coords[0] - coords[2], 2);
30707 var y = Math.pow(coords[1] - coords[3], 2);
30709 this.endDistance = Math.sqrt(x + y);
30711 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30713 if(!this.zoomable()){
30714 this.scale = this.startScale;
30722 onTouchEnd : function(e)
30724 this.pinching = false;
30725 this.dragable = false;
30729 process : function(file, crop)
30732 this.maskEl.mask(this.loadingText);
30735 this.xhr = new XMLHttpRequest();
30737 file.xhr = this.xhr;
30739 this.xhr.open(this.method, this.url, true);
30742 "Accept": "application/json",
30743 "Cache-Control": "no-cache",
30744 "X-Requested-With": "XMLHttpRequest"
30747 for (var headerName in headers) {
30748 var headerValue = headers[headerName];
30750 this.xhr.setRequestHeader(headerName, headerValue);
30756 this.xhr.onload = function()
30758 _this.xhrOnLoad(_this.xhr);
30761 this.xhr.onerror = function()
30763 _this.xhrOnError(_this.xhr);
30766 var formData = new FormData();
30768 formData.append('returnHTML', 'NO');
30771 formData.append('crop', crop);
30774 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30775 formData.append(this.paramName, file, file.name);
30778 if(typeof(file.filename) != 'undefined'){
30779 formData.append('filename', file.filename);
30782 if(typeof(file.mimetype) != 'undefined'){
30783 formData.append('mimetype', file.mimetype);
30786 if(this.fireEvent('arrange', this, formData) != false){
30787 this.xhr.send(formData);
30791 xhrOnLoad : function(xhr)
30794 this.maskEl.unmask();
30797 if (xhr.readyState !== 4) {
30798 this.fireEvent('exception', this, xhr);
30802 var response = Roo.decode(xhr.responseText);
30804 if(!response.success){
30805 this.fireEvent('exception', this, xhr);
30809 var response = Roo.decode(xhr.responseText);
30811 this.fireEvent('upload', this, response);
30815 xhrOnError : function()
30818 this.maskEl.unmask();
30821 Roo.log('xhr on error');
30823 var response = Roo.decode(xhr.responseText);
30829 prepare : function(file)
30832 this.maskEl.mask(this.loadingText);
30838 if(typeof(file) === 'string'){
30839 this.loadCanvas(file);
30843 if(!file || !this.urlAPI){
30848 this.cropType = file.type;
30852 if(this.fireEvent('prepare', this, this.file) != false){
30854 var reader = new FileReader();
30856 reader.onload = function (e) {
30857 if (e.target.error) {
30858 Roo.log(e.target.error);
30862 var buffer = e.target.result,
30863 dataView = new DataView(buffer),
30865 maxOffset = dataView.byteLength - 4,
30869 if (dataView.getUint16(0) === 0xffd8) {
30870 while (offset < maxOffset) {
30871 markerBytes = dataView.getUint16(offset);
30873 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30874 markerLength = dataView.getUint16(offset + 2) + 2;
30875 if (offset + markerLength > dataView.byteLength) {
30876 Roo.log('Invalid meta data: Invalid segment size.');
30880 if(markerBytes == 0xffe1){
30881 _this.parseExifData(
30888 offset += markerLength;
30898 var url = _this.urlAPI.createObjectURL(_this.file);
30900 _this.loadCanvas(url);
30905 reader.readAsArrayBuffer(this.file);
30911 parseExifData : function(dataView, offset, length)
30913 var tiffOffset = offset + 10,
30917 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30918 // No Exif data, might be XMP data instead
30922 // Check for the ASCII code for "Exif" (0x45786966):
30923 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30924 // No Exif data, might be XMP data instead
30927 if (tiffOffset + 8 > dataView.byteLength) {
30928 Roo.log('Invalid Exif data: Invalid segment size.');
30931 // Check for the two null bytes:
30932 if (dataView.getUint16(offset + 8) !== 0x0000) {
30933 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30936 // Check the byte alignment:
30937 switch (dataView.getUint16(tiffOffset)) {
30939 littleEndian = true;
30942 littleEndian = false;
30945 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30948 // Check for the TIFF tag marker (0x002A):
30949 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30950 Roo.log('Invalid Exif data: Missing TIFF marker.');
30953 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30954 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30956 this.parseExifTags(
30959 tiffOffset + dirOffset,
30964 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30969 if (dirOffset + 6 > dataView.byteLength) {
30970 Roo.log('Invalid Exif data: Invalid directory offset.');
30973 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30974 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30975 if (dirEndOffset + 4 > dataView.byteLength) {
30976 Roo.log('Invalid Exif data: Invalid directory size.');
30979 for (i = 0; i < tagsNumber; i += 1) {
30983 dirOffset + 2 + 12 * i, // tag offset
30987 // Return the offset to the next directory:
30988 return dataView.getUint32(dirEndOffset, littleEndian);
30991 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30993 var tag = dataView.getUint16(offset, littleEndian);
30995 this.exif[tag] = this.getExifValue(
30999 dataView.getUint16(offset + 2, littleEndian), // tag type
31000 dataView.getUint32(offset + 4, littleEndian), // tag length
31005 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31007 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31016 Roo.log('Invalid Exif data: Invalid tag type.');
31020 tagSize = tagType.size * length;
31021 // Determine if the value is contained in the dataOffset bytes,
31022 // or if the value at the dataOffset is a pointer to the actual data:
31023 dataOffset = tagSize > 4 ?
31024 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31025 if (dataOffset + tagSize > dataView.byteLength) {
31026 Roo.log('Invalid Exif data: Invalid data offset.');
31029 if (length === 1) {
31030 return tagType.getValue(dataView, dataOffset, littleEndian);
31033 for (i = 0; i < length; i += 1) {
31034 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31037 if (tagType.ascii) {
31039 // Concatenate the chars:
31040 for (i = 0; i < values.length; i += 1) {
31042 // Ignore the terminating NULL byte(s):
31043 if (c === '\u0000') {
31055 Roo.apply(Roo.bootstrap.UploadCropbox, {
31057 'Orientation': 0x0112
31061 1: 0, //'top-left',
31063 3: 180, //'bottom-right',
31064 // 4: 'bottom-left',
31066 6: 90, //'right-top',
31067 // 7: 'right-bottom',
31068 8: 270 //'left-bottom'
31072 // byte, 8-bit unsigned int:
31074 getValue: function (dataView, dataOffset) {
31075 return dataView.getUint8(dataOffset);
31079 // ascii, 8-bit byte:
31081 getValue: function (dataView, dataOffset) {
31082 return String.fromCharCode(dataView.getUint8(dataOffset));
31087 // short, 16 bit int:
31089 getValue: function (dataView, dataOffset, littleEndian) {
31090 return dataView.getUint16(dataOffset, littleEndian);
31094 // long, 32 bit int:
31096 getValue: function (dataView, dataOffset, littleEndian) {
31097 return dataView.getUint32(dataOffset, littleEndian);
31101 // rational = two long values, first is numerator, second is denominator:
31103 getValue: function (dataView, dataOffset, littleEndian) {
31104 return dataView.getUint32(dataOffset, littleEndian) /
31105 dataView.getUint32(dataOffset + 4, littleEndian);
31109 // slong, 32 bit signed int:
31111 getValue: function (dataView, dataOffset, littleEndian) {
31112 return dataView.getInt32(dataOffset, littleEndian);
31116 // srational, two slongs, first is numerator, second is denominator:
31118 getValue: function (dataView, dataOffset, littleEndian) {
31119 return dataView.getInt32(dataOffset, littleEndian) /
31120 dataView.getInt32(dataOffset + 4, littleEndian);
31130 cls : 'btn-group roo-upload-cropbox-rotate-left',
31131 action : 'rotate-left',
31135 cls : 'btn btn-default',
31136 html : '<i class="fa fa-undo"></i>'
31142 cls : 'btn-group roo-upload-cropbox-picture',
31143 action : 'picture',
31147 cls : 'btn btn-default',
31148 html : '<i class="fa fa-picture-o"></i>'
31154 cls : 'btn-group roo-upload-cropbox-rotate-right',
31155 action : 'rotate-right',
31159 cls : 'btn btn-default',
31160 html : '<i class="fa fa-repeat"></i>'
31168 cls : 'btn-group roo-upload-cropbox-rotate-left',
31169 action : 'rotate-left',
31173 cls : 'btn btn-default',
31174 html : '<i class="fa fa-undo"></i>'
31180 cls : 'btn-group roo-upload-cropbox-download',
31181 action : 'download',
31185 cls : 'btn btn-default',
31186 html : '<i class="fa fa-download"></i>'
31192 cls : 'btn-group roo-upload-cropbox-crop',
31197 cls : 'btn btn-default',
31198 html : '<i class="fa fa-crop"></i>'
31204 cls : 'btn-group roo-upload-cropbox-trash',
31209 cls : 'btn btn-default',
31210 html : '<i class="fa fa-trash"></i>'
31216 cls : 'btn-group roo-upload-cropbox-rotate-right',
31217 action : 'rotate-right',
31221 cls : 'btn btn-default',
31222 html : '<i class="fa fa-repeat"></i>'
31230 cls : 'btn-group roo-upload-cropbox-rotate-left',
31231 action : 'rotate-left',
31235 cls : 'btn btn-default',
31236 html : '<i class="fa fa-undo"></i>'
31242 cls : 'btn-group roo-upload-cropbox-rotate-right',
31243 action : 'rotate-right',
31247 cls : 'btn btn-default',
31248 html : '<i class="fa fa-repeat"></i>'
31261 * @class Roo.bootstrap.DocumentManager
31262 * @extends Roo.bootstrap.Component
31263 * Bootstrap DocumentManager class
31264 * @cfg {String} paramName default 'imageUpload'
31265 * @cfg {String} toolTipName default 'filename'
31266 * @cfg {String} method default POST
31267 * @cfg {String} url action url
31268 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31269 * @cfg {Boolean} multiple multiple upload default true
31270 * @cfg {Number} thumbSize default 300
31271 * @cfg {String} fieldLabel
31272 * @cfg {Number} labelWidth default 4
31273 * @cfg {String} labelAlign (left|top) default left
31274 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31275 * @cfg {Number} labellg set the width of label (1-12)
31276 * @cfg {Number} labelmd set the width of label (1-12)
31277 * @cfg {Number} labelsm set the width of label (1-12)
31278 * @cfg {Number} labelxs set the width of label (1-12)
31281 * Create a new DocumentManager
31282 * @param {Object} config The config object
31285 Roo.bootstrap.DocumentManager = function(config){
31286 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31289 this.delegates = [];
31294 * Fire when initial the DocumentManager
31295 * @param {Roo.bootstrap.DocumentManager} this
31300 * inspect selected file
31301 * @param {Roo.bootstrap.DocumentManager} this
31302 * @param {File} file
31307 * Fire when xhr load exception
31308 * @param {Roo.bootstrap.DocumentManager} this
31309 * @param {XMLHttpRequest} xhr
31311 "exception" : true,
31313 * @event afterupload
31314 * Fire when xhr load exception
31315 * @param {Roo.bootstrap.DocumentManager} this
31316 * @param {XMLHttpRequest} xhr
31318 "afterupload" : true,
31321 * prepare the form data
31322 * @param {Roo.bootstrap.DocumentManager} this
31323 * @param {Object} formData
31328 * Fire when remove the file
31329 * @param {Roo.bootstrap.DocumentManager} this
31330 * @param {Object} file
31335 * Fire after refresh the file
31336 * @param {Roo.bootstrap.DocumentManager} this
31341 * Fire after click the image
31342 * @param {Roo.bootstrap.DocumentManager} this
31343 * @param {Object} file
31348 * Fire when upload a image and editable set to true
31349 * @param {Roo.bootstrap.DocumentManager} this
31350 * @param {Object} file
31354 * @event beforeselectfile
31355 * Fire before select file
31356 * @param {Roo.bootstrap.DocumentManager} this
31358 "beforeselectfile" : true,
31361 * Fire before process file
31362 * @param {Roo.bootstrap.DocumentManager} this
31363 * @param {Object} file
31367 * @event previewrendered
31368 * Fire when preview rendered
31369 * @param {Roo.bootstrap.DocumentManager} this
31370 * @param {Object} file
31372 "previewrendered" : true,
31375 "previewResize" : true
31380 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31389 paramName : 'imageUpload',
31390 toolTipName : 'filename',
31393 labelAlign : 'left',
31403 getAutoCreate : function()
31405 var managerWidget = {
31407 cls : 'roo-document-manager',
31411 cls : 'roo-document-manager-selector',
31416 cls : 'roo-document-manager-uploader',
31420 cls : 'roo-document-manager-upload-btn',
31421 html : '<i class="fa fa-plus"></i>'
31432 cls : 'column col-md-12',
31437 if(this.fieldLabel.length){
31442 cls : 'column col-md-12',
31443 html : this.fieldLabel
31447 cls : 'column col-md-12',
31452 if(this.labelAlign == 'left'){
31457 html : this.fieldLabel
31466 if(this.labelWidth > 12){
31467 content[0].style = "width: " + this.labelWidth + 'px';
31470 if(this.labelWidth < 13 && this.labelmd == 0){
31471 this.labelmd = this.labelWidth;
31474 if(this.labellg > 0){
31475 content[0].cls += ' col-lg-' + this.labellg;
31476 content[1].cls += ' col-lg-' + (12 - this.labellg);
31479 if(this.labelmd > 0){
31480 content[0].cls += ' col-md-' + this.labelmd;
31481 content[1].cls += ' col-md-' + (12 - this.labelmd);
31484 if(this.labelsm > 0){
31485 content[0].cls += ' col-sm-' + this.labelsm;
31486 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31489 if(this.labelxs > 0){
31490 content[0].cls += ' col-xs-' + this.labelxs;
31491 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31499 cls : 'row clearfix',
31507 initEvents : function()
31509 this.managerEl = this.el.select('.roo-document-manager', true).first();
31510 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31512 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31513 this.selectorEl.hide();
31516 this.selectorEl.attr('multiple', 'multiple');
31519 this.selectorEl.on('change', this.onFileSelected, this);
31521 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31522 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31524 this.uploader.on('click', this.onUploaderClick, this);
31526 this.renderProgressDialog();
31530 window.addEventListener("resize", function() { _this.refresh(); } );
31532 this.fireEvent('initial', this);
31535 renderProgressDialog : function()
31539 this.progressDialog = new Roo.bootstrap.Modal({
31540 cls : 'roo-document-manager-progress-dialog',
31541 allow_close : false,
31552 btnclick : function() {
31553 _this.uploadCancel();
31559 this.progressDialog.render(Roo.get(document.body));
31561 this.progress = new Roo.bootstrap.Progress({
31562 cls : 'roo-document-manager-progress',
31567 this.progress.render(this.progressDialog.getChildContainer());
31569 this.progressBar = new Roo.bootstrap.ProgressBar({
31570 cls : 'roo-document-manager-progress-bar',
31573 aria_valuemax : 12,
31577 this.progressBar.render(this.progress.getChildContainer());
31580 onUploaderClick : function(e)
31582 e.preventDefault();
31584 if(this.fireEvent('beforeselectfile', this) != false){
31585 this.selectorEl.dom.click();
31590 onFileSelected : function(e)
31592 e.preventDefault();
31594 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31598 Roo.each(this.selectorEl.dom.files, function(file){
31599 if(this.fireEvent('inspect', this, file) != false){
31600 this.files.push(file);
31610 this.selectorEl.dom.value = '';
31612 if(!this.files || !this.files.length){
31616 if(this.boxes > 0 && this.files.length > this.boxes){
31617 this.files = this.files.slice(0, this.boxes);
31620 this.uploader.show();
31622 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31623 this.uploader.hide();
31632 Roo.each(this.files, function(file){
31634 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31635 var f = this.renderPreview(file);
31640 if(file.type.indexOf('image') != -1){
31641 this.delegates.push(
31643 _this.process(file);
31644 }).createDelegate(this)
31652 _this.process(file);
31653 }).createDelegate(this)
31658 this.files = files;
31660 this.delegates = this.delegates.concat(docs);
31662 if(!this.delegates.length){
31667 this.progressBar.aria_valuemax = this.delegates.length;
31674 arrange : function()
31676 if(!this.delegates.length){
31677 this.progressDialog.hide();
31682 var delegate = this.delegates.shift();
31684 this.progressDialog.show();
31686 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31688 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31693 refresh : function()
31695 this.uploader.show();
31697 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31698 this.uploader.hide();
31701 Roo.isTouch ? this.closable(false) : this.closable(true);
31703 this.fireEvent('refresh', this);
31706 onRemove : function(e, el, o)
31708 e.preventDefault();
31710 this.fireEvent('remove', this, o);
31714 remove : function(o)
31718 Roo.each(this.files, function(file){
31719 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31728 this.files = files;
31735 Roo.each(this.files, function(file){
31740 file.target.remove();
31749 onClick : function(e, el, o)
31751 e.preventDefault();
31753 this.fireEvent('click', this, o);
31757 closable : function(closable)
31759 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31761 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31773 xhrOnLoad : function(xhr)
31775 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31779 if (xhr.readyState !== 4) {
31781 this.fireEvent('exception', this, xhr);
31785 var response = Roo.decode(xhr.responseText);
31787 if(!response.success){
31789 this.fireEvent('exception', this, xhr);
31793 var file = this.renderPreview(response.data);
31795 this.files.push(file);
31799 this.fireEvent('afterupload', this, xhr);
31803 xhrOnError : function(xhr)
31805 Roo.log('xhr on error');
31807 var response = Roo.decode(xhr.responseText);
31814 process : function(file)
31816 if(this.fireEvent('process', this, file) !== false){
31817 if(this.editable && file.type.indexOf('image') != -1){
31818 this.fireEvent('edit', this, file);
31822 this.uploadStart(file, false);
31829 uploadStart : function(file, crop)
31831 this.xhr = new XMLHttpRequest();
31833 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31838 file.xhr = this.xhr;
31840 this.managerEl.createChild({
31842 cls : 'roo-document-manager-loading',
31846 tooltip : file.name,
31847 cls : 'roo-document-manager-thumb',
31848 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31854 this.xhr.open(this.method, this.url, true);
31857 "Accept": "application/json",
31858 "Cache-Control": "no-cache",
31859 "X-Requested-With": "XMLHttpRequest"
31862 for (var headerName in headers) {
31863 var headerValue = headers[headerName];
31865 this.xhr.setRequestHeader(headerName, headerValue);
31871 this.xhr.onload = function()
31873 _this.xhrOnLoad(_this.xhr);
31876 this.xhr.onerror = function()
31878 _this.xhrOnError(_this.xhr);
31881 var formData = new FormData();
31883 formData.append('returnHTML', 'NO');
31886 formData.append('crop', crop);
31889 formData.append(this.paramName, file, file.name);
31896 if(this.fireEvent('prepare', this, formData, options) != false){
31898 if(options.manually){
31902 this.xhr.send(formData);
31906 this.uploadCancel();
31909 uploadCancel : function()
31915 this.delegates = [];
31917 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31924 renderPreview : function(file)
31926 if(typeof(file.target) != 'undefined' && file.target){
31930 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31932 var previewEl = this.managerEl.createChild({
31934 cls : 'roo-document-manager-preview',
31938 tooltip : file[this.toolTipName],
31939 cls : 'roo-document-manager-thumb',
31940 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31945 html : '<i class="fa fa-times-circle"></i>'
31950 var close = previewEl.select('button.close', true).first();
31952 close.on('click', this.onRemove, this, file);
31954 file.target = previewEl;
31956 var image = previewEl.select('img', true).first();
31960 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31962 image.on('click', this.onClick, this, file);
31964 this.fireEvent('previewrendered', this, file);
31970 onPreviewLoad : function(file, image)
31972 if(typeof(file.target) == 'undefined' || !file.target){
31976 var width = image.dom.naturalWidth || image.dom.width;
31977 var height = image.dom.naturalHeight || image.dom.height;
31979 if(!this.previewResize) {
31983 if(width > height){
31984 file.target.addClass('wide');
31988 file.target.addClass('tall');
31993 uploadFromSource : function(file, crop)
31995 this.xhr = new XMLHttpRequest();
31997 this.managerEl.createChild({
31999 cls : 'roo-document-manager-loading',
32003 tooltip : file.name,
32004 cls : 'roo-document-manager-thumb',
32005 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32011 this.xhr.open(this.method, this.url, true);
32014 "Accept": "application/json",
32015 "Cache-Control": "no-cache",
32016 "X-Requested-With": "XMLHttpRequest"
32019 for (var headerName in headers) {
32020 var headerValue = headers[headerName];
32022 this.xhr.setRequestHeader(headerName, headerValue);
32028 this.xhr.onload = function()
32030 _this.xhrOnLoad(_this.xhr);
32033 this.xhr.onerror = function()
32035 _this.xhrOnError(_this.xhr);
32038 var formData = new FormData();
32040 formData.append('returnHTML', 'NO');
32042 formData.append('crop', crop);
32044 if(typeof(file.filename) != 'undefined'){
32045 formData.append('filename', file.filename);
32048 if(typeof(file.mimetype) != 'undefined'){
32049 formData.append('mimetype', file.mimetype);
32054 if(this.fireEvent('prepare', this, formData) != false){
32055 this.xhr.send(formData);
32065 * @class Roo.bootstrap.DocumentViewer
32066 * @extends Roo.bootstrap.Component
32067 * Bootstrap DocumentViewer class
32068 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32069 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32072 * Create a new DocumentViewer
32073 * @param {Object} config The config object
32076 Roo.bootstrap.DocumentViewer = function(config){
32077 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32082 * Fire after initEvent
32083 * @param {Roo.bootstrap.DocumentViewer} this
32089 * @param {Roo.bootstrap.DocumentViewer} this
32094 * Fire after download button
32095 * @param {Roo.bootstrap.DocumentViewer} this
32100 * Fire after trash button
32101 * @param {Roo.bootstrap.DocumentViewer} this
32108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32110 showDownload : true,
32114 getAutoCreate : function()
32118 cls : 'roo-document-viewer',
32122 cls : 'roo-document-viewer-body',
32126 cls : 'roo-document-viewer-thumb',
32130 cls : 'roo-document-viewer-image'
32138 cls : 'roo-document-viewer-footer',
32141 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32145 cls : 'btn-group roo-document-viewer-download',
32149 cls : 'btn btn-default',
32150 html : '<i class="fa fa-download"></i>'
32156 cls : 'btn-group roo-document-viewer-trash',
32160 cls : 'btn btn-default',
32161 html : '<i class="fa fa-trash"></i>'
32174 initEvents : function()
32176 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32177 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32179 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32180 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32182 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32183 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32185 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32186 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32188 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32189 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32191 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32192 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32194 this.bodyEl.on('click', this.onClick, this);
32195 this.downloadBtn.on('click', this.onDownload, this);
32196 this.trashBtn.on('click', this.onTrash, this);
32198 this.downloadBtn.hide();
32199 this.trashBtn.hide();
32201 if(this.showDownload){
32202 this.downloadBtn.show();
32205 if(this.showTrash){
32206 this.trashBtn.show();
32209 if(!this.showDownload && !this.showTrash) {
32210 this.footerEl.hide();
32215 initial : function()
32217 this.fireEvent('initial', this);
32221 onClick : function(e)
32223 e.preventDefault();
32225 this.fireEvent('click', this);
32228 onDownload : function(e)
32230 e.preventDefault();
32232 this.fireEvent('download', this);
32235 onTrash : function(e)
32237 e.preventDefault();
32239 this.fireEvent('trash', this);
32251 * @class Roo.bootstrap.NavProgressBar
32252 * @extends Roo.bootstrap.Component
32253 * Bootstrap NavProgressBar class
32256 * Create a new nav progress bar
32257 * @param {Object} config The config object
32260 Roo.bootstrap.NavProgressBar = function(config){
32261 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32263 this.bullets = this.bullets || [];
32265 // Roo.bootstrap.NavProgressBar.register(this);
32269 * Fires when the active item changes
32270 * @param {Roo.bootstrap.NavProgressBar} this
32271 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32272 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32279 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32284 getAutoCreate : function()
32286 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32290 cls : 'roo-navigation-bar-group',
32294 cls : 'roo-navigation-top-bar'
32298 cls : 'roo-navigation-bullets-bar',
32302 cls : 'roo-navigation-bar'
32309 cls : 'roo-navigation-bottom-bar'
32319 initEvents: function()
32324 onRender : function(ct, position)
32326 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32328 if(this.bullets.length){
32329 Roo.each(this.bullets, function(b){
32338 addItem : function(cfg)
32340 var item = new Roo.bootstrap.NavProgressItem(cfg);
32342 item.parentId = this.id;
32343 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32346 var top = new Roo.bootstrap.Element({
32348 cls : 'roo-navigation-bar-text'
32351 var bottom = new Roo.bootstrap.Element({
32353 cls : 'roo-navigation-bar-text'
32356 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32357 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32359 var topText = new Roo.bootstrap.Element({
32361 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32364 var bottomText = new Roo.bootstrap.Element({
32366 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32369 topText.onRender(top.el, null);
32370 bottomText.onRender(bottom.el, null);
32373 item.bottomEl = bottom;
32376 this.barItems.push(item);
32381 getActive : function()
32383 var active = false;
32385 Roo.each(this.barItems, function(v){
32387 if (!v.isActive()) {
32399 setActiveItem : function(item)
32403 Roo.each(this.barItems, function(v){
32404 if (v.rid == item.rid) {
32408 if (v.isActive()) {
32409 v.setActive(false);
32414 item.setActive(true);
32416 this.fireEvent('changed', this, item, prev);
32419 getBarItem: function(rid)
32423 Roo.each(this.barItems, function(e) {
32424 if (e.rid != rid) {
32435 indexOfItem : function(item)
32439 Roo.each(this.barItems, function(v, i){
32441 if (v.rid != item.rid) {
32452 setActiveNext : function()
32454 var i = this.indexOfItem(this.getActive());
32456 if (i > this.barItems.length) {
32460 this.setActiveItem(this.barItems[i+1]);
32463 setActivePrev : function()
32465 var i = this.indexOfItem(this.getActive());
32471 this.setActiveItem(this.barItems[i-1]);
32474 format : function()
32476 if(!this.barItems.length){
32480 var width = 100 / this.barItems.length;
32482 Roo.each(this.barItems, function(i){
32483 i.el.setStyle('width', width + '%');
32484 i.topEl.el.setStyle('width', width + '%');
32485 i.bottomEl.el.setStyle('width', width + '%');
32494 * Nav Progress Item
32499 * @class Roo.bootstrap.NavProgressItem
32500 * @extends Roo.bootstrap.Component
32501 * Bootstrap NavProgressItem class
32502 * @cfg {String} rid the reference id
32503 * @cfg {Boolean} active (true|false) Is item active default false
32504 * @cfg {Boolean} disabled (true|false) Is item active default false
32505 * @cfg {String} html
32506 * @cfg {String} position (top|bottom) text position default bottom
32507 * @cfg {String} icon show icon instead of number
32510 * Create a new NavProgressItem
32511 * @param {Object} config The config object
32513 Roo.bootstrap.NavProgressItem = function(config){
32514 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32519 * The raw click event for the entire grid.
32520 * @param {Roo.bootstrap.NavProgressItem} this
32521 * @param {Roo.EventObject} e
32528 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32534 position : 'bottom',
32537 getAutoCreate : function()
32539 var iconCls = 'roo-navigation-bar-item-icon';
32541 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32545 cls: 'roo-navigation-bar-item',
32555 cfg.cls += ' active';
32558 cfg.cls += ' disabled';
32564 disable : function()
32566 this.setDisabled(true);
32569 enable : function()
32571 this.setDisabled(false);
32574 initEvents: function()
32576 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32578 this.iconEl.on('click', this.onClick, this);
32581 onClick : function(e)
32583 e.preventDefault();
32589 if(this.fireEvent('click', this, e) === false){
32593 this.parent().setActiveItem(this);
32596 isActive: function ()
32598 return this.active;
32601 setActive : function(state)
32603 if(this.active == state){
32607 this.active = state;
32610 this.el.addClass('active');
32614 this.el.removeClass('active');
32619 setDisabled : function(state)
32621 if(this.disabled == state){
32625 this.disabled = state;
32628 this.el.addClass('disabled');
32632 this.el.removeClass('disabled');
32635 tooltipEl : function()
32637 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32650 * @class Roo.bootstrap.FieldLabel
32651 * @extends Roo.bootstrap.Component
32652 * Bootstrap FieldLabel class
32653 * @cfg {String} html contents of the element
32654 * @cfg {String} tag tag of the element default label
32655 * @cfg {String} cls class of the element
32656 * @cfg {String} target label target
32657 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32658 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32659 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32660 * @cfg {String} iconTooltip default "This field is required"
32661 * @cfg {String} indicatorpos (left|right) default left
32664 * Create a new FieldLabel
32665 * @param {Object} config The config object
32668 Roo.bootstrap.FieldLabel = function(config){
32669 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32674 * Fires after the field has been marked as invalid.
32675 * @param {Roo.form.FieldLabel} this
32676 * @param {String} msg The validation message
32681 * Fires after the field has been validated with no errors.
32682 * @param {Roo.form.FieldLabel} this
32688 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32695 invalidClass : 'has-warning',
32696 validClass : 'has-success',
32697 iconTooltip : 'This field is required',
32698 indicatorpos : 'left',
32700 getAutoCreate : function(){
32703 if (!this.allowBlank) {
32709 cls : 'roo-bootstrap-field-label ' + this.cls,
32714 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32715 tooltip : this.iconTooltip
32724 if(this.indicatorpos == 'right'){
32727 cls : 'roo-bootstrap-field-label ' + this.cls,
32736 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32737 tooltip : this.iconTooltip
32746 initEvents: function()
32748 Roo.bootstrap.Element.superclass.initEvents.call(this);
32750 this.indicator = this.indicatorEl();
32752 if(this.indicator){
32753 this.indicator.removeClass('visible');
32754 this.indicator.addClass('invisible');
32757 Roo.bootstrap.FieldLabel.register(this);
32760 indicatorEl : function()
32762 var indicator = this.el.select('i.roo-required-indicator',true).first();
32773 * Mark this field as valid
32775 markValid : function()
32777 if(this.indicator){
32778 this.indicator.removeClass('visible');
32779 this.indicator.addClass('invisible');
32781 if (Roo.bootstrap.version == 3) {
32782 this.el.removeClass(this.invalidClass);
32783 this.el.addClass(this.validClass);
32785 this.el.removeClass('is-invalid');
32786 this.el.addClass('is-valid');
32790 this.fireEvent('valid', this);
32794 * Mark this field as invalid
32795 * @param {String} msg The validation message
32797 markInvalid : function(msg)
32799 if(this.indicator){
32800 this.indicator.removeClass('invisible');
32801 this.indicator.addClass('visible');
32803 if (Roo.bootstrap.version == 3) {
32804 this.el.removeClass(this.validClass);
32805 this.el.addClass(this.invalidClass);
32807 this.el.removeClass('is-valid');
32808 this.el.addClass('is-invalid');
32812 this.fireEvent('invalid', this, msg);
32818 Roo.apply(Roo.bootstrap.FieldLabel, {
32823 * register a FieldLabel Group
32824 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32826 register : function(label)
32828 if(this.groups.hasOwnProperty(label.target)){
32832 this.groups[label.target] = label;
32836 * fetch a FieldLabel Group based on the target
32837 * @param {string} target
32838 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32840 get: function(target) {
32841 if (typeof(this.groups[target]) == 'undefined') {
32845 return this.groups[target] ;
32854 * page DateSplitField.
32860 * @class Roo.bootstrap.DateSplitField
32861 * @extends Roo.bootstrap.Component
32862 * Bootstrap DateSplitField class
32863 * @cfg {string} fieldLabel - the label associated
32864 * @cfg {Number} labelWidth set the width of label (0-12)
32865 * @cfg {String} labelAlign (top|left)
32866 * @cfg {Boolean} dayAllowBlank (true|false) default false
32867 * @cfg {Boolean} monthAllowBlank (true|false) default false
32868 * @cfg {Boolean} yearAllowBlank (true|false) default false
32869 * @cfg {string} dayPlaceholder
32870 * @cfg {string} monthPlaceholder
32871 * @cfg {string} yearPlaceholder
32872 * @cfg {string} dayFormat default 'd'
32873 * @cfg {string} monthFormat default 'm'
32874 * @cfg {string} yearFormat default 'Y'
32875 * @cfg {Number} labellg set the width of label (1-12)
32876 * @cfg {Number} labelmd set the width of label (1-12)
32877 * @cfg {Number} labelsm set the width of label (1-12)
32878 * @cfg {Number} labelxs set the width of label (1-12)
32882 * Create a new DateSplitField
32883 * @param {Object} config The config object
32886 Roo.bootstrap.DateSplitField = function(config){
32887 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32893 * getting the data of years
32894 * @param {Roo.bootstrap.DateSplitField} this
32895 * @param {Object} years
32900 * getting the data of days
32901 * @param {Roo.bootstrap.DateSplitField} this
32902 * @param {Object} days
32907 * Fires after the field has been marked as invalid.
32908 * @param {Roo.form.Field} this
32909 * @param {String} msg The validation message
32914 * Fires after the field has been validated with no errors.
32915 * @param {Roo.form.Field} this
32921 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32924 labelAlign : 'top',
32926 dayAllowBlank : false,
32927 monthAllowBlank : false,
32928 yearAllowBlank : false,
32929 dayPlaceholder : '',
32930 monthPlaceholder : '',
32931 yearPlaceholder : '',
32935 isFormField : true,
32941 getAutoCreate : function()
32945 cls : 'row roo-date-split-field-group',
32950 cls : 'form-hidden-field roo-date-split-field-group-value',
32956 var labelCls = 'col-md-12';
32957 var contentCls = 'col-md-4';
32959 if(this.fieldLabel){
32963 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32967 html : this.fieldLabel
32972 if(this.labelAlign == 'left'){
32974 if(this.labelWidth > 12){
32975 label.style = "width: " + this.labelWidth + 'px';
32978 if(this.labelWidth < 13 && this.labelmd == 0){
32979 this.labelmd = this.labelWidth;
32982 if(this.labellg > 0){
32983 labelCls = ' col-lg-' + this.labellg;
32984 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32987 if(this.labelmd > 0){
32988 labelCls = ' col-md-' + this.labelmd;
32989 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32992 if(this.labelsm > 0){
32993 labelCls = ' col-sm-' + this.labelsm;
32994 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32997 if(this.labelxs > 0){
32998 labelCls = ' col-xs-' + this.labelxs;
32999 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33003 label.cls += ' ' + labelCls;
33005 cfg.cn.push(label);
33008 Roo.each(['day', 'month', 'year'], function(t){
33011 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33018 inputEl: function ()
33020 return this.el.select('.roo-date-split-field-group-value', true).first();
33023 onRender : function(ct, position)
33027 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33029 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33031 this.dayField = new Roo.bootstrap.ComboBox({
33032 allowBlank : this.dayAllowBlank,
33033 alwaysQuery : true,
33034 displayField : 'value',
33037 forceSelection : true,
33039 placeholder : this.dayPlaceholder,
33040 selectOnFocus : true,
33041 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33042 triggerAction : 'all',
33044 valueField : 'value',
33045 store : new Roo.data.SimpleStore({
33046 data : (function() {
33048 _this.fireEvent('days', _this, days);
33051 fields : [ 'value' ]
33054 select : function (_self, record, index)
33056 _this.setValue(_this.getValue());
33061 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33063 this.monthField = new Roo.bootstrap.MonthField({
33064 after : '<i class=\"fa fa-calendar\"></i>',
33065 allowBlank : this.monthAllowBlank,
33066 placeholder : this.monthPlaceholder,
33069 render : function (_self)
33071 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33072 e.preventDefault();
33076 select : function (_self, oldvalue, newvalue)
33078 _this.setValue(_this.getValue());
33083 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33085 this.yearField = new Roo.bootstrap.ComboBox({
33086 allowBlank : this.yearAllowBlank,
33087 alwaysQuery : true,
33088 displayField : 'value',
33091 forceSelection : true,
33093 placeholder : this.yearPlaceholder,
33094 selectOnFocus : true,
33095 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33096 triggerAction : 'all',
33098 valueField : 'value',
33099 store : new Roo.data.SimpleStore({
33100 data : (function() {
33102 _this.fireEvent('years', _this, years);
33105 fields : [ 'value' ]
33108 select : function (_self, record, index)
33110 _this.setValue(_this.getValue());
33115 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33118 setValue : function(v, format)
33120 this.inputEl.dom.value = v;
33122 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33124 var d = Date.parseDate(v, f);
33131 this.setDay(d.format(this.dayFormat));
33132 this.setMonth(d.format(this.monthFormat));
33133 this.setYear(d.format(this.yearFormat));
33140 setDay : function(v)
33142 this.dayField.setValue(v);
33143 this.inputEl.dom.value = this.getValue();
33148 setMonth : function(v)
33150 this.monthField.setValue(v, true);
33151 this.inputEl.dom.value = this.getValue();
33156 setYear : function(v)
33158 this.yearField.setValue(v);
33159 this.inputEl.dom.value = this.getValue();
33164 getDay : function()
33166 return this.dayField.getValue();
33169 getMonth : function()
33171 return this.monthField.getValue();
33174 getYear : function()
33176 return this.yearField.getValue();
33179 getValue : function()
33181 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33183 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33193 this.inputEl.dom.value = '';
33198 validate : function()
33200 var d = this.dayField.validate();
33201 var m = this.monthField.validate();
33202 var y = this.yearField.validate();
33207 (!this.dayAllowBlank && !d) ||
33208 (!this.monthAllowBlank && !m) ||
33209 (!this.yearAllowBlank && !y)
33214 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33223 this.markInvalid();
33228 markValid : function()
33231 var label = this.el.select('label', true).first();
33232 var icon = this.el.select('i.fa-star', true).first();
33238 this.fireEvent('valid', this);
33242 * Mark this field as invalid
33243 * @param {String} msg The validation message
33245 markInvalid : function(msg)
33248 var label = this.el.select('label', true).first();
33249 var icon = this.el.select('i.fa-star', true).first();
33251 if(label && !icon){
33252 this.el.select('.roo-date-split-field-label', true).createChild({
33254 cls : 'text-danger fa fa-lg fa-star',
33255 tooltip : 'This field is required',
33256 style : 'margin-right:5px;'
33260 this.fireEvent('invalid', this, msg);
33263 clearInvalid : function()
33265 var label = this.el.select('label', true).first();
33266 var icon = this.el.select('i.fa-star', true).first();
33272 this.fireEvent('valid', this);
33275 getName: function()
33285 * http://masonry.desandro.com
33287 * The idea is to render all the bricks based on vertical width...
33289 * The original code extends 'outlayer' - we might need to use that....
33295 * @class Roo.bootstrap.LayoutMasonry
33296 * @extends Roo.bootstrap.Component
33297 * Bootstrap Layout Masonry class
33300 * Create a new Element
33301 * @param {Object} config The config object
33304 Roo.bootstrap.LayoutMasonry = function(config){
33306 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33310 Roo.bootstrap.LayoutMasonry.register(this);
33316 * Fire after layout the items
33317 * @param {Roo.bootstrap.LayoutMasonry} this
33318 * @param {Roo.EventObject} e
33325 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33328 * @cfg {Boolean} isLayoutInstant = no animation?
33330 isLayoutInstant : false, // needed?
33333 * @cfg {Number} boxWidth width of the columns
33338 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33343 * @cfg {Number} padWidth padding below box..
33348 * @cfg {Number} gutter gutter width..
33353 * @cfg {Number} maxCols maximum number of columns
33359 * @cfg {Boolean} isAutoInitial defalut true
33361 isAutoInitial : true,
33366 * @cfg {Boolean} isHorizontal defalut false
33368 isHorizontal : false,
33370 currentSize : null,
33376 bricks: null, //CompositeElement
33380 _isLayoutInited : false,
33382 // isAlternative : false, // only use for vertical layout...
33385 * @cfg {Number} alternativePadWidth padding below box..
33387 alternativePadWidth : 50,
33389 selectedBrick : [],
33391 getAutoCreate : function(){
33393 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33397 cls: 'blog-masonary-wrapper ' + this.cls,
33399 cls : 'mas-boxes masonary'
33406 getChildContainer: function( )
33408 if (this.boxesEl) {
33409 return this.boxesEl;
33412 this.boxesEl = this.el.select('.mas-boxes').first();
33414 return this.boxesEl;
33418 initEvents : function()
33422 if(this.isAutoInitial){
33423 Roo.log('hook children rendered');
33424 this.on('childrenrendered', function() {
33425 Roo.log('children rendered');
33431 initial : function()
33433 this.selectedBrick = [];
33435 this.currentSize = this.el.getBox(true);
33437 Roo.EventManager.onWindowResize(this.resize, this);
33439 if(!this.isAutoInitial){
33447 //this.layout.defer(500,this);
33451 resize : function()
33453 var cs = this.el.getBox(true);
33456 this.currentSize.width == cs.width &&
33457 this.currentSize.x == cs.x &&
33458 this.currentSize.height == cs.height &&
33459 this.currentSize.y == cs.y
33461 Roo.log("no change in with or X or Y");
33465 this.currentSize = cs;
33471 layout : function()
33473 this._resetLayout();
33475 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33477 this.layoutItems( isInstant );
33479 this._isLayoutInited = true;
33481 this.fireEvent('layout', this);
33485 _resetLayout : function()
33487 if(this.isHorizontal){
33488 this.horizontalMeasureColumns();
33492 this.verticalMeasureColumns();
33496 verticalMeasureColumns : function()
33498 this.getContainerWidth();
33500 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33501 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33505 var boxWidth = this.boxWidth + this.padWidth;
33507 if(this.containerWidth < this.boxWidth){
33508 boxWidth = this.containerWidth
33511 var containerWidth = this.containerWidth;
33513 var cols = Math.floor(containerWidth / boxWidth);
33515 this.cols = Math.max( cols, 1 );
33517 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33519 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33521 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33523 this.colWidth = boxWidth + avail - this.padWidth;
33525 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33526 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33529 horizontalMeasureColumns : function()
33531 this.getContainerWidth();
33533 var boxWidth = this.boxWidth;
33535 if(this.containerWidth < boxWidth){
33536 boxWidth = this.containerWidth;
33539 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33541 this.el.setHeight(boxWidth);
33545 getContainerWidth : function()
33547 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33550 layoutItems : function( isInstant )
33552 Roo.log(this.bricks);
33554 var items = Roo.apply([], this.bricks);
33556 if(this.isHorizontal){
33557 this._horizontalLayoutItems( items , isInstant );
33561 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33562 // this._verticalAlternativeLayoutItems( items , isInstant );
33566 this._verticalLayoutItems( items , isInstant );
33570 _verticalLayoutItems : function ( items , isInstant)
33572 if ( !items || !items.length ) {
33577 ['xs', 'xs', 'xs', 'tall'],
33578 ['xs', 'xs', 'tall'],
33579 ['xs', 'xs', 'sm'],
33580 ['xs', 'xs', 'xs'],
33586 ['sm', 'xs', 'xs'],
33590 ['tall', 'xs', 'xs', 'xs'],
33591 ['tall', 'xs', 'xs'],
33603 Roo.each(items, function(item, k){
33605 switch (item.size) {
33606 // these layouts take up a full box,
33617 boxes.push([item]);
33640 var filterPattern = function(box, length)
33648 var pattern = box.slice(0, length);
33652 Roo.each(pattern, function(i){
33653 format.push(i.size);
33656 Roo.each(standard, function(s){
33658 if(String(s) != String(format)){
33667 if(!match && length == 1){
33672 filterPattern(box, length - 1);
33676 queue.push(pattern);
33678 box = box.slice(length, box.length);
33680 filterPattern(box, 4);
33686 Roo.each(boxes, function(box, k){
33692 if(box.length == 1){
33697 filterPattern(box, 4);
33701 this._processVerticalLayoutQueue( queue, isInstant );
33705 // _verticalAlternativeLayoutItems : function( items , isInstant )
33707 // if ( !items || !items.length ) {
33711 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33715 _horizontalLayoutItems : function ( items , isInstant)
33717 if ( !items || !items.length || items.length < 3) {
33723 var eItems = items.slice(0, 3);
33725 items = items.slice(3, items.length);
33728 ['xs', 'xs', 'xs', 'wide'],
33729 ['xs', 'xs', 'wide'],
33730 ['xs', 'xs', 'sm'],
33731 ['xs', 'xs', 'xs'],
33737 ['sm', 'xs', 'xs'],
33741 ['wide', 'xs', 'xs', 'xs'],
33742 ['wide', 'xs', 'xs'],
33755 Roo.each(items, function(item, k){
33757 switch (item.size) {
33768 boxes.push([item]);
33792 var filterPattern = function(box, length)
33800 var pattern = box.slice(0, length);
33804 Roo.each(pattern, function(i){
33805 format.push(i.size);
33808 Roo.each(standard, function(s){
33810 if(String(s) != String(format)){
33819 if(!match && length == 1){
33824 filterPattern(box, length - 1);
33828 queue.push(pattern);
33830 box = box.slice(length, box.length);
33832 filterPattern(box, 4);
33838 Roo.each(boxes, function(box, k){
33844 if(box.length == 1){
33849 filterPattern(box, 4);
33856 var pos = this.el.getBox(true);
33860 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33862 var hit_end = false;
33864 Roo.each(queue, function(box){
33868 Roo.each(box, function(b){
33870 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33880 Roo.each(box, function(b){
33882 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33885 mx = Math.max(mx, b.x);
33889 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33893 Roo.each(box, function(b){
33895 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33909 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33912 /** Sets position of item in DOM
33913 * @param {Element} item
33914 * @param {Number} x - horizontal position
33915 * @param {Number} y - vertical position
33916 * @param {Boolean} isInstant - disables transitions
33918 _processVerticalLayoutQueue : function( queue, isInstant )
33920 var pos = this.el.getBox(true);
33925 for (var i = 0; i < this.cols; i++){
33929 Roo.each(queue, function(box, k){
33931 var col = k % this.cols;
33933 Roo.each(box, function(b,kk){
33935 b.el.position('absolute');
33937 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33938 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33940 if(b.size == 'md-left' || b.size == 'md-right'){
33941 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33942 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33945 b.el.setWidth(width);
33946 b.el.setHeight(height);
33948 b.el.select('iframe',true).setSize(width,height);
33952 for (var i = 0; i < this.cols; i++){
33954 if(maxY[i] < maxY[col]){
33959 col = Math.min(col, i);
33963 x = pos.x + col * (this.colWidth + this.padWidth);
33967 var positions = [];
33969 switch (box.length){
33971 positions = this.getVerticalOneBoxColPositions(x, y, box);
33974 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33977 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33980 positions = this.getVerticalFourBoxColPositions(x, y, box);
33986 Roo.each(box, function(b,kk){
33988 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33990 var sz = b.el.getSize();
33992 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34000 for (var i = 0; i < this.cols; i++){
34001 mY = Math.max(mY, maxY[i]);
34004 this.el.setHeight(mY - pos.y);
34008 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34010 // var pos = this.el.getBox(true);
34013 // var maxX = pos.right;
34015 // var maxHeight = 0;
34017 // Roo.each(items, function(item, k){
34021 // item.el.position('absolute');
34023 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34025 // item.el.setWidth(width);
34027 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34029 // item.el.setHeight(height);
34032 // item.el.setXY([x, y], isInstant ? false : true);
34034 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34037 // y = y + height + this.alternativePadWidth;
34039 // maxHeight = maxHeight + height + this.alternativePadWidth;
34043 // this.el.setHeight(maxHeight);
34047 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34049 var pos = this.el.getBox(true);
34054 var maxX = pos.right;
34056 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34058 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34060 Roo.each(queue, function(box, k){
34062 Roo.each(box, function(b, kk){
34064 b.el.position('absolute');
34066 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34067 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34069 if(b.size == 'md-left' || b.size == 'md-right'){
34070 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34071 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34074 b.el.setWidth(width);
34075 b.el.setHeight(height);
34083 var positions = [];
34085 switch (box.length){
34087 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34090 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34093 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34096 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34102 Roo.each(box, function(b,kk){
34104 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34106 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34114 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34116 Roo.each(eItems, function(b,k){
34118 b.size = (k == 0) ? 'sm' : 'xs';
34119 b.x = (k == 0) ? 2 : 1;
34120 b.y = (k == 0) ? 2 : 1;
34122 b.el.position('absolute');
34124 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34126 b.el.setWidth(width);
34128 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34130 b.el.setHeight(height);
34134 var positions = [];
34137 x : maxX - this.unitWidth * 2 - this.gutter,
34142 x : maxX - this.unitWidth,
34143 y : minY + (this.unitWidth + this.gutter) * 2
34147 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34151 Roo.each(eItems, function(b,k){
34153 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34159 getVerticalOneBoxColPositions : function(x, y, box)
34163 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34165 if(box[0].size == 'md-left'){
34169 if(box[0].size == 'md-right'){
34174 x : x + (this.unitWidth + this.gutter) * rand,
34181 getVerticalTwoBoxColPositions : function(x, y, box)
34185 if(box[0].size == 'xs'){
34189 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34193 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34207 x : x + (this.unitWidth + this.gutter) * 2,
34208 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34215 getVerticalThreeBoxColPositions : function(x, y, box)
34219 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34227 x : x + (this.unitWidth + this.gutter) * 1,
34232 x : x + (this.unitWidth + this.gutter) * 2,
34240 if(box[0].size == 'xs' && box[1].size == 'xs'){
34249 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34253 x : x + (this.unitWidth + this.gutter) * 1,
34267 x : x + (this.unitWidth + this.gutter) * 2,
34272 x : x + (this.unitWidth + this.gutter) * 2,
34273 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34280 getVerticalFourBoxColPositions : function(x, y, box)
34284 if(box[0].size == 'xs'){
34293 y : y + (this.unitHeight + this.gutter) * 1
34298 y : y + (this.unitHeight + this.gutter) * 2
34302 x : x + (this.unitWidth + this.gutter) * 1,
34316 x : x + (this.unitWidth + this.gutter) * 2,
34321 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34322 y : y + (this.unitHeight + this.gutter) * 1
34326 x : x + (this.unitWidth + this.gutter) * 2,
34327 y : y + (this.unitWidth + this.gutter) * 2
34334 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34338 if(box[0].size == 'md-left'){
34340 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34347 if(box[0].size == 'md-right'){
34349 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34350 y : minY + (this.unitWidth + this.gutter) * 1
34356 var rand = Math.floor(Math.random() * (4 - box[0].y));
34359 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34360 y : minY + (this.unitWidth + this.gutter) * rand
34367 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34371 if(box[0].size == 'xs'){
34374 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34379 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34380 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34388 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34393 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34394 y : minY + (this.unitWidth + this.gutter) * 2
34401 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34405 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34408 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34413 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34414 y : minY + (this.unitWidth + this.gutter) * 1
34418 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34419 y : minY + (this.unitWidth + this.gutter) * 2
34426 if(box[0].size == 'xs' && box[1].size == 'xs'){
34429 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34434 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34439 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34440 y : minY + (this.unitWidth + this.gutter) * 1
34448 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34453 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34454 y : minY + (this.unitWidth + this.gutter) * 2
34458 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34459 y : minY + (this.unitWidth + this.gutter) * 2
34466 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34470 if(box[0].size == 'xs'){
34473 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34478 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34483 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),
34488 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34489 y : minY + (this.unitWidth + this.gutter) * 1
34497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34502 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34503 y : minY + (this.unitWidth + this.gutter) * 2
34507 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34508 y : minY + (this.unitWidth + this.gutter) * 2
34512 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),
34513 y : minY + (this.unitWidth + this.gutter) * 2
34521 * remove a Masonry Brick
34522 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34524 removeBrick : function(brick_id)
34530 for (var i = 0; i<this.bricks.length; i++) {
34531 if (this.bricks[i].id == brick_id) {
34532 this.bricks.splice(i,1);
34533 this.el.dom.removeChild(Roo.get(brick_id).dom);
34540 * adds a Masonry Brick
34541 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34543 addBrick : function(cfg)
34545 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34546 //this.register(cn);
34547 cn.parentId = this.id;
34548 cn.render(this.el);
34553 * register a Masonry Brick
34554 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34557 register : function(brick)
34559 this.bricks.push(brick);
34560 brick.masonryId = this.id;
34564 * clear all the Masonry Brick
34566 clearAll : function()
34569 //this.getChildContainer().dom.innerHTML = "";
34570 this.el.dom.innerHTML = '';
34573 getSelected : function()
34575 if (!this.selectedBrick) {
34579 return this.selectedBrick;
34583 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34587 * register a Masonry Layout
34588 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34591 register : function(layout)
34593 this.groups[layout.id] = layout;
34596 * fetch a Masonry Layout based on the masonry layout ID
34597 * @param {string} the masonry layout to add
34598 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34601 get: function(layout_id) {
34602 if (typeof(this.groups[layout_id]) == 'undefined') {
34605 return this.groups[layout_id] ;
34617 * http://masonry.desandro.com
34619 * The idea is to render all the bricks based on vertical width...
34621 * The original code extends 'outlayer' - we might need to use that....
34627 * @class Roo.bootstrap.LayoutMasonryAuto
34628 * @extends Roo.bootstrap.Component
34629 * Bootstrap Layout Masonry class
34632 * Create a new Element
34633 * @param {Object} config The config object
34636 Roo.bootstrap.LayoutMasonryAuto = function(config){
34637 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34640 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34643 * @cfg {Boolean} isFitWidth - resize the width..
34645 isFitWidth : false, // options..
34647 * @cfg {Boolean} isOriginLeft = left align?
34649 isOriginLeft : true,
34651 * @cfg {Boolean} isOriginTop = top align?
34653 isOriginTop : false,
34655 * @cfg {Boolean} isLayoutInstant = no animation?
34657 isLayoutInstant : false, // needed?
34659 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34661 isResizingContainer : true,
34663 * @cfg {Number} columnWidth width of the columns
34669 * @cfg {Number} maxCols maximum number of columns
34674 * @cfg {Number} padHeight padding below box..
34680 * @cfg {Boolean} isAutoInitial defalut true
34683 isAutoInitial : true,
34689 initialColumnWidth : 0,
34690 currentSize : null,
34692 colYs : null, // array.
34699 bricks: null, //CompositeElement
34700 cols : 0, // array?
34701 // element : null, // wrapped now this.el
34702 _isLayoutInited : null,
34705 getAutoCreate : function(){
34709 cls: 'blog-masonary-wrapper ' + this.cls,
34711 cls : 'mas-boxes masonary'
34718 getChildContainer: function( )
34720 if (this.boxesEl) {
34721 return this.boxesEl;
34724 this.boxesEl = this.el.select('.mas-boxes').first();
34726 return this.boxesEl;
34730 initEvents : function()
34734 if(this.isAutoInitial){
34735 Roo.log('hook children rendered');
34736 this.on('childrenrendered', function() {
34737 Roo.log('children rendered');
34744 initial : function()
34746 this.reloadItems();
34748 this.currentSize = this.el.getBox(true);
34750 /// was window resize... - let's see if this works..
34751 Roo.EventManager.onWindowResize(this.resize, this);
34753 if(!this.isAutoInitial){
34758 this.layout.defer(500,this);
34761 reloadItems: function()
34763 this.bricks = this.el.select('.masonry-brick', true);
34765 this.bricks.each(function(b) {
34766 //Roo.log(b.getSize());
34767 if (!b.attr('originalwidth')) {
34768 b.attr('originalwidth', b.getSize().width);
34773 Roo.log(this.bricks.elements.length);
34776 resize : function()
34779 var cs = this.el.getBox(true);
34781 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34782 Roo.log("no change in with or X");
34785 this.currentSize = cs;
34789 layout : function()
34792 this._resetLayout();
34793 //this._manageStamps();
34795 // don't animate first layout
34796 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34797 this.layoutItems( isInstant );
34799 // flag for initalized
34800 this._isLayoutInited = true;
34803 layoutItems : function( isInstant )
34805 //var items = this._getItemsForLayout( this.items );
34806 // original code supports filtering layout items.. we just ignore it..
34808 this._layoutItems( this.bricks , isInstant );
34810 this._postLayout();
34812 _layoutItems : function ( items , isInstant)
34814 //this.fireEvent( 'layout', this, items );
34817 if ( !items || !items.elements.length ) {
34818 // no items, emit event with empty array
34823 items.each(function(item) {
34824 Roo.log("layout item");
34826 // get x/y object from method
34827 var position = this._getItemLayoutPosition( item );
34829 position.item = item;
34830 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34831 queue.push( position );
34834 this._processLayoutQueue( queue );
34836 /** Sets position of item in DOM
34837 * @param {Element} item
34838 * @param {Number} x - horizontal position
34839 * @param {Number} y - vertical position
34840 * @param {Boolean} isInstant - disables transitions
34842 _processLayoutQueue : function( queue )
34844 for ( var i=0, len = queue.length; i < len; i++ ) {
34845 var obj = queue[i];
34846 obj.item.position('absolute');
34847 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34853 * Any logic you want to do after each layout,
34854 * i.e. size the container
34856 _postLayout : function()
34858 this.resizeContainer();
34861 resizeContainer : function()
34863 if ( !this.isResizingContainer ) {
34866 var size = this._getContainerSize();
34868 this.el.setSize(size.width,size.height);
34869 this.boxesEl.setSize(size.width,size.height);
34875 _resetLayout : function()
34877 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34878 this.colWidth = this.el.getWidth();
34879 //this.gutter = this.el.getWidth();
34881 this.measureColumns();
34887 this.colYs.push( 0 );
34893 measureColumns : function()
34895 this.getContainerWidth();
34896 // if columnWidth is 0, default to outerWidth of first item
34897 if ( !this.columnWidth ) {
34898 var firstItem = this.bricks.first();
34899 Roo.log(firstItem);
34900 this.columnWidth = this.containerWidth;
34901 if (firstItem && firstItem.attr('originalwidth') ) {
34902 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34904 // columnWidth fall back to item of first element
34905 Roo.log("set column width?");
34906 this.initialColumnWidth = this.columnWidth ;
34908 // if first elem has no width, default to size of container
34913 if (this.initialColumnWidth) {
34914 this.columnWidth = this.initialColumnWidth;
34919 // column width is fixed at the top - however if container width get's smaller we should
34922 // this bit calcs how man columns..
34924 var columnWidth = this.columnWidth += this.gutter;
34926 // calculate columns
34927 var containerWidth = this.containerWidth + this.gutter;
34929 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34930 // fix rounding errors, typically with gutters
34931 var excess = columnWidth - containerWidth % columnWidth;
34934 // if overshoot is less than a pixel, round up, otherwise floor it
34935 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34936 cols = Math[ mathMethod ]( cols );
34937 this.cols = Math.max( cols, 1 );
34938 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34940 // padding positioning..
34941 var totalColWidth = this.cols * this.columnWidth;
34942 var padavail = this.containerWidth - totalColWidth;
34943 // so for 2 columns - we need 3 'pads'
34945 var padNeeded = (1+this.cols) * this.padWidth;
34947 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34949 this.columnWidth += padExtra
34950 //this.padWidth = Math.floor(padavail / ( this.cols));
34952 // adjust colum width so that padding is fixed??
34954 // we have 3 columns ... total = width * 3
34955 // we have X left over... that should be used by
34957 //if (this.expandC) {
34965 getContainerWidth : function()
34967 /* // container is parent if fit width
34968 var container = this.isFitWidth ? this.element.parentNode : this.element;
34969 // check that this.size and size are there
34970 // IE8 triggers resize on body size change, so they might not be
34972 var size = getSize( container ); //FIXME
34973 this.containerWidth = size && size.innerWidth; //FIXME
34976 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34980 _getItemLayoutPosition : function( item ) // what is item?
34982 // we resize the item to our columnWidth..
34984 item.setWidth(this.columnWidth);
34985 item.autoBoxAdjust = false;
34987 var sz = item.getSize();
34989 // how many columns does this brick span
34990 var remainder = this.containerWidth % this.columnWidth;
34992 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34993 // round if off by 1 pixel, otherwise use ceil
34994 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34995 colSpan = Math.min( colSpan, this.cols );
34997 // normally this should be '1' as we dont' currently allow multi width columns..
34999 var colGroup = this._getColGroup( colSpan );
35000 // get the minimum Y value from the columns
35001 var minimumY = Math.min.apply( Math, colGroup );
35002 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35004 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35006 // position the brick
35008 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35009 y: this.currentSize.y + minimumY + this.padHeight
35013 // apply setHeight to necessary columns
35014 var setHeight = minimumY + sz.height + this.padHeight;
35015 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35017 var setSpan = this.cols + 1 - colGroup.length;
35018 for ( var i = 0; i < setSpan; i++ ) {
35019 this.colYs[ shortColIndex + i ] = setHeight ;
35026 * @param {Number} colSpan - number of columns the element spans
35027 * @returns {Array} colGroup
35029 _getColGroup : function( colSpan )
35031 if ( colSpan < 2 ) {
35032 // if brick spans only one column, use all the column Ys
35037 // how many different places could this brick fit horizontally
35038 var groupCount = this.cols + 1 - colSpan;
35039 // for each group potential horizontal position
35040 for ( var i = 0; i < groupCount; i++ ) {
35041 // make an array of colY values for that one group
35042 var groupColYs = this.colYs.slice( i, i + colSpan );
35043 // and get the max value of the array
35044 colGroup[i] = Math.max.apply( Math, groupColYs );
35049 _manageStamp : function( stamp )
35051 var stampSize = stamp.getSize();
35052 var offset = stamp.getBox();
35053 // get the columns that this stamp affects
35054 var firstX = this.isOriginLeft ? offset.x : offset.right;
35055 var lastX = firstX + stampSize.width;
35056 var firstCol = Math.floor( firstX / this.columnWidth );
35057 firstCol = Math.max( 0, firstCol );
35059 var lastCol = Math.floor( lastX / this.columnWidth );
35060 // lastCol should not go over if multiple of columnWidth #425
35061 lastCol -= lastX % this.columnWidth ? 0 : 1;
35062 lastCol = Math.min( this.cols - 1, lastCol );
35064 // set colYs to bottom of the stamp
35065 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35068 for ( var i = firstCol; i <= lastCol; i++ ) {
35069 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35074 _getContainerSize : function()
35076 this.maxY = Math.max.apply( Math, this.colYs );
35081 if ( this.isFitWidth ) {
35082 size.width = this._getContainerFitWidth();
35088 _getContainerFitWidth : function()
35090 var unusedCols = 0;
35091 // count unused columns
35094 if ( this.colYs[i] !== 0 ) {
35099 // fit container to columns that have been used
35100 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35103 needsResizeLayout : function()
35105 var previousWidth = this.containerWidth;
35106 this.getContainerWidth();
35107 return previousWidth !== this.containerWidth;
35122 * @class Roo.bootstrap.MasonryBrick
35123 * @extends Roo.bootstrap.Component
35124 * Bootstrap MasonryBrick class
35127 * Create a new MasonryBrick
35128 * @param {Object} config The config object
35131 Roo.bootstrap.MasonryBrick = function(config){
35133 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35135 Roo.bootstrap.MasonryBrick.register(this);
35141 * When a MasonryBrick is clcik
35142 * @param {Roo.bootstrap.MasonryBrick} this
35143 * @param {Roo.EventObject} e
35149 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35152 * @cfg {String} title
35156 * @cfg {String} html
35160 * @cfg {String} bgimage
35164 * @cfg {String} videourl
35168 * @cfg {String} cls
35172 * @cfg {String} href
35176 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35181 * @cfg {String} placetitle (center|bottom)
35186 * @cfg {Boolean} isFitContainer defalut true
35188 isFitContainer : true,
35191 * @cfg {Boolean} preventDefault defalut false
35193 preventDefault : false,
35196 * @cfg {Boolean} inverse defalut false
35198 maskInverse : false,
35200 getAutoCreate : function()
35202 if(!this.isFitContainer){
35203 return this.getSplitAutoCreate();
35206 var cls = 'masonry-brick masonry-brick-full';
35208 if(this.href.length){
35209 cls += ' masonry-brick-link';
35212 if(this.bgimage.length){
35213 cls += ' masonry-brick-image';
35216 if(this.maskInverse){
35217 cls += ' mask-inverse';
35220 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35221 cls += ' enable-mask';
35225 cls += ' masonry-' + this.size + '-brick';
35228 if(this.placetitle.length){
35230 switch (this.placetitle) {
35232 cls += ' masonry-center-title';
35235 cls += ' masonry-bottom-title';
35242 if(!this.html.length && !this.bgimage.length){
35243 cls += ' masonry-center-title';
35246 if(!this.html.length && this.bgimage.length){
35247 cls += ' masonry-bottom-title';
35252 cls += ' ' + this.cls;
35256 tag: (this.href.length) ? 'a' : 'div',
35261 cls: 'masonry-brick-mask'
35265 cls: 'masonry-brick-paragraph',
35271 if(this.href.length){
35272 cfg.href = this.href;
35275 var cn = cfg.cn[1].cn;
35277 if(this.title.length){
35280 cls: 'masonry-brick-title',
35285 if(this.html.length){
35288 cls: 'masonry-brick-text',
35293 if (!this.title.length && !this.html.length) {
35294 cfg.cn[1].cls += ' hide';
35297 if(this.bgimage.length){
35300 cls: 'masonry-brick-image-view',
35305 if(this.videourl.length){
35306 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35307 // youtube support only?
35310 cls: 'masonry-brick-image-view',
35313 allowfullscreen : true
35321 getSplitAutoCreate : function()
35323 var cls = 'masonry-brick masonry-brick-split';
35325 if(this.href.length){
35326 cls += ' masonry-brick-link';
35329 if(this.bgimage.length){
35330 cls += ' masonry-brick-image';
35334 cls += ' masonry-' + this.size + '-brick';
35337 switch (this.placetitle) {
35339 cls += ' masonry-center-title';
35342 cls += ' masonry-bottom-title';
35345 if(!this.bgimage.length){
35346 cls += ' masonry-center-title';
35349 if(this.bgimage.length){
35350 cls += ' masonry-bottom-title';
35356 cls += ' ' + this.cls;
35360 tag: (this.href.length) ? 'a' : 'div',
35365 cls: 'masonry-brick-split-head',
35369 cls: 'masonry-brick-paragraph',
35376 cls: 'masonry-brick-split-body',
35382 if(this.href.length){
35383 cfg.href = this.href;
35386 if(this.title.length){
35387 cfg.cn[0].cn[0].cn.push({
35389 cls: 'masonry-brick-title',
35394 if(this.html.length){
35395 cfg.cn[1].cn.push({
35397 cls: 'masonry-brick-text',
35402 if(this.bgimage.length){
35403 cfg.cn[0].cn.push({
35405 cls: 'masonry-brick-image-view',
35410 if(this.videourl.length){
35411 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35412 // youtube support only?
35413 cfg.cn[0].cn.cn.push({
35415 cls: 'masonry-brick-image-view',
35418 allowfullscreen : true
35425 initEvents: function()
35427 switch (this.size) {
35460 this.el.on('touchstart', this.onTouchStart, this);
35461 this.el.on('touchmove', this.onTouchMove, this);
35462 this.el.on('touchend', this.onTouchEnd, this);
35463 this.el.on('contextmenu', this.onContextMenu, this);
35465 this.el.on('mouseenter' ,this.enter, this);
35466 this.el.on('mouseleave', this.leave, this);
35467 this.el.on('click', this.onClick, this);
35470 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35471 this.parent().bricks.push(this);
35476 onClick: function(e, el)
35478 var time = this.endTimer - this.startTimer;
35479 // Roo.log(e.preventDefault());
35482 e.preventDefault();
35487 if(!this.preventDefault){
35491 e.preventDefault();
35493 if (this.activeClass != '') {
35494 this.selectBrick();
35497 this.fireEvent('click', this, e);
35500 enter: function(e, el)
35502 e.preventDefault();
35504 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35508 if(this.bgimage.length && this.html.length){
35509 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35513 leave: function(e, el)
35515 e.preventDefault();
35517 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35521 if(this.bgimage.length && this.html.length){
35522 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35526 onTouchStart: function(e, el)
35528 // e.preventDefault();
35530 this.touchmoved = false;
35532 if(!this.isFitContainer){
35536 if(!this.bgimage.length || !this.html.length){
35540 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35542 this.timer = new Date().getTime();
35546 onTouchMove: function(e, el)
35548 this.touchmoved = true;
35551 onContextMenu : function(e,el)
35553 e.preventDefault();
35554 e.stopPropagation();
35558 onTouchEnd: function(e, el)
35560 // e.preventDefault();
35562 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35569 if(!this.bgimage.length || !this.html.length){
35571 if(this.href.length){
35572 window.location.href = this.href;
35578 if(!this.isFitContainer){
35582 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35584 window.location.href = this.href;
35587 //selection on single brick only
35588 selectBrick : function() {
35590 if (!this.parentId) {
35594 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35595 var index = m.selectedBrick.indexOf(this.id);
35598 m.selectedBrick.splice(index,1);
35599 this.el.removeClass(this.activeClass);
35603 for(var i = 0; i < m.selectedBrick.length; i++) {
35604 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35605 b.el.removeClass(b.activeClass);
35608 m.selectedBrick = [];
35610 m.selectedBrick.push(this.id);
35611 this.el.addClass(this.activeClass);
35615 isSelected : function(){
35616 return this.el.hasClass(this.activeClass);
35621 Roo.apply(Roo.bootstrap.MasonryBrick, {
35624 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35626 * register a Masonry Brick
35627 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35630 register : function(brick)
35632 //this.groups[brick.id] = brick;
35633 this.groups.add(brick.id, brick);
35636 * fetch a masonry brick based on the masonry brick ID
35637 * @param {string} the masonry brick to add
35638 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35641 get: function(brick_id)
35643 // if (typeof(this.groups[brick_id]) == 'undefined') {
35646 // return this.groups[brick_id] ;
35648 if(this.groups.key(brick_id)) {
35649 return this.groups.key(brick_id);
35667 * @class Roo.bootstrap.Brick
35668 * @extends Roo.bootstrap.Component
35669 * Bootstrap Brick class
35672 * Create a new Brick
35673 * @param {Object} config The config object
35676 Roo.bootstrap.Brick = function(config){
35677 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35683 * When a Brick is click
35684 * @param {Roo.bootstrap.Brick} this
35685 * @param {Roo.EventObject} e
35691 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35694 * @cfg {String} title
35698 * @cfg {String} html
35702 * @cfg {String} bgimage
35706 * @cfg {String} cls
35710 * @cfg {String} href
35714 * @cfg {String} video
35718 * @cfg {Boolean} square
35722 getAutoCreate : function()
35724 var cls = 'roo-brick';
35726 if(this.href.length){
35727 cls += ' roo-brick-link';
35730 if(this.bgimage.length){
35731 cls += ' roo-brick-image';
35734 if(!this.html.length && !this.bgimage.length){
35735 cls += ' roo-brick-center-title';
35738 if(!this.html.length && this.bgimage.length){
35739 cls += ' roo-brick-bottom-title';
35743 cls += ' ' + this.cls;
35747 tag: (this.href.length) ? 'a' : 'div',
35752 cls: 'roo-brick-paragraph',
35758 if(this.href.length){
35759 cfg.href = this.href;
35762 var cn = cfg.cn[0].cn;
35764 if(this.title.length){
35767 cls: 'roo-brick-title',
35772 if(this.html.length){
35775 cls: 'roo-brick-text',
35782 if(this.bgimage.length){
35785 cls: 'roo-brick-image-view',
35793 initEvents: function()
35795 if(this.title.length || this.html.length){
35796 this.el.on('mouseenter' ,this.enter, this);
35797 this.el.on('mouseleave', this.leave, this);
35800 Roo.EventManager.onWindowResize(this.resize, this);
35802 if(this.bgimage.length){
35803 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35804 this.imageEl.on('load', this.onImageLoad, this);
35811 onImageLoad : function()
35816 resize : function()
35818 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35820 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35822 if(this.bgimage.length){
35823 var image = this.el.select('.roo-brick-image-view', true).first();
35825 image.setWidth(paragraph.getWidth());
35828 image.setHeight(paragraph.getWidth());
35831 this.el.setHeight(image.getHeight());
35832 paragraph.setHeight(image.getHeight());
35838 enter: function(e, el)
35840 e.preventDefault();
35842 if(this.bgimage.length){
35843 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35844 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35848 leave: function(e, el)
35850 e.preventDefault();
35852 if(this.bgimage.length){
35853 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35854 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35869 * @class Roo.bootstrap.NumberField
35870 * @extends Roo.bootstrap.Input
35871 * Bootstrap NumberField class
35877 * Create a new NumberField
35878 * @param {Object} config The config object
35881 Roo.bootstrap.NumberField = function(config){
35882 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35885 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35888 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35890 allowDecimals : true,
35892 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35894 decimalSeparator : ".",
35896 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35898 decimalPrecision : 2,
35900 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35902 allowNegative : true,
35905 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35909 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35911 minValue : Number.NEGATIVE_INFINITY,
35913 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35915 maxValue : Number.MAX_VALUE,
35917 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35919 minText : "The minimum value for this field is {0}",
35921 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35923 maxText : "The maximum value for this field is {0}",
35925 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35926 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35928 nanText : "{0} is not a valid number",
35930 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35932 thousandsDelimiter : false,
35934 * @cfg {String} valueAlign alignment of value
35936 valueAlign : "left",
35938 getAutoCreate : function()
35940 var hiddenInput = {
35944 cls: 'hidden-number-input'
35948 hiddenInput.name = this.name;
35953 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35955 this.name = hiddenInput.name;
35957 if(cfg.cn.length > 0) {
35958 cfg.cn.push(hiddenInput);
35965 initEvents : function()
35967 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35969 var allowed = "0123456789";
35971 if(this.allowDecimals){
35972 allowed += this.decimalSeparator;
35975 if(this.allowNegative){
35979 if(this.thousandsDelimiter) {
35983 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35985 var keyPress = function(e){
35987 var k = e.getKey();
35989 var c = e.getCharCode();
35992 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35993 allowed.indexOf(String.fromCharCode(c)) === -1
35999 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36003 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36008 this.el.on("keypress", keyPress, this);
36011 validateValue : function(value)
36014 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36018 var num = this.parseValue(value);
36021 this.markInvalid(String.format(this.nanText, value));
36025 if(num < this.minValue){
36026 this.markInvalid(String.format(this.minText, this.minValue));
36030 if(num > this.maxValue){
36031 this.markInvalid(String.format(this.maxText, this.maxValue));
36038 getValue : function()
36040 var v = this.hiddenEl().getValue();
36042 return this.fixPrecision(this.parseValue(v));
36045 parseValue : function(value)
36047 if(this.thousandsDelimiter) {
36049 r = new RegExp(",", "g");
36050 value = value.replace(r, "");
36053 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36054 return isNaN(value) ? '' : value;
36057 fixPrecision : function(value)
36059 if(this.thousandsDelimiter) {
36061 r = new RegExp(",", "g");
36062 value = value.replace(r, "");
36065 var nan = isNaN(value);
36067 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36068 return nan ? '' : value;
36070 return parseFloat(value).toFixed(this.decimalPrecision);
36073 setValue : function(v)
36075 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36081 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36083 this.inputEl().dom.value = (v == '') ? '' :
36084 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36086 if(!this.allowZero && v === '0') {
36087 this.hiddenEl().dom.value = '';
36088 this.inputEl().dom.value = '';
36095 decimalPrecisionFcn : function(v)
36097 return Math.floor(v);
36100 beforeBlur : function()
36102 var v = this.parseValue(this.getRawValue());
36104 if(v || v === 0 || v === ''){
36109 hiddenEl : function()
36111 return this.el.select('input.hidden-number-input',true).first();
36123 * @class Roo.bootstrap.DocumentSlider
36124 * @extends Roo.bootstrap.Component
36125 * Bootstrap DocumentSlider class
36128 * Create a new DocumentViewer
36129 * @param {Object} config The config object
36132 Roo.bootstrap.DocumentSlider = function(config){
36133 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36140 * Fire after initEvent
36141 * @param {Roo.bootstrap.DocumentSlider} this
36146 * Fire after update
36147 * @param {Roo.bootstrap.DocumentSlider} this
36153 * @param {Roo.bootstrap.DocumentSlider} this
36159 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36165 getAutoCreate : function()
36169 cls : 'roo-document-slider',
36173 cls : 'roo-document-slider-header',
36177 cls : 'roo-document-slider-header-title'
36183 cls : 'roo-document-slider-body',
36187 cls : 'roo-document-slider-prev',
36191 cls : 'fa fa-chevron-left'
36197 cls : 'roo-document-slider-thumb',
36201 cls : 'roo-document-slider-image'
36207 cls : 'roo-document-slider-next',
36211 cls : 'fa fa-chevron-right'
36223 initEvents : function()
36225 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36226 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36228 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36229 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36231 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36232 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36234 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36235 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36237 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36238 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36240 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36241 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36243 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36244 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36246 this.thumbEl.on('click', this.onClick, this);
36248 this.prevIndicator.on('click', this.prev, this);
36250 this.nextIndicator.on('click', this.next, this);
36254 initial : function()
36256 if(this.files.length){
36257 this.indicator = 1;
36261 this.fireEvent('initial', this);
36264 update : function()
36266 this.imageEl.attr('src', this.files[this.indicator - 1]);
36268 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36270 this.prevIndicator.show();
36272 if(this.indicator == 1){
36273 this.prevIndicator.hide();
36276 this.nextIndicator.show();
36278 if(this.indicator == this.files.length){
36279 this.nextIndicator.hide();
36282 this.thumbEl.scrollTo('top');
36284 this.fireEvent('update', this);
36287 onClick : function(e)
36289 e.preventDefault();
36291 this.fireEvent('click', this);
36296 e.preventDefault();
36298 this.indicator = Math.max(1, this.indicator - 1);
36305 e.preventDefault();
36307 this.indicator = Math.min(this.files.length, this.indicator + 1);
36321 * @class Roo.bootstrap.RadioSet
36322 * @extends Roo.bootstrap.Input
36323 * Bootstrap RadioSet class
36324 * @cfg {String} indicatorpos (left|right) default left
36325 * @cfg {Boolean} inline (true|false) inline the element (default true)
36326 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36328 * Create a new RadioSet
36329 * @param {Object} config The config object
36332 Roo.bootstrap.RadioSet = function(config){
36334 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36338 Roo.bootstrap.RadioSet.register(this);
36343 * Fires when the element is checked or unchecked.
36344 * @param {Roo.bootstrap.RadioSet} this This radio
36345 * @param {Roo.bootstrap.Radio} item The checked item
36350 * Fires when the element is click.
36351 * @param {Roo.bootstrap.RadioSet} this This radio set
36352 * @param {Roo.bootstrap.Radio} item The checked item
36353 * @param {Roo.EventObject} e The event object
36360 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36368 indicatorpos : 'left',
36370 getAutoCreate : function()
36374 cls : 'roo-radio-set-label',
36378 html : this.fieldLabel
36382 if (Roo.bootstrap.version == 3) {
36385 if(this.indicatorpos == 'left'){
36388 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36389 tooltip : 'This field is required'
36394 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36395 tooltip : 'This field is required'
36401 cls : 'roo-radio-set-items'
36404 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36406 if (align === 'left' && this.fieldLabel.length) {
36409 cls : "roo-radio-set-right",
36415 if(this.labelWidth > 12){
36416 label.style = "width: " + this.labelWidth + 'px';
36419 if(this.labelWidth < 13 && this.labelmd == 0){
36420 this.labelmd = this.labelWidth;
36423 if(this.labellg > 0){
36424 label.cls += ' col-lg-' + this.labellg;
36425 items.cls += ' col-lg-' + (12 - this.labellg);
36428 if(this.labelmd > 0){
36429 label.cls += ' col-md-' + this.labelmd;
36430 items.cls += ' col-md-' + (12 - this.labelmd);
36433 if(this.labelsm > 0){
36434 label.cls += ' col-sm-' + this.labelsm;
36435 items.cls += ' col-sm-' + (12 - this.labelsm);
36438 if(this.labelxs > 0){
36439 label.cls += ' col-xs-' + this.labelxs;
36440 items.cls += ' col-xs-' + (12 - this.labelxs);
36446 cls : 'roo-radio-set',
36450 cls : 'roo-radio-set-input',
36453 value : this.value ? this.value : ''
36460 if(this.weight.length){
36461 cfg.cls += ' roo-radio-' + this.weight;
36465 cfg.cls += ' roo-radio-set-inline';
36469 ['xs','sm','md','lg'].map(function(size){
36470 if (settings[size]) {
36471 cfg.cls += ' col-' + size + '-' + settings[size];
36479 initEvents : function()
36481 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36482 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36484 if(!this.fieldLabel.length){
36485 this.labelEl.hide();
36488 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36489 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36491 this.indicator = this.indicatorEl();
36493 if(this.indicator){
36494 this.indicator.addClass('invisible');
36497 this.originalValue = this.getValue();
36501 inputEl: function ()
36503 return this.el.select('.roo-radio-set-input', true).first();
36506 getChildContainer : function()
36508 return this.itemsEl;
36511 register : function(item)
36513 this.radioes.push(item);
36517 validate : function()
36519 if(this.getVisibilityEl().hasClass('hidden')){
36525 Roo.each(this.radioes, function(i){
36534 if(this.allowBlank) {
36538 if(this.disabled || valid){
36543 this.markInvalid();
36548 markValid : function()
36550 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36551 this.indicatorEl().removeClass('visible');
36552 this.indicatorEl().addClass('invisible');
36556 if (Roo.bootstrap.version == 3) {
36557 this.el.removeClass([this.invalidClass, this.validClass]);
36558 this.el.addClass(this.validClass);
36560 this.el.removeClass(['is-invalid','is-valid']);
36561 this.el.addClass(['is-valid']);
36563 this.fireEvent('valid', this);
36566 markInvalid : function(msg)
36568 if(this.allowBlank || this.disabled){
36572 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36573 this.indicatorEl().removeClass('invisible');
36574 this.indicatorEl().addClass('visible');
36576 if (Roo.bootstrap.version == 3) {
36577 this.el.removeClass([this.invalidClass, this.validClass]);
36578 this.el.addClass(this.invalidClass);
36580 this.el.removeClass(['is-invalid','is-valid']);
36581 this.el.addClass(['is-invalid']);
36584 this.fireEvent('invalid', this, msg);
36588 setValue : function(v, suppressEvent)
36590 if(this.value === v){
36597 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36600 Roo.each(this.radioes, function(i){
36602 i.el.removeClass('checked');
36605 Roo.each(this.radioes, function(i){
36607 if(i.value === v || i.value.toString() === v.toString()){
36609 i.el.addClass('checked');
36611 if(suppressEvent !== true){
36612 this.fireEvent('check', this, i);
36623 clearInvalid : function(){
36625 if(!this.el || this.preventMark){
36629 this.el.removeClass([this.invalidClass]);
36631 this.fireEvent('valid', this);
36636 Roo.apply(Roo.bootstrap.RadioSet, {
36640 register : function(set)
36642 this.groups[set.name] = set;
36645 get: function(name)
36647 if (typeof(this.groups[name]) == 'undefined') {
36651 return this.groups[name] ;
36657 * Ext JS Library 1.1.1
36658 * Copyright(c) 2006-2007, Ext JS, LLC.
36660 * Originally Released Under LGPL - original licence link has changed is not relivant.
36663 * <script type="text/javascript">
36668 * @class Roo.bootstrap.SplitBar
36669 * @extends Roo.util.Observable
36670 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36674 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36675 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36676 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36677 split.minSize = 100;
36678 split.maxSize = 600;
36679 split.animate = true;
36680 split.on('moved', splitterMoved);
36683 * Create a new SplitBar
36684 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36685 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36686 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36687 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36688 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36689 position of the SplitBar).
36691 Roo.bootstrap.SplitBar = function(cfg){
36696 // dragElement : elm
36697 // resizingElement: el,
36699 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36700 // placement : Roo.bootstrap.SplitBar.LEFT ,
36701 // existingProxy ???
36704 this.el = Roo.get(cfg.dragElement, true);
36705 this.el.dom.unselectable = "on";
36707 this.resizingEl = Roo.get(cfg.resizingElement, true);
36711 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36712 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36715 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36718 * The minimum size of the resizing element. (Defaults to 0)
36724 * The maximum size of the resizing element. (Defaults to 2000)
36727 this.maxSize = 2000;
36730 * Whether to animate the transition to the new size
36733 this.animate = false;
36736 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36739 this.useShim = false;
36744 if(!cfg.existingProxy){
36746 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36748 this.proxy = Roo.get(cfg.existingProxy).dom;
36751 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36754 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36757 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36760 this.dragSpecs = {};
36763 * @private The adapter to use to positon and resize elements
36765 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36766 this.adapter.init(this);
36768 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36770 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36771 this.el.addClass("roo-splitbar-h");
36774 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36775 this.el.addClass("roo-splitbar-v");
36781 * Fires when the splitter is moved (alias for {@link #event-moved})
36782 * @param {Roo.bootstrap.SplitBar} this
36783 * @param {Number} newSize the new width or height
36788 * Fires when the splitter is moved
36789 * @param {Roo.bootstrap.SplitBar} this
36790 * @param {Number} newSize the new width or height
36794 * @event beforeresize
36795 * Fires before the splitter is dragged
36796 * @param {Roo.bootstrap.SplitBar} this
36798 "beforeresize" : true,
36800 "beforeapply" : true
36803 Roo.util.Observable.call(this);
36806 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36807 onStartProxyDrag : function(x, y){
36808 this.fireEvent("beforeresize", this);
36810 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36812 o.enableDisplayMode("block");
36813 // all splitbars share the same overlay
36814 Roo.bootstrap.SplitBar.prototype.overlay = o;
36816 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36817 this.overlay.show();
36818 Roo.get(this.proxy).setDisplayed("block");
36819 var size = this.adapter.getElementSize(this);
36820 this.activeMinSize = this.getMinimumSize();;
36821 this.activeMaxSize = this.getMaximumSize();;
36822 var c1 = size - this.activeMinSize;
36823 var c2 = Math.max(this.activeMaxSize - size, 0);
36824 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36825 this.dd.resetConstraints();
36826 this.dd.setXConstraint(
36827 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36828 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36830 this.dd.setYConstraint(0, 0);
36832 this.dd.resetConstraints();
36833 this.dd.setXConstraint(0, 0);
36834 this.dd.setYConstraint(
36835 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36836 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36839 this.dragSpecs.startSize = size;
36840 this.dragSpecs.startPoint = [x, y];
36841 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36845 * @private Called after the drag operation by the DDProxy
36847 onEndProxyDrag : function(e){
36848 Roo.get(this.proxy).setDisplayed(false);
36849 var endPoint = Roo.lib.Event.getXY(e);
36851 this.overlay.hide();
36854 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36855 newSize = this.dragSpecs.startSize +
36856 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36857 endPoint[0] - this.dragSpecs.startPoint[0] :
36858 this.dragSpecs.startPoint[0] - endPoint[0]
36861 newSize = this.dragSpecs.startSize +
36862 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36863 endPoint[1] - this.dragSpecs.startPoint[1] :
36864 this.dragSpecs.startPoint[1] - endPoint[1]
36867 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36868 if(newSize != this.dragSpecs.startSize){
36869 if(this.fireEvent('beforeapply', this, newSize) !== false){
36870 this.adapter.setElementSize(this, newSize);
36871 this.fireEvent("moved", this, newSize);
36872 this.fireEvent("resize", this, newSize);
36878 * Get the adapter this SplitBar uses
36879 * @return The adapter object
36881 getAdapter : function(){
36882 return this.adapter;
36886 * Set the adapter this SplitBar uses
36887 * @param {Object} adapter A SplitBar adapter object
36889 setAdapter : function(adapter){
36890 this.adapter = adapter;
36891 this.adapter.init(this);
36895 * Gets the minimum size for the resizing element
36896 * @return {Number} The minimum size
36898 getMinimumSize : function(){
36899 return this.minSize;
36903 * Sets the minimum size for the resizing element
36904 * @param {Number} minSize The minimum size
36906 setMinimumSize : function(minSize){
36907 this.minSize = minSize;
36911 * Gets the maximum size for the resizing element
36912 * @return {Number} The maximum size
36914 getMaximumSize : function(){
36915 return this.maxSize;
36919 * Sets the maximum size for the resizing element
36920 * @param {Number} maxSize The maximum size
36922 setMaximumSize : function(maxSize){
36923 this.maxSize = maxSize;
36927 * Sets the initialize size for the resizing element
36928 * @param {Number} size The initial size
36930 setCurrentSize : function(size){
36931 var oldAnimate = this.animate;
36932 this.animate = false;
36933 this.adapter.setElementSize(this, size);
36934 this.animate = oldAnimate;
36938 * Destroy this splitbar.
36939 * @param {Boolean} removeEl True to remove the element
36941 destroy : function(removeEl){
36943 this.shim.remove();
36946 this.proxy.parentNode.removeChild(this.proxy);
36954 * @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.
36956 Roo.bootstrap.SplitBar.createProxy = function(dir){
36957 var proxy = new Roo.Element(document.createElement("div"));
36958 proxy.unselectable();
36959 var cls = 'roo-splitbar-proxy';
36960 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36961 document.body.appendChild(proxy.dom);
36966 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36967 * Default Adapter. It assumes the splitter and resizing element are not positioned
36968 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36970 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36973 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36974 // do nothing for now
36975 init : function(s){
36979 * Called before drag operations to get the current size of the resizing element.
36980 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36982 getElementSize : function(s){
36983 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36984 return s.resizingEl.getWidth();
36986 return s.resizingEl.getHeight();
36991 * Called after drag operations to set the size of the resizing element.
36992 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36993 * @param {Number} newSize The new size to set
36994 * @param {Function} onComplete A function to be invoked when resizing is complete
36996 setElementSize : function(s, newSize, onComplete){
36997 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36999 s.resizingEl.setWidth(newSize);
37001 onComplete(s, newSize);
37004 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37009 s.resizingEl.setHeight(newSize);
37011 onComplete(s, newSize);
37014 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37021 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37022 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37023 * Adapter that moves the splitter element to align with the resized sizing element.
37024 * Used with an absolute positioned SplitBar.
37025 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37026 * document.body, make sure you assign an id to the body element.
37028 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37029 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37030 this.container = Roo.get(container);
37033 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37034 init : function(s){
37035 this.basic.init(s);
37038 getElementSize : function(s){
37039 return this.basic.getElementSize(s);
37042 setElementSize : function(s, newSize, onComplete){
37043 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37046 moveSplitter : function(s){
37047 var yes = Roo.bootstrap.SplitBar;
37048 switch(s.placement){
37050 s.el.setX(s.resizingEl.getRight());
37053 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37056 s.el.setY(s.resizingEl.getBottom());
37059 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37066 * Orientation constant - Create a vertical SplitBar
37070 Roo.bootstrap.SplitBar.VERTICAL = 1;
37073 * Orientation constant - Create a horizontal SplitBar
37077 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37080 * Placement constant - The resizing element is to the left of the splitter element
37084 Roo.bootstrap.SplitBar.LEFT = 1;
37087 * Placement constant - The resizing element is to the right of the splitter element
37091 Roo.bootstrap.SplitBar.RIGHT = 2;
37094 * Placement constant - The resizing element is positioned above the splitter element
37098 Roo.bootstrap.SplitBar.TOP = 3;
37101 * Placement constant - The resizing element is positioned under splitter element
37105 Roo.bootstrap.SplitBar.BOTTOM = 4;
37106 Roo.namespace("Roo.bootstrap.layout");/*
37108 * Ext JS Library 1.1.1
37109 * Copyright(c) 2006-2007, Ext JS, LLC.
37111 * Originally Released Under LGPL - original licence link has changed is not relivant.
37114 * <script type="text/javascript">
37118 * @class Roo.bootstrap.layout.Manager
37119 * @extends Roo.bootstrap.Component
37120 * Base class for layout managers.
37122 Roo.bootstrap.layout.Manager = function(config)
37124 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37130 /** false to disable window resize monitoring @type Boolean */
37131 this.monitorWindowResize = true;
37136 * Fires when a layout is performed.
37137 * @param {Roo.LayoutManager} this
37141 * @event regionresized
37142 * Fires when the user resizes a region.
37143 * @param {Roo.LayoutRegion} region The resized region
37144 * @param {Number} newSize The new size (width for east/west, height for north/south)
37146 "regionresized" : true,
37148 * @event regioncollapsed
37149 * Fires when a region is collapsed.
37150 * @param {Roo.LayoutRegion} region The collapsed region
37152 "regioncollapsed" : true,
37154 * @event regionexpanded
37155 * Fires when a region is expanded.
37156 * @param {Roo.LayoutRegion} region The expanded region
37158 "regionexpanded" : true
37160 this.updating = false;
37163 this.el = Roo.get(config.el);
37169 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37174 monitorWindowResize : true,
37180 onRender : function(ct, position)
37183 this.el = Roo.get(ct);
37186 //this.fireEvent('render',this);
37190 initEvents: function()
37194 // ie scrollbar fix
37195 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37196 document.body.scroll = "no";
37197 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37198 this.el.position('relative');
37200 this.id = this.el.id;
37201 this.el.addClass("roo-layout-container");
37202 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37203 if(this.el.dom != document.body ) {
37204 this.el.on('resize', this.layout,this);
37205 this.el.on('show', this.layout,this);
37211 * Returns true if this layout is currently being updated
37212 * @return {Boolean}
37214 isUpdating : function(){
37215 return this.updating;
37219 * Suspend the LayoutManager from doing auto-layouts while
37220 * making multiple add or remove calls
37222 beginUpdate : function(){
37223 this.updating = true;
37227 * Restore auto-layouts and optionally disable the manager from performing a layout
37228 * @param {Boolean} noLayout true to disable a layout update
37230 endUpdate : function(noLayout){
37231 this.updating = false;
37237 layout: function(){
37241 onRegionResized : function(region, newSize){
37242 this.fireEvent("regionresized", region, newSize);
37246 onRegionCollapsed : function(region){
37247 this.fireEvent("regioncollapsed", region);
37250 onRegionExpanded : function(region){
37251 this.fireEvent("regionexpanded", region);
37255 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37256 * performs box-model adjustments.
37257 * @return {Object} The size as an object {width: (the width), height: (the height)}
37259 getViewSize : function()
37262 if(this.el.dom != document.body){
37263 size = this.el.getSize();
37265 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37267 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37268 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37273 * Returns the Element this layout is bound to.
37274 * @return {Roo.Element}
37276 getEl : function(){
37281 * Returns the specified region.
37282 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37283 * @return {Roo.LayoutRegion}
37285 getRegion : function(target){
37286 return this.regions[target.toLowerCase()];
37289 onWindowResize : function(){
37290 if(this.monitorWindowResize){
37297 * Ext JS Library 1.1.1
37298 * Copyright(c) 2006-2007, Ext JS, LLC.
37300 * Originally Released Under LGPL - original licence link has changed is not relivant.
37303 * <script type="text/javascript">
37306 * @class Roo.bootstrap.layout.Border
37307 * @extends Roo.bootstrap.layout.Manager
37308 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37309 * please see: examples/bootstrap/nested.html<br><br>
37311 <b>The container the layout is rendered into can be either the body element or any other element.
37312 If it is not the body element, the container needs to either be an absolute positioned element,
37313 or you will need to add "position:relative" to the css of the container. You will also need to specify
37314 the container size if it is not the body element.</b>
37317 * Create a new Border
37318 * @param {Object} config Configuration options
37320 Roo.bootstrap.layout.Border = function(config){
37321 config = config || {};
37322 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37326 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37327 if(config[region]){
37328 config[region].region = region;
37329 this.addRegion(config[region]);
37335 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37337 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37339 parent : false, // this might point to a 'nest' or a ???
37342 * Creates and adds a new region if it doesn't already exist.
37343 * @param {String} target The target region key (north, south, east, west or center).
37344 * @param {Object} config The regions config object
37345 * @return {BorderLayoutRegion} The new region
37347 addRegion : function(config)
37349 if(!this.regions[config.region]){
37350 var r = this.factory(config);
37351 this.bindRegion(r);
37353 return this.regions[config.region];
37357 bindRegion : function(r){
37358 this.regions[r.config.region] = r;
37360 r.on("visibilitychange", this.layout, this);
37361 r.on("paneladded", this.layout, this);
37362 r.on("panelremoved", this.layout, this);
37363 r.on("invalidated", this.layout, this);
37364 r.on("resized", this.onRegionResized, this);
37365 r.on("collapsed", this.onRegionCollapsed, this);
37366 r.on("expanded", this.onRegionExpanded, this);
37370 * Performs a layout update.
37372 layout : function()
37374 if(this.updating) {
37378 // render all the rebions if they have not been done alreayd?
37379 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37380 if(this.regions[region] && !this.regions[region].bodyEl){
37381 this.regions[region].onRender(this.el)
37385 var size = this.getViewSize();
37386 var w = size.width;
37387 var h = size.height;
37392 //var x = 0, y = 0;
37394 var rs = this.regions;
37395 var north = rs["north"];
37396 var south = rs["south"];
37397 var west = rs["west"];
37398 var east = rs["east"];
37399 var center = rs["center"];
37400 //if(this.hideOnLayout){ // not supported anymore
37401 //c.el.setStyle("display", "none");
37403 if(north && north.isVisible()){
37404 var b = north.getBox();
37405 var m = north.getMargins();
37406 b.width = w - (m.left+m.right);
37409 centerY = b.height + b.y + m.bottom;
37410 centerH -= centerY;
37411 north.updateBox(this.safeBox(b));
37413 if(south && south.isVisible()){
37414 var b = south.getBox();
37415 var m = south.getMargins();
37416 b.width = w - (m.left+m.right);
37418 var totalHeight = (b.height + m.top + m.bottom);
37419 b.y = h - totalHeight + m.top;
37420 centerH -= totalHeight;
37421 south.updateBox(this.safeBox(b));
37423 if(west && west.isVisible()){
37424 var b = west.getBox();
37425 var m = west.getMargins();
37426 b.height = centerH - (m.top+m.bottom);
37428 b.y = centerY + m.top;
37429 var totalWidth = (b.width + m.left + m.right);
37430 centerX += totalWidth;
37431 centerW -= totalWidth;
37432 west.updateBox(this.safeBox(b));
37434 if(east && east.isVisible()){
37435 var b = east.getBox();
37436 var m = east.getMargins();
37437 b.height = centerH - (m.top+m.bottom);
37438 var totalWidth = (b.width + m.left + m.right);
37439 b.x = w - totalWidth + m.left;
37440 b.y = centerY + m.top;
37441 centerW -= totalWidth;
37442 east.updateBox(this.safeBox(b));
37445 var m = center.getMargins();
37447 x: centerX + m.left,
37448 y: centerY + m.top,
37449 width: centerW - (m.left+m.right),
37450 height: centerH - (m.top+m.bottom)
37452 //if(this.hideOnLayout){
37453 //center.el.setStyle("display", "block");
37455 center.updateBox(this.safeBox(centerBox));
37458 this.fireEvent("layout", this);
37462 safeBox : function(box){
37463 box.width = Math.max(0, box.width);
37464 box.height = Math.max(0, box.height);
37469 * Adds a ContentPanel (or subclass) to this layout.
37470 * @param {String} target The target region key (north, south, east, west or center).
37471 * @param {Roo.ContentPanel} panel The panel to add
37472 * @return {Roo.ContentPanel} The added panel
37474 add : function(target, panel){
37476 target = target.toLowerCase();
37477 return this.regions[target].add(panel);
37481 * Remove a ContentPanel (or subclass) to this layout.
37482 * @param {String} target The target region key (north, south, east, west or center).
37483 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37484 * @return {Roo.ContentPanel} The removed panel
37486 remove : function(target, panel){
37487 target = target.toLowerCase();
37488 return this.regions[target].remove(panel);
37492 * Searches all regions for a panel with the specified id
37493 * @param {String} panelId
37494 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37496 findPanel : function(panelId){
37497 var rs = this.regions;
37498 for(var target in rs){
37499 if(typeof rs[target] != "function"){
37500 var p = rs[target].getPanel(panelId);
37510 * Searches all regions for a panel with the specified id and activates (shows) it.
37511 * @param {String/ContentPanel} panelId The panels id or the panel itself
37512 * @return {Roo.ContentPanel} The shown panel or null
37514 showPanel : function(panelId) {
37515 var rs = this.regions;
37516 for(var target in rs){
37517 var r = rs[target];
37518 if(typeof r != "function"){
37519 if(r.hasPanel(panelId)){
37520 return r.showPanel(panelId);
37528 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37529 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37532 restoreState : function(provider){
37534 provider = Roo.state.Manager;
37536 var sm = new Roo.LayoutStateManager();
37537 sm.init(this, provider);
37543 * Adds a xtype elements to the layout.
37547 xtype : 'ContentPanel',
37554 xtype : 'NestedLayoutPanel',
37560 items : [ ... list of content panels or nested layout panels.. ]
37564 * @param {Object} cfg Xtype definition of item to add.
37566 addxtype : function(cfg)
37568 // basically accepts a pannel...
37569 // can accept a layout region..!?!?
37570 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37573 // theory? children can only be panels??
37575 //if (!cfg.xtype.match(/Panel$/)) {
37580 if (typeof(cfg.region) == 'undefined') {
37581 Roo.log("Failed to add Panel, region was not set");
37585 var region = cfg.region;
37591 xitems = cfg.items;
37596 if ( region == 'center') {
37597 Roo.log("Center: " + cfg.title);
37603 case 'Content': // ContentPanel (el, cfg)
37604 case 'Scroll': // ContentPanel (el, cfg)
37606 cfg.autoCreate = cfg.autoCreate || true;
37607 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37609 // var el = this.el.createChild();
37610 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37613 this.add(region, ret);
37617 case 'TreePanel': // our new panel!
37618 cfg.el = this.el.createChild();
37619 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37620 this.add(region, ret);
37625 // create a new Layout (which is a Border Layout...
37627 var clayout = cfg.layout;
37628 clayout.el = this.el.createChild();
37629 clayout.items = clayout.items || [];
37633 // replace this exitems with the clayout ones..
37634 xitems = clayout.items;
37636 // force background off if it's in center...
37637 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37638 cfg.background = false;
37640 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37643 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37644 //console.log('adding nested layout panel ' + cfg.toSource());
37645 this.add(region, ret);
37646 nb = {}; /// find first...
37651 // needs grid and region
37653 //var el = this.getRegion(region).el.createChild();
37655 *var el = this.el.createChild();
37656 // create the grid first...
37657 cfg.grid.container = el;
37658 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37661 if (region == 'center' && this.active ) {
37662 cfg.background = false;
37665 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37667 this.add(region, ret);
37669 if (cfg.background) {
37670 // render grid on panel activation (if panel background)
37671 ret.on('activate', function(gp) {
37672 if (!gp.grid.rendered) {
37673 // gp.grid.render(el);
37677 // cfg.grid.render(el);
37683 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37684 // it was the old xcomponent building that caused this before.
37685 // espeically if border is the top element in the tree.
37695 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37697 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37698 this.add(region, ret);
37702 throw "Can not add '" + cfg.xtype + "' to Border";
37708 this.beginUpdate();
37712 Roo.each(xitems, function(i) {
37713 region = nb && i.region ? i.region : false;
37715 var add = ret.addxtype(i);
37718 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37719 if (!i.background) {
37720 abn[region] = nb[region] ;
37727 // make the last non-background panel active..
37728 //if (nb) { Roo.log(abn); }
37731 for(var r in abn) {
37732 region = this.getRegion(r);
37734 // tried using nb[r], but it does not work..
37736 region.showPanel(abn[r]);
37747 factory : function(cfg)
37750 var validRegions = Roo.bootstrap.layout.Border.regions;
37752 var target = cfg.region;
37755 var r = Roo.bootstrap.layout;
37759 return new r.North(cfg);
37761 return new r.South(cfg);
37763 return new r.East(cfg);
37765 return new r.West(cfg);
37767 return new r.Center(cfg);
37769 throw 'Layout region "'+target+'" not supported.';
37776 * Ext JS Library 1.1.1
37777 * Copyright(c) 2006-2007, Ext JS, LLC.
37779 * Originally Released Under LGPL - original licence link has changed is not relivant.
37782 * <script type="text/javascript">
37786 * @class Roo.bootstrap.layout.Basic
37787 * @extends Roo.util.Observable
37788 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37789 * and does not have a titlebar, tabs or any other features. All it does is size and position
37790 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37791 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37792 * @cfg {string} region the region that it inhabits..
37793 * @cfg {bool} skipConfig skip config?
37797 Roo.bootstrap.layout.Basic = function(config){
37799 this.mgr = config.mgr;
37801 this.position = config.region;
37803 var skipConfig = config.skipConfig;
37807 * @scope Roo.BasicLayoutRegion
37811 * @event beforeremove
37812 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37813 * @param {Roo.LayoutRegion} this
37814 * @param {Roo.ContentPanel} panel The panel
37815 * @param {Object} e The cancel event object
37817 "beforeremove" : true,
37819 * @event invalidated
37820 * Fires when the layout for this region is changed.
37821 * @param {Roo.LayoutRegion} this
37823 "invalidated" : true,
37825 * @event visibilitychange
37826 * Fires when this region is shown or hidden
37827 * @param {Roo.LayoutRegion} this
37828 * @param {Boolean} visibility true or false
37830 "visibilitychange" : true,
37832 * @event paneladded
37833 * Fires when a panel is added.
37834 * @param {Roo.LayoutRegion} this
37835 * @param {Roo.ContentPanel} panel The panel
37837 "paneladded" : true,
37839 * @event panelremoved
37840 * Fires when a panel is removed.
37841 * @param {Roo.LayoutRegion} this
37842 * @param {Roo.ContentPanel} panel The panel
37844 "panelremoved" : true,
37846 * @event beforecollapse
37847 * Fires when this region before collapse.
37848 * @param {Roo.LayoutRegion} this
37850 "beforecollapse" : true,
37853 * Fires when this region is collapsed.
37854 * @param {Roo.LayoutRegion} this
37856 "collapsed" : true,
37859 * Fires when this region is expanded.
37860 * @param {Roo.LayoutRegion} this
37865 * Fires when this region is slid into view.
37866 * @param {Roo.LayoutRegion} this
37868 "slideshow" : true,
37871 * Fires when this region slides out of view.
37872 * @param {Roo.LayoutRegion} this
37874 "slidehide" : true,
37876 * @event panelactivated
37877 * Fires when a panel is activated.
37878 * @param {Roo.LayoutRegion} this
37879 * @param {Roo.ContentPanel} panel The activated panel
37881 "panelactivated" : true,
37884 * Fires when the user resizes this region.
37885 * @param {Roo.LayoutRegion} this
37886 * @param {Number} newSize The new size (width for east/west, height for north/south)
37890 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37891 this.panels = new Roo.util.MixedCollection();
37892 this.panels.getKey = this.getPanelId.createDelegate(this);
37894 this.activePanel = null;
37895 // ensure listeners are added...
37897 if (config.listeners || config.events) {
37898 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37899 listeners : config.listeners || {},
37900 events : config.events || {}
37904 if(skipConfig !== true){
37905 this.applyConfig(config);
37909 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37911 getPanelId : function(p){
37915 applyConfig : function(config){
37916 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37917 this.config = config;
37922 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37923 * the width, for horizontal (north, south) the height.
37924 * @param {Number} newSize The new width or height
37926 resizeTo : function(newSize){
37927 var el = this.el ? this.el :
37928 (this.activePanel ? this.activePanel.getEl() : null);
37930 switch(this.position){
37933 el.setWidth(newSize);
37934 this.fireEvent("resized", this, newSize);
37938 el.setHeight(newSize);
37939 this.fireEvent("resized", this, newSize);
37945 getBox : function(){
37946 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37949 getMargins : function(){
37950 return this.margins;
37953 updateBox : function(box){
37955 var el = this.activePanel.getEl();
37956 el.dom.style.left = box.x + "px";
37957 el.dom.style.top = box.y + "px";
37958 this.activePanel.setSize(box.width, box.height);
37962 * Returns the container element for this region.
37963 * @return {Roo.Element}
37965 getEl : function(){
37966 return this.activePanel;
37970 * Returns true if this region is currently visible.
37971 * @return {Boolean}
37973 isVisible : function(){
37974 return this.activePanel ? true : false;
37977 setActivePanel : function(panel){
37978 panel = this.getPanel(panel);
37979 if(this.activePanel && this.activePanel != panel){
37980 this.activePanel.setActiveState(false);
37981 this.activePanel.getEl().setLeftTop(-10000,-10000);
37983 this.activePanel = panel;
37984 panel.setActiveState(true);
37986 panel.setSize(this.box.width, this.box.height);
37988 this.fireEvent("panelactivated", this, panel);
37989 this.fireEvent("invalidated");
37993 * Show the specified panel.
37994 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37995 * @return {Roo.ContentPanel} The shown panel or null
37997 showPanel : function(panel){
37998 panel = this.getPanel(panel);
38000 this.setActivePanel(panel);
38006 * Get the active panel for this region.
38007 * @return {Roo.ContentPanel} The active panel or null
38009 getActivePanel : function(){
38010 return this.activePanel;
38014 * Add the passed ContentPanel(s)
38015 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38016 * @return {Roo.ContentPanel} The panel added (if only one was added)
38018 add : function(panel){
38019 if(arguments.length > 1){
38020 for(var i = 0, len = arguments.length; i < len; i++) {
38021 this.add(arguments[i]);
38025 if(this.hasPanel(panel)){
38026 this.showPanel(panel);
38029 var el = panel.getEl();
38030 if(el.dom.parentNode != this.mgr.el.dom){
38031 this.mgr.el.dom.appendChild(el.dom);
38033 if(panel.setRegion){
38034 panel.setRegion(this);
38036 this.panels.add(panel);
38037 el.setStyle("position", "absolute");
38038 if(!panel.background){
38039 this.setActivePanel(panel);
38040 if(this.config.initialSize && this.panels.getCount()==1){
38041 this.resizeTo(this.config.initialSize);
38044 this.fireEvent("paneladded", this, panel);
38049 * Returns true if the panel is in this region.
38050 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38051 * @return {Boolean}
38053 hasPanel : function(panel){
38054 if(typeof panel == "object"){ // must be panel obj
38055 panel = panel.getId();
38057 return this.getPanel(panel) ? true : false;
38061 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38062 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38063 * @param {Boolean} preservePanel Overrides the config preservePanel option
38064 * @return {Roo.ContentPanel} The panel that was removed
38066 remove : function(panel, preservePanel){
38067 panel = this.getPanel(panel);
38072 this.fireEvent("beforeremove", this, panel, e);
38073 if(e.cancel === true){
38076 var panelId = panel.getId();
38077 this.panels.removeKey(panelId);
38082 * Returns the panel specified or null if it's not in this region.
38083 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38084 * @return {Roo.ContentPanel}
38086 getPanel : function(id){
38087 if(typeof id == "object"){ // must be panel obj
38090 return this.panels.get(id);
38094 * Returns this regions position (north/south/east/west/center).
38097 getPosition: function(){
38098 return this.position;
38102 * Ext JS Library 1.1.1
38103 * Copyright(c) 2006-2007, Ext JS, LLC.
38105 * Originally Released Under LGPL - original licence link has changed is not relivant.
38108 * <script type="text/javascript">
38112 * @class Roo.bootstrap.layout.Region
38113 * @extends Roo.bootstrap.layout.Basic
38114 * This class represents a region in a layout manager.
38116 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38117 * @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})
38118 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38119 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38120 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38121 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38122 * @cfg {String} title The title for the region (overrides panel titles)
38123 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38124 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38125 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38126 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38127 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38128 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38129 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38130 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38131 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38132 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38134 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38135 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38136 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38137 * @cfg {Number} width For East/West panels
38138 * @cfg {Number} height For North/South panels
38139 * @cfg {Boolean} split To show the splitter
38140 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38142 * @cfg {string} cls Extra CSS classes to add to region
38144 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38145 * @cfg {string} region the region that it inhabits..
38148 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38149 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38151 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38152 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38153 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38155 Roo.bootstrap.layout.Region = function(config)
38157 this.applyConfig(config);
38159 var mgr = config.mgr;
38160 var pos = config.region;
38161 config.skipConfig = true;
38162 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38165 this.onRender(mgr.el);
38168 this.visible = true;
38169 this.collapsed = false;
38170 this.unrendered_panels = [];
38173 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38175 position: '', // set by wrapper (eg. north/south etc..)
38176 unrendered_panels : null, // unrendered panels.
38178 tabPosition : false,
38180 mgr: false, // points to 'Border'
38183 createBody : function(){
38184 /** This region's body element
38185 * @type Roo.Element */
38186 this.bodyEl = this.el.createChild({
38188 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38192 onRender: function(ctr, pos)
38194 var dh = Roo.DomHelper;
38195 /** This region's container element
38196 * @type Roo.Element */
38197 this.el = dh.append(ctr.dom, {
38199 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38201 /** This region's title element
38202 * @type Roo.Element */
38204 this.titleEl = dh.append(this.el.dom, {
38206 unselectable: "on",
38207 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38209 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38210 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38214 this.titleEl.enableDisplayMode();
38215 /** This region's title text element
38216 * @type HTMLElement */
38217 this.titleTextEl = this.titleEl.dom.firstChild;
38218 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38220 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38221 this.closeBtn.enableDisplayMode();
38222 this.closeBtn.on("click", this.closeClicked, this);
38223 this.closeBtn.hide();
38225 this.createBody(this.config);
38226 if(this.config.hideWhenEmpty){
38228 this.on("paneladded", this.validateVisibility, this);
38229 this.on("panelremoved", this.validateVisibility, this);
38231 if(this.autoScroll){
38232 this.bodyEl.setStyle("overflow", "auto");
38234 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38236 //if(c.titlebar !== false){
38237 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38238 this.titleEl.hide();
38240 this.titleEl.show();
38241 if(this.config.title){
38242 this.titleTextEl.innerHTML = this.config.title;
38246 if(this.config.collapsed){
38247 this.collapse(true);
38249 if(this.config.hidden){
38253 if (this.unrendered_panels && this.unrendered_panels.length) {
38254 for (var i =0;i< this.unrendered_panels.length; i++) {
38255 this.add(this.unrendered_panels[i]);
38257 this.unrendered_panels = null;
38263 applyConfig : function(c)
38266 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38267 var dh = Roo.DomHelper;
38268 if(c.titlebar !== false){
38269 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38270 this.collapseBtn.on("click", this.collapse, this);
38271 this.collapseBtn.enableDisplayMode();
38273 if(c.showPin === true || this.showPin){
38274 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38275 this.stickBtn.enableDisplayMode();
38276 this.stickBtn.on("click", this.expand, this);
38277 this.stickBtn.hide();
38282 /** This region's collapsed element
38283 * @type Roo.Element */
38286 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38287 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38290 if(c.floatable !== false){
38291 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38292 this.collapsedEl.on("click", this.collapseClick, this);
38295 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38296 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38297 id: "message", unselectable: "on", style:{"float":"left"}});
38298 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38300 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38301 this.expandBtn.on("click", this.expand, this);
38305 if(this.collapseBtn){
38306 this.collapseBtn.setVisible(c.collapsible == true);
38309 this.cmargins = c.cmargins || this.cmargins ||
38310 (this.position == "west" || this.position == "east" ?
38311 {top: 0, left: 2, right:2, bottom: 0} :
38312 {top: 2, left: 0, right:0, bottom: 2});
38314 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38317 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38319 this.autoScroll = c.autoScroll || false;
38324 this.duration = c.duration || .30;
38325 this.slideDuration = c.slideDuration || .45;
38330 * Returns true if this region is currently visible.
38331 * @return {Boolean}
38333 isVisible : function(){
38334 return this.visible;
38338 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38339 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38341 //setCollapsedTitle : function(title){
38342 // title = title || " ";
38343 // if(this.collapsedTitleTextEl){
38344 // this.collapsedTitleTextEl.innerHTML = title;
38348 getBox : function(){
38350 // if(!this.collapsed){
38351 b = this.el.getBox(false, true);
38353 // b = this.collapsedEl.getBox(false, true);
38358 getMargins : function(){
38359 return this.margins;
38360 //return this.collapsed ? this.cmargins : this.margins;
38363 highlight : function(){
38364 this.el.addClass("x-layout-panel-dragover");
38367 unhighlight : function(){
38368 this.el.removeClass("x-layout-panel-dragover");
38371 updateBox : function(box)
38373 if (!this.bodyEl) {
38374 return; // not rendered yet..
38378 if(!this.collapsed){
38379 this.el.dom.style.left = box.x + "px";
38380 this.el.dom.style.top = box.y + "px";
38381 this.updateBody(box.width, box.height);
38383 this.collapsedEl.dom.style.left = box.x + "px";
38384 this.collapsedEl.dom.style.top = box.y + "px";
38385 this.collapsedEl.setSize(box.width, box.height);
38388 this.tabs.autoSizeTabs();
38392 updateBody : function(w, h)
38395 this.el.setWidth(w);
38396 w -= this.el.getBorderWidth("rl");
38397 if(this.config.adjustments){
38398 w += this.config.adjustments[0];
38401 if(h !== null && h > 0){
38402 this.el.setHeight(h);
38403 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38404 h -= this.el.getBorderWidth("tb");
38405 if(this.config.adjustments){
38406 h += this.config.adjustments[1];
38408 this.bodyEl.setHeight(h);
38410 h = this.tabs.syncHeight(h);
38413 if(this.panelSize){
38414 w = w !== null ? w : this.panelSize.width;
38415 h = h !== null ? h : this.panelSize.height;
38417 if(this.activePanel){
38418 var el = this.activePanel.getEl();
38419 w = w !== null ? w : el.getWidth();
38420 h = h !== null ? h : el.getHeight();
38421 this.panelSize = {width: w, height: h};
38422 this.activePanel.setSize(w, h);
38424 if(Roo.isIE && this.tabs){
38425 this.tabs.el.repaint();
38430 * Returns the container element for this region.
38431 * @return {Roo.Element}
38433 getEl : function(){
38438 * Hides this region.
38441 //if(!this.collapsed){
38442 this.el.dom.style.left = "-2000px";
38445 // this.collapsedEl.dom.style.left = "-2000px";
38446 // this.collapsedEl.hide();
38448 this.visible = false;
38449 this.fireEvent("visibilitychange", this, false);
38453 * Shows this region if it was previously hidden.
38456 //if(!this.collapsed){
38459 // this.collapsedEl.show();
38461 this.visible = true;
38462 this.fireEvent("visibilitychange", this, true);
38465 closeClicked : function(){
38466 if(this.activePanel){
38467 this.remove(this.activePanel);
38471 collapseClick : function(e){
38473 e.stopPropagation();
38476 e.stopPropagation();
38482 * Collapses this region.
38483 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38486 collapse : function(skipAnim, skipCheck = false){
38487 if(this.collapsed) {
38491 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38493 this.collapsed = true;
38495 this.split.el.hide();
38497 if(this.config.animate && skipAnim !== true){
38498 this.fireEvent("invalidated", this);
38499 this.animateCollapse();
38501 this.el.setLocation(-20000,-20000);
38503 this.collapsedEl.show();
38504 this.fireEvent("collapsed", this);
38505 this.fireEvent("invalidated", this);
38511 animateCollapse : function(){
38516 * Expands this region if it was previously collapsed.
38517 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38518 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38521 expand : function(e, skipAnim){
38523 e.stopPropagation();
38525 if(!this.collapsed || this.el.hasActiveFx()) {
38529 this.afterSlideIn();
38532 this.collapsed = false;
38533 if(this.config.animate && skipAnim !== true){
38534 this.animateExpand();
38538 this.split.el.show();
38540 this.collapsedEl.setLocation(-2000,-2000);
38541 this.collapsedEl.hide();
38542 this.fireEvent("invalidated", this);
38543 this.fireEvent("expanded", this);
38547 animateExpand : function(){
38551 initTabs : function()
38553 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38555 var ts = new Roo.bootstrap.panel.Tabs({
38556 el: this.bodyEl.dom,
38558 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38559 disableTooltips: this.config.disableTabTips,
38560 toolbar : this.config.toolbar
38563 if(this.config.hideTabs){
38564 ts.stripWrap.setDisplayed(false);
38567 ts.resizeTabs = this.config.resizeTabs === true;
38568 ts.minTabWidth = this.config.minTabWidth || 40;
38569 ts.maxTabWidth = this.config.maxTabWidth || 250;
38570 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38571 ts.monitorResize = false;
38572 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38573 ts.bodyEl.addClass('roo-layout-tabs-body');
38574 this.panels.each(this.initPanelAsTab, this);
38577 initPanelAsTab : function(panel){
38578 var ti = this.tabs.addTab(
38582 this.config.closeOnTab && panel.isClosable(),
38585 if(panel.tabTip !== undefined){
38586 ti.setTooltip(panel.tabTip);
38588 ti.on("activate", function(){
38589 this.setActivePanel(panel);
38592 if(this.config.closeOnTab){
38593 ti.on("beforeclose", function(t, e){
38595 this.remove(panel);
38599 panel.tabItem = ti;
38604 updatePanelTitle : function(panel, title)
38606 if(this.activePanel == panel){
38607 this.updateTitle(title);
38610 var ti = this.tabs.getTab(panel.getEl().id);
38612 if(panel.tabTip !== undefined){
38613 ti.setTooltip(panel.tabTip);
38618 updateTitle : function(title){
38619 if(this.titleTextEl && !this.config.title){
38620 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38624 setActivePanel : function(panel)
38626 panel = this.getPanel(panel);
38627 if(this.activePanel && this.activePanel != panel){
38628 if(this.activePanel.setActiveState(false) === false){
38632 this.activePanel = panel;
38633 panel.setActiveState(true);
38634 if(this.panelSize){
38635 panel.setSize(this.panelSize.width, this.panelSize.height);
38638 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38640 this.updateTitle(panel.getTitle());
38642 this.fireEvent("invalidated", this);
38644 this.fireEvent("panelactivated", this, panel);
38648 * Shows the specified panel.
38649 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38650 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38652 showPanel : function(panel)
38654 panel = this.getPanel(panel);
38657 var tab = this.tabs.getTab(panel.getEl().id);
38658 if(tab.isHidden()){
38659 this.tabs.unhideTab(tab.id);
38663 this.setActivePanel(panel);
38670 * Get the active panel for this region.
38671 * @return {Roo.ContentPanel} The active panel or null
38673 getActivePanel : function(){
38674 return this.activePanel;
38677 validateVisibility : function(){
38678 if(this.panels.getCount() < 1){
38679 this.updateTitle(" ");
38680 this.closeBtn.hide();
38683 if(!this.isVisible()){
38690 * Adds the passed ContentPanel(s) to this region.
38691 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38692 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38694 add : function(panel)
38696 if(arguments.length > 1){
38697 for(var i = 0, len = arguments.length; i < len; i++) {
38698 this.add(arguments[i]);
38703 // if we have not been rendered yet, then we can not really do much of this..
38704 if (!this.bodyEl) {
38705 this.unrendered_panels.push(panel);
38712 if(this.hasPanel(panel)){
38713 this.showPanel(panel);
38716 panel.setRegion(this);
38717 this.panels.add(panel);
38718 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38719 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38720 // and hide them... ???
38721 this.bodyEl.dom.appendChild(panel.getEl().dom);
38722 if(panel.background !== true){
38723 this.setActivePanel(panel);
38725 this.fireEvent("paneladded", this, panel);
38732 this.initPanelAsTab(panel);
38736 if(panel.background !== true){
38737 this.tabs.activate(panel.getEl().id);
38739 this.fireEvent("paneladded", this, panel);
38744 * Hides the tab for the specified panel.
38745 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38747 hidePanel : function(panel){
38748 if(this.tabs && (panel = this.getPanel(panel))){
38749 this.tabs.hideTab(panel.getEl().id);
38754 * Unhides the tab for a previously hidden panel.
38755 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38757 unhidePanel : function(panel){
38758 if(this.tabs && (panel = this.getPanel(panel))){
38759 this.tabs.unhideTab(panel.getEl().id);
38763 clearPanels : function(){
38764 while(this.panels.getCount() > 0){
38765 this.remove(this.panels.first());
38770 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38771 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38772 * @param {Boolean} preservePanel Overrides the config preservePanel option
38773 * @return {Roo.ContentPanel} The panel that was removed
38775 remove : function(panel, preservePanel)
38777 panel = this.getPanel(panel);
38782 this.fireEvent("beforeremove", this, panel, e);
38783 if(e.cancel === true){
38786 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38787 var panelId = panel.getId();
38788 this.panels.removeKey(panelId);
38790 document.body.appendChild(panel.getEl().dom);
38793 this.tabs.removeTab(panel.getEl().id);
38794 }else if (!preservePanel){
38795 this.bodyEl.dom.removeChild(panel.getEl().dom);
38797 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38798 var p = this.panels.first();
38799 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38800 tempEl.appendChild(p.getEl().dom);
38801 this.bodyEl.update("");
38802 this.bodyEl.dom.appendChild(p.getEl().dom);
38804 this.updateTitle(p.getTitle());
38806 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38807 this.setActivePanel(p);
38809 panel.setRegion(null);
38810 if(this.activePanel == panel){
38811 this.activePanel = null;
38813 if(this.config.autoDestroy !== false && preservePanel !== true){
38814 try{panel.destroy();}catch(e){}
38816 this.fireEvent("panelremoved", this, panel);
38821 * Returns the TabPanel component used by this region
38822 * @return {Roo.TabPanel}
38824 getTabs : function(){
38828 createTool : function(parentEl, className){
38829 var btn = Roo.DomHelper.append(parentEl, {
38831 cls: "x-layout-tools-button",
38834 cls: "roo-layout-tools-button-inner " + className,
38838 btn.addClassOnOver("roo-layout-tools-button-over");
38843 * Ext JS Library 1.1.1
38844 * Copyright(c) 2006-2007, Ext JS, LLC.
38846 * Originally Released Under LGPL - original licence link has changed is not relivant.
38849 * <script type="text/javascript">
38855 * @class Roo.SplitLayoutRegion
38856 * @extends Roo.LayoutRegion
38857 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38859 Roo.bootstrap.layout.Split = function(config){
38860 this.cursor = config.cursor;
38861 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38864 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38866 splitTip : "Drag to resize.",
38867 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38868 useSplitTips : false,
38870 applyConfig : function(config){
38871 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38874 onRender : function(ctr,pos) {
38876 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38877 if(!this.config.split){
38882 var splitEl = Roo.DomHelper.append(ctr.dom, {
38884 id: this.el.id + "-split",
38885 cls: "roo-layout-split roo-layout-split-"+this.position,
38888 /** The SplitBar for this region
38889 * @type Roo.SplitBar */
38890 // does not exist yet...
38891 Roo.log([this.position, this.orientation]);
38893 this.split = new Roo.bootstrap.SplitBar({
38894 dragElement : splitEl,
38895 resizingElement: this.el,
38896 orientation : this.orientation
38899 this.split.on("moved", this.onSplitMove, this);
38900 this.split.useShim = this.config.useShim === true;
38901 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38902 if(this.useSplitTips){
38903 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38905 //if(config.collapsible){
38906 // this.split.el.on("dblclick", this.collapse, this);
38909 if(typeof this.config.minSize != "undefined"){
38910 this.split.minSize = this.config.minSize;
38912 if(typeof this.config.maxSize != "undefined"){
38913 this.split.maxSize = this.config.maxSize;
38915 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38916 this.hideSplitter();
38921 getHMaxSize : function(){
38922 var cmax = this.config.maxSize || 10000;
38923 var center = this.mgr.getRegion("center");
38924 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38927 getVMaxSize : function(){
38928 var cmax = this.config.maxSize || 10000;
38929 var center = this.mgr.getRegion("center");
38930 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38933 onSplitMove : function(split, newSize){
38934 this.fireEvent("resized", this, newSize);
38938 * Returns the {@link Roo.SplitBar} for this region.
38939 * @return {Roo.SplitBar}
38941 getSplitBar : function(){
38946 this.hideSplitter();
38947 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38950 hideSplitter : function(){
38952 this.split.el.setLocation(-2000,-2000);
38953 this.split.el.hide();
38959 this.split.el.show();
38961 Roo.bootstrap.layout.Split.superclass.show.call(this);
38964 beforeSlide: function(){
38965 if(Roo.isGecko){// firefox overflow auto bug workaround
38966 this.bodyEl.clip();
38968 this.tabs.bodyEl.clip();
38970 if(this.activePanel){
38971 this.activePanel.getEl().clip();
38973 if(this.activePanel.beforeSlide){
38974 this.activePanel.beforeSlide();
38980 afterSlide : function(){
38981 if(Roo.isGecko){// firefox overflow auto bug workaround
38982 this.bodyEl.unclip();
38984 this.tabs.bodyEl.unclip();
38986 if(this.activePanel){
38987 this.activePanel.getEl().unclip();
38988 if(this.activePanel.afterSlide){
38989 this.activePanel.afterSlide();
38995 initAutoHide : function(){
38996 if(this.autoHide !== false){
38997 if(!this.autoHideHd){
38998 var st = new Roo.util.DelayedTask(this.slideIn, this);
38999 this.autoHideHd = {
39000 "mouseout": function(e){
39001 if(!e.within(this.el, true)){
39005 "mouseover" : function(e){
39011 this.el.on(this.autoHideHd);
39015 clearAutoHide : function(){
39016 if(this.autoHide !== false){
39017 this.el.un("mouseout", this.autoHideHd.mouseout);
39018 this.el.un("mouseover", this.autoHideHd.mouseover);
39022 clearMonitor : function(){
39023 Roo.get(document).un("click", this.slideInIf, this);
39026 // these names are backwards but not changed for compat
39027 slideOut : function(){
39028 if(this.isSlid || this.el.hasActiveFx()){
39031 this.isSlid = true;
39032 if(this.collapseBtn){
39033 this.collapseBtn.hide();
39035 this.closeBtnState = this.closeBtn.getStyle('display');
39036 this.closeBtn.hide();
39038 this.stickBtn.show();
39041 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39042 this.beforeSlide();
39043 this.el.setStyle("z-index", 10001);
39044 this.el.slideIn(this.getSlideAnchor(), {
39045 callback: function(){
39047 this.initAutoHide();
39048 Roo.get(document).on("click", this.slideInIf, this);
39049 this.fireEvent("slideshow", this);
39056 afterSlideIn : function(){
39057 this.clearAutoHide();
39058 this.isSlid = false;
39059 this.clearMonitor();
39060 this.el.setStyle("z-index", "");
39061 if(this.collapseBtn){
39062 this.collapseBtn.show();
39064 this.closeBtn.setStyle('display', this.closeBtnState);
39066 this.stickBtn.hide();
39068 this.fireEvent("slidehide", this);
39071 slideIn : function(cb){
39072 if(!this.isSlid || this.el.hasActiveFx()){
39076 this.isSlid = false;
39077 this.beforeSlide();
39078 this.el.slideOut(this.getSlideAnchor(), {
39079 callback: function(){
39080 this.el.setLeftTop(-10000, -10000);
39082 this.afterSlideIn();
39090 slideInIf : function(e){
39091 if(!e.within(this.el)){
39096 animateCollapse : function(){
39097 this.beforeSlide();
39098 this.el.setStyle("z-index", 20000);
39099 var anchor = this.getSlideAnchor();
39100 this.el.slideOut(anchor, {
39101 callback : function(){
39102 this.el.setStyle("z-index", "");
39103 this.collapsedEl.slideIn(anchor, {duration:.3});
39105 this.el.setLocation(-10000,-10000);
39107 this.fireEvent("collapsed", this);
39114 animateExpand : function(){
39115 this.beforeSlide();
39116 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39117 this.el.setStyle("z-index", 20000);
39118 this.collapsedEl.hide({
39121 this.el.slideIn(this.getSlideAnchor(), {
39122 callback : function(){
39123 this.el.setStyle("z-index", "");
39126 this.split.el.show();
39128 this.fireEvent("invalidated", this);
39129 this.fireEvent("expanded", this);
39157 getAnchor : function(){
39158 return this.anchors[this.position];
39161 getCollapseAnchor : function(){
39162 return this.canchors[this.position];
39165 getSlideAnchor : function(){
39166 return this.sanchors[this.position];
39169 getAlignAdj : function(){
39170 var cm = this.cmargins;
39171 switch(this.position){
39187 getExpandAdj : function(){
39188 var c = this.collapsedEl, cm = this.cmargins;
39189 switch(this.position){
39191 return [-(cm.right+c.getWidth()+cm.left), 0];
39194 return [cm.right+c.getWidth()+cm.left, 0];
39197 return [0, -(cm.top+cm.bottom+c.getHeight())];
39200 return [0, cm.top+cm.bottom+c.getHeight()];
39206 * Ext JS Library 1.1.1
39207 * Copyright(c) 2006-2007, Ext JS, LLC.
39209 * Originally Released Under LGPL - original licence link has changed is not relivant.
39212 * <script type="text/javascript">
39215 * These classes are private internal classes
39217 Roo.bootstrap.layout.Center = function(config){
39218 config.region = "center";
39219 Roo.bootstrap.layout.Region.call(this, config);
39220 this.visible = true;
39221 this.minWidth = config.minWidth || 20;
39222 this.minHeight = config.minHeight || 20;
39225 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39227 // center panel can't be hidden
39231 // center panel can't be hidden
39234 getMinWidth: function(){
39235 return this.minWidth;
39238 getMinHeight: function(){
39239 return this.minHeight;
39253 Roo.bootstrap.layout.North = function(config)
39255 config.region = 'north';
39256 config.cursor = 'n-resize';
39258 Roo.bootstrap.layout.Split.call(this, config);
39262 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39263 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39264 this.split.el.addClass("roo-layout-split-v");
39266 var size = config.initialSize || config.height;
39267 if(typeof size != "undefined"){
39268 this.el.setHeight(size);
39271 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39273 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39277 getBox : function(){
39278 if(this.collapsed){
39279 return this.collapsedEl.getBox();
39281 var box = this.el.getBox();
39283 box.height += this.split.el.getHeight();
39288 updateBox : function(box){
39289 if(this.split && !this.collapsed){
39290 box.height -= this.split.el.getHeight();
39291 this.split.el.setLeft(box.x);
39292 this.split.el.setTop(box.y+box.height);
39293 this.split.el.setWidth(box.width);
39295 if(this.collapsed){
39296 this.updateBody(box.width, null);
39298 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39306 Roo.bootstrap.layout.South = function(config){
39307 config.region = 'south';
39308 config.cursor = 's-resize';
39309 Roo.bootstrap.layout.Split.call(this, config);
39311 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39312 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39313 this.split.el.addClass("roo-layout-split-v");
39315 var size = config.initialSize || config.height;
39316 if(typeof size != "undefined"){
39317 this.el.setHeight(size);
39321 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39322 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39323 getBox : function(){
39324 if(this.collapsed){
39325 return this.collapsedEl.getBox();
39327 var box = this.el.getBox();
39329 var sh = this.split.el.getHeight();
39336 updateBox : function(box){
39337 if(this.split && !this.collapsed){
39338 var sh = this.split.el.getHeight();
39341 this.split.el.setLeft(box.x);
39342 this.split.el.setTop(box.y-sh);
39343 this.split.el.setWidth(box.width);
39345 if(this.collapsed){
39346 this.updateBody(box.width, null);
39348 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39352 Roo.bootstrap.layout.East = function(config){
39353 config.region = "east";
39354 config.cursor = "e-resize";
39355 Roo.bootstrap.layout.Split.call(this, config);
39357 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39358 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39359 this.split.el.addClass("roo-layout-split-h");
39361 var size = config.initialSize || config.width;
39362 if(typeof size != "undefined"){
39363 this.el.setWidth(size);
39366 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39367 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39368 getBox : function(){
39369 if(this.collapsed){
39370 return this.collapsedEl.getBox();
39372 var box = this.el.getBox();
39374 var sw = this.split.el.getWidth();
39381 updateBox : function(box){
39382 if(this.split && !this.collapsed){
39383 var sw = this.split.el.getWidth();
39385 this.split.el.setLeft(box.x);
39386 this.split.el.setTop(box.y);
39387 this.split.el.setHeight(box.height);
39390 if(this.collapsed){
39391 this.updateBody(null, box.height);
39393 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39397 Roo.bootstrap.layout.West = function(config){
39398 config.region = "west";
39399 config.cursor = "w-resize";
39401 Roo.bootstrap.layout.Split.call(this, config);
39403 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39404 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39405 this.split.el.addClass("roo-layout-split-h");
39409 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39410 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39412 onRender: function(ctr, pos)
39414 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39415 var size = this.config.initialSize || this.config.width;
39416 if(typeof size != "undefined"){
39417 this.el.setWidth(size);
39421 getBox : function(){
39422 if(this.collapsed){
39423 return this.collapsedEl.getBox();
39425 var box = this.el.getBox();
39427 box.width += this.split.el.getWidth();
39432 updateBox : function(box){
39433 if(this.split && !this.collapsed){
39434 var sw = this.split.el.getWidth();
39436 this.split.el.setLeft(box.x+box.width);
39437 this.split.el.setTop(box.y);
39438 this.split.el.setHeight(box.height);
39440 if(this.collapsed){
39441 this.updateBody(null, box.height);
39443 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39445 });Roo.namespace("Roo.bootstrap.panel");/*
39447 * Ext JS Library 1.1.1
39448 * Copyright(c) 2006-2007, Ext JS, LLC.
39450 * Originally Released Under LGPL - original licence link has changed is not relivant.
39453 * <script type="text/javascript">
39456 * @class Roo.ContentPanel
39457 * @extends Roo.util.Observable
39458 * A basic ContentPanel element.
39459 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39460 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39461 * @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
39462 * @cfg {Boolean} closable True if the panel can be closed/removed
39463 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39464 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39465 * @cfg {Toolbar} toolbar A toolbar for this panel
39466 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39467 * @cfg {String} title The title for this panel
39468 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39469 * @cfg {String} url Calls {@link #setUrl} with this value
39470 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39471 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39472 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39473 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39474 * @cfg {Boolean} badges render the badges
39475 * @cfg {String} cls extra classes to use
39476 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39479 * Create a new ContentPanel.
39480 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39481 * @param {String/Object} config A string to set only the title or a config object
39482 * @param {String} content (optional) Set the HTML content for this panel
39483 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39485 Roo.bootstrap.panel.Content = function( config){
39487 this.tpl = config.tpl || false;
39489 var el = config.el;
39490 var content = config.content;
39492 if(config.autoCreate){ // xtype is available if this is called from factory
39495 this.el = Roo.get(el);
39496 if(!this.el && config && config.autoCreate){
39497 if(typeof config.autoCreate == "object"){
39498 if(!config.autoCreate.id){
39499 config.autoCreate.id = config.id||el;
39501 this.el = Roo.DomHelper.append(document.body,
39502 config.autoCreate, true);
39506 cls: (config.cls || '') +
39507 (config.background ? ' bg-' + config.background : '') +
39508 " roo-layout-inactive-content",
39512 elcfg.html = config.html;
39516 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39519 this.closable = false;
39520 this.loaded = false;
39521 this.active = false;
39524 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39526 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39528 this.wrapEl = this.el; //this.el.wrap();
39530 if (config.toolbar.items) {
39531 ti = config.toolbar.items ;
39532 delete config.toolbar.items ;
39536 this.toolbar.render(this.wrapEl, 'before');
39537 for(var i =0;i < ti.length;i++) {
39538 // Roo.log(['add child', items[i]]);
39539 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39541 this.toolbar.items = nitems;
39542 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39543 delete config.toolbar;
39547 // xtype created footer. - not sure if will work as we normally have to render first..
39548 if (this.footer && !this.footer.el && this.footer.xtype) {
39549 if (!this.wrapEl) {
39550 this.wrapEl = this.el.wrap();
39553 this.footer.container = this.wrapEl.createChild();
39555 this.footer = Roo.factory(this.footer, Roo);
39560 if(typeof config == "string"){
39561 this.title = config;
39563 Roo.apply(this, config);
39567 this.resizeEl = Roo.get(this.resizeEl, true);
39569 this.resizeEl = this.el;
39571 // handle view.xtype
39579 * Fires when this panel is activated.
39580 * @param {Roo.ContentPanel} this
39584 * @event deactivate
39585 * Fires when this panel is activated.
39586 * @param {Roo.ContentPanel} this
39588 "deactivate" : true,
39592 * Fires when this panel is resized if fitToFrame is true.
39593 * @param {Roo.ContentPanel} this
39594 * @param {Number} width The width after any component adjustments
39595 * @param {Number} height The height after any component adjustments
39601 * Fires when this tab is created
39602 * @param {Roo.ContentPanel} this
39613 if(this.autoScroll){
39614 this.resizeEl.setStyle("overflow", "auto");
39616 // fix randome scrolling
39617 //this.el.on('scroll', function() {
39618 // Roo.log('fix random scolling');
39619 // this.scrollTo('top',0);
39622 content = content || this.content;
39624 this.setContent(content);
39626 if(config && config.url){
39627 this.setUrl(this.url, this.params, this.loadOnce);
39632 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39634 if (this.view && typeof(this.view.xtype) != 'undefined') {
39635 this.view.el = this.el.appendChild(document.createElement("div"));
39636 this.view = Roo.factory(this.view);
39637 this.view.render && this.view.render(false, '');
39641 this.fireEvent('render', this);
39644 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39651 setRegion : function(region){
39652 this.region = region;
39653 this.setActiveClass(region && !this.background);
39657 setActiveClass: function(state)
39660 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39661 this.el.setStyle('position','relative');
39663 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39664 this.el.setStyle('position', 'absolute');
39669 * Returns the toolbar for this Panel if one was configured.
39670 * @return {Roo.Toolbar}
39672 getToolbar : function(){
39673 return this.toolbar;
39676 setActiveState : function(active)
39678 this.active = active;
39679 this.setActiveClass(active);
39681 if(this.fireEvent("deactivate", this) === false){
39686 this.fireEvent("activate", this);
39690 * Updates this panel's element
39691 * @param {String} content The new content
39692 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39694 setContent : function(content, loadScripts){
39695 this.el.update(content, loadScripts);
39698 ignoreResize : function(w, h){
39699 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39702 this.lastSize = {width: w, height: h};
39707 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39708 * @return {Roo.UpdateManager} The UpdateManager
39710 getUpdateManager : function(){
39711 return this.el.getUpdateManager();
39714 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39715 * @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:
39718 url: "your-url.php",
39719 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39720 callback: yourFunction,
39721 scope: yourObject, //(optional scope)
39724 text: "Loading...",
39729 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39730 * 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.
39731 * @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}
39732 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39733 * @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.
39734 * @return {Roo.ContentPanel} this
39737 var um = this.el.getUpdateManager();
39738 um.update.apply(um, arguments);
39744 * 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.
39745 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39746 * @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)
39747 * @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)
39748 * @return {Roo.UpdateManager} The UpdateManager
39750 setUrl : function(url, params, loadOnce){
39751 if(this.refreshDelegate){
39752 this.removeListener("activate", this.refreshDelegate);
39754 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39755 this.on("activate", this.refreshDelegate);
39756 return this.el.getUpdateManager();
39759 _handleRefresh : function(url, params, loadOnce){
39760 if(!loadOnce || !this.loaded){
39761 var updater = this.el.getUpdateManager();
39762 updater.update(url, params, this._setLoaded.createDelegate(this));
39766 _setLoaded : function(){
39767 this.loaded = true;
39771 * Returns this panel's id
39774 getId : function(){
39779 * Returns this panel's element - used by regiosn to add.
39780 * @return {Roo.Element}
39782 getEl : function(){
39783 return this.wrapEl || this.el;
39788 adjustForComponents : function(width, height)
39790 //Roo.log('adjustForComponents ');
39791 if(this.resizeEl != this.el){
39792 width -= this.el.getFrameWidth('lr');
39793 height -= this.el.getFrameWidth('tb');
39796 var te = this.toolbar.getEl();
39797 te.setWidth(width);
39798 height -= te.getHeight();
39801 var te = this.footer.getEl();
39802 te.setWidth(width);
39803 height -= te.getHeight();
39807 if(this.adjustments){
39808 width += this.adjustments[0];
39809 height += this.adjustments[1];
39811 return {"width": width, "height": height};
39814 setSize : function(width, height){
39815 if(this.fitToFrame && !this.ignoreResize(width, height)){
39816 if(this.fitContainer && this.resizeEl != this.el){
39817 this.el.setSize(width, height);
39819 var size = this.adjustForComponents(width, height);
39820 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39821 this.fireEvent('resize', this, size.width, size.height);
39826 * Returns this panel's title
39829 getTitle : function(){
39831 if (typeof(this.title) != 'object') {
39836 for (var k in this.title) {
39837 if (!this.title.hasOwnProperty(k)) {
39841 if (k.indexOf('-') >= 0) {
39842 var s = k.split('-');
39843 for (var i = 0; i<s.length; i++) {
39844 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39847 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39854 * Set this panel's title
39855 * @param {String} title
39857 setTitle : function(title){
39858 this.title = title;
39860 this.region.updatePanelTitle(this, title);
39865 * Returns true is this panel was configured to be closable
39866 * @return {Boolean}
39868 isClosable : function(){
39869 return this.closable;
39872 beforeSlide : function(){
39874 this.resizeEl.clip();
39877 afterSlide : function(){
39879 this.resizeEl.unclip();
39883 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39884 * Will fail silently if the {@link #setUrl} method has not been called.
39885 * This does not activate the panel, just updates its content.
39887 refresh : function(){
39888 if(this.refreshDelegate){
39889 this.loaded = false;
39890 this.refreshDelegate();
39895 * Destroys this panel
39897 destroy : function(){
39898 this.el.removeAllListeners();
39899 var tempEl = document.createElement("span");
39900 tempEl.appendChild(this.el.dom);
39901 tempEl.innerHTML = "";
39907 * form - if the content panel contains a form - this is a reference to it.
39908 * @type {Roo.form.Form}
39912 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39913 * This contains a reference to it.
39919 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39929 * @param {Object} cfg Xtype definition of item to add.
39933 getChildContainer: function () {
39934 return this.getEl();
39939 var ret = new Roo.factory(cfg);
39944 if (cfg.xtype.match(/^Form$/)) {
39947 //if (this.footer) {
39948 // el = this.footer.container.insertSibling(false, 'before');
39950 el = this.el.createChild();
39953 this.form = new Roo.form.Form(cfg);
39956 if ( this.form.allItems.length) {
39957 this.form.render(el.dom);
39961 // should only have one of theses..
39962 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39963 // views.. should not be just added - used named prop 'view''
39965 cfg.el = this.el.appendChild(document.createElement("div"));
39968 var ret = new Roo.factory(cfg);
39970 ret.render && ret.render(false, ''); // render blank..
39980 * @class Roo.bootstrap.panel.Grid
39981 * @extends Roo.bootstrap.panel.Content
39983 * Create a new GridPanel.
39984 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39985 * @param {Object} config A the config object
39991 Roo.bootstrap.panel.Grid = function(config)
39995 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39996 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39998 config.el = this.wrapper;
39999 //this.el = this.wrapper;
40001 if (config.container) {
40002 // ctor'ed from a Border/panel.grid
40005 this.wrapper.setStyle("overflow", "hidden");
40006 this.wrapper.addClass('roo-grid-container');
40011 if(config.toolbar){
40012 var tool_el = this.wrapper.createChild();
40013 this.toolbar = Roo.factory(config.toolbar);
40015 if (config.toolbar.items) {
40016 ti = config.toolbar.items ;
40017 delete config.toolbar.items ;
40021 this.toolbar.render(tool_el);
40022 for(var i =0;i < ti.length;i++) {
40023 // Roo.log(['add child', items[i]]);
40024 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40026 this.toolbar.items = nitems;
40028 delete config.toolbar;
40031 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40032 config.grid.scrollBody = true;;
40033 config.grid.monitorWindowResize = false; // turn off autosizing
40034 config.grid.autoHeight = false;
40035 config.grid.autoWidth = false;
40037 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40039 if (config.background) {
40040 // render grid on panel activation (if panel background)
40041 this.on('activate', function(gp) {
40042 if (!gp.grid.rendered) {
40043 gp.grid.render(this.wrapper);
40044 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40049 this.grid.render(this.wrapper);
40050 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40053 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40054 // ??? needed ??? config.el = this.wrapper;
40059 // xtype created footer. - not sure if will work as we normally have to render first..
40060 if (this.footer && !this.footer.el && this.footer.xtype) {
40062 var ctr = this.grid.getView().getFooterPanel(true);
40063 this.footer.dataSource = this.grid.dataSource;
40064 this.footer = Roo.factory(this.footer, Roo);
40065 this.footer.render(ctr);
40075 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40076 getId : function(){
40077 return this.grid.id;
40081 * Returns the grid for this panel
40082 * @return {Roo.bootstrap.Table}
40084 getGrid : function(){
40088 setSize : function(width, height){
40089 if(!this.ignoreResize(width, height)){
40090 var grid = this.grid;
40091 var size = this.adjustForComponents(width, height);
40092 // tfoot is not a footer?
40095 var gridel = grid.getGridEl();
40096 gridel.setSize(size.width, size.height);
40098 var tbd = grid.getGridEl().select('tbody', true).first();
40099 var thd = grid.getGridEl().select('thead',true).first();
40100 var tbf= grid.getGridEl().select('tfoot', true).first();
40103 size.height -= thd.getHeight();
40106 size.height -= thd.getHeight();
40109 tbd.setSize(size.width, size.height );
40110 // this is for the account management tab -seems to work there.
40111 var thd = grid.getGridEl().select('thead',true).first();
40113 // tbd.setSize(size.width, size.height - thd.getHeight());
40122 beforeSlide : function(){
40123 this.grid.getView().scroller.clip();
40126 afterSlide : function(){
40127 this.grid.getView().scroller.unclip();
40130 destroy : function(){
40131 this.grid.destroy();
40133 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40138 * @class Roo.bootstrap.panel.Nest
40139 * @extends Roo.bootstrap.panel.Content
40141 * Create a new Panel, that can contain a layout.Border.
40144 * @param {Roo.BorderLayout} layout The layout for this panel
40145 * @param {String/Object} config A string to set only the title or a config object
40147 Roo.bootstrap.panel.Nest = function(config)
40149 // construct with only one argument..
40150 /* FIXME - implement nicer consturctors
40151 if (layout.layout) {
40153 layout = config.layout;
40154 delete config.layout;
40156 if (layout.xtype && !layout.getEl) {
40157 // then layout needs constructing..
40158 layout = Roo.factory(layout, Roo);
40162 config.el = config.layout.getEl();
40164 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40166 config.layout.monitorWindowResize = false; // turn off autosizing
40167 this.layout = config.layout;
40168 this.layout.getEl().addClass("roo-layout-nested-layout");
40169 this.layout.parent = this;
40176 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40178 setSize : function(width, height){
40179 if(!this.ignoreResize(width, height)){
40180 var size = this.adjustForComponents(width, height);
40181 var el = this.layout.getEl();
40182 if (size.height < 1) {
40183 el.setWidth(size.width);
40185 el.setSize(size.width, size.height);
40187 var touch = el.dom.offsetWidth;
40188 this.layout.layout();
40189 // ie requires a double layout on the first pass
40190 if(Roo.isIE && !this.initialized){
40191 this.initialized = true;
40192 this.layout.layout();
40197 // activate all subpanels if not currently active..
40199 setActiveState : function(active){
40200 this.active = active;
40201 this.setActiveClass(active);
40204 this.fireEvent("deactivate", this);
40208 this.fireEvent("activate", this);
40209 // not sure if this should happen before or after..
40210 if (!this.layout) {
40211 return; // should not happen..
40214 for (var r in this.layout.regions) {
40215 reg = this.layout.getRegion(r);
40216 if (reg.getActivePanel()) {
40217 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40218 reg.setActivePanel(reg.getActivePanel());
40221 if (!reg.panels.length) {
40224 reg.showPanel(reg.getPanel(0));
40233 * Returns the nested BorderLayout for this panel
40234 * @return {Roo.BorderLayout}
40236 getLayout : function(){
40237 return this.layout;
40241 * Adds a xtype elements to the layout of the nested panel
40245 xtype : 'ContentPanel',
40252 xtype : 'NestedLayoutPanel',
40258 items : [ ... list of content panels or nested layout panels.. ]
40262 * @param {Object} cfg Xtype definition of item to add.
40264 addxtype : function(cfg) {
40265 return this.layout.addxtype(cfg);
40270 * Ext JS Library 1.1.1
40271 * Copyright(c) 2006-2007, Ext JS, LLC.
40273 * Originally Released Under LGPL - original licence link has changed is not relivant.
40276 * <script type="text/javascript">
40279 * @class Roo.TabPanel
40280 * @extends Roo.util.Observable
40281 * A lightweight tab container.
40285 // basic tabs 1, built from existing content
40286 var tabs = new Roo.TabPanel("tabs1");
40287 tabs.addTab("script", "View Script");
40288 tabs.addTab("markup", "View Markup");
40289 tabs.activate("script");
40291 // more advanced tabs, built from javascript
40292 var jtabs = new Roo.TabPanel("jtabs");
40293 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40295 // set up the UpdateManager
40296 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40297 var updater = tab2.getUpdateManager();
40298 updater.setDefaultUrl("ajax1.htm");
40299 tab2.on('activate', updater.refresh, updater, true);
40301 // Use setUrl for Ajax loading
40302 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40303 tab3.setUrl("ajax2.htm", null, true);
40306 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40309 jtabs.activate("jtabs-1");
40312 * Create a new TabPanel.
40313 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40314 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40316 Roo.bootstrap.panel.Tabs = function(config){
40318 * The container element for this TabPanel.
40319 * @type Roo.Element
40321 this.el = Roo.get(config.el);
40324 if(typeof config == "boolean"){
40325 this.tabPosition = config ? "bottom" : "top";
40327 Roo.apply(this, config);
40331 if(this.tabPosition == "bottom"){
40332 // if tabs are at the bottom = create the body first.
40333 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40334 this.el.addClass("roo-tabs-bottom");
40336 // next create the tabs holders
40338 if (this.tabPosition == "west"){
40340 var reg = this.region; // fake it..
40342 if (!reg.mgr.parent) {
40345 reg = reg.mgr.parent.region;
40347 Roo.log("got nest?");
40349 if (reg.mgr.getRegion('west')) {
40350 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40351 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40352 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40353 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40354 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40362 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40363 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40364 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40365 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40370 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40373 // finally - if tabs are at the top, then create the body last..
40374 if(this.tabPosition != "bottom"){
40375 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40376 * @type Roo.Element
40378 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40379 this.el.addClass("roo-tabs-top");
40383 this.bodyEl.setStyle("position", "relative");
40385 this.active = null;
40386 this.activateDelegate = this.activate.createDelegate(this);
40391 * Fires when the active tab changes
40392 * @param {Roo.TabPanel} this
40393 * @param {Roo.TabPanelItem} activePanel The new active tab
40397 * @event beforetabchange
40398 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40399 * @param {Roo.TabPanel} this
40400 * @param {Object} e Set cancel to true on this object to cancel the tab change
40401 * @param {Roo.TabPanelItem} tab The tab being changed to
40403 "beforetabchange" : true
40406 Roo.EventManager.onWindowResize(this.onResize, this);
40407 this.cpad = this.el.getPadding("lr");
40408 this.hiddenCount = 0;
40411 // toolbar on the tabbar support...
40412 if (this.toolbar) {
40413 alert("no toolbar support yet");
40414 this.toolbar = false;
40416 var tcfg = this.toolbar;
40417 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40418 this.toolbar = new Roo.Toolbar(tcfg);
40419 if (Roo.isSafari) {
40420 var tbl = tcfg.container.child('table', true);
40421 tbl.setAttribute('width', '100%');
40429 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40432 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40434 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40436 tabPosition : "top",
40438 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40440 currentTabWidth : 0,
40442 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40446 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40450 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40452 preferredTabWidth : 175,
40454 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40456 resizeTabs : false,
40458 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40460 monitorResize : true,
40462 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40464 toolbar : false, // set by caller..
40466 region : false, /// set by caller
40468 disableTooltips : true, // not used yet...
40471 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40472 * @param {String} id The id of the div to use <b>or create</b>
40473 * @param {String} text The text for the tab
40474 * @param {String} content (optional) Content to put in the TabPanelItem body
40475 * @param {Boolean} closable (optional) True to create a close icon on the tab
40476 * @return {Roo.TabPanelItem} The created TabPanelItem
40478 addTab : function(id, text, content, closable, tpl)
40480 var item = new Roo.bootstrap.panel.TabItem({
40484 closable : closable,
40487 this.addTabItem(item);
40489 item.setContent(content);
40495 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40496 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40497 * @return {Roo.TabPanelItem}
40499 getTab : function(id){
40500 return this.items[id];
40504 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40505 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40507 hideTab : function(id){
40508 var t = this.items[id];
40511 this.hiddenCount++;
40512 this.autoSizeTabs();
40517 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40518 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40520 unhideTab : function(id){
40521 var t = this.items[id];
40523 t.setHidden(false);
40524 this.hiddenCount--;
40525 this.autoSizeTabs();
40530 * Adds an existing {@link Roo.TabPanelItem}.
40531 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40533 addTabItem : function(item)
40535 this.items[item.id] = item;
40536 this.items.push(item);
40537 this.autoSizeTabs();
40538 // if(this.resizeTabs){
40539 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40540 // this.autoSizeTabs();
40542 // item.autoSize();
40547 * Removes a {@link Roo.TabPanelItem}.
40548 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40550 removeTab : function(id){
40551 var items = this.items;
40552 var tab = items[id];
40553 if(!tab) { return; }
40554 var index = items.indexOf(tab);
40555 if(this.active == tab && items.length > 1){
40556 var newTab = this.getNextAvailable(index);
40561 this.stripEl.dom.removeChild(tab.pnode.dom);
40562 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40563 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40565 items.splice(index, 1);
40566 delete this.items[tab.id];
40567 tab.fireEvent("close", tab);
40568 tab.purgeListeners();
40569 this.autoSizeTabs();
40572 getNextAvailable : function(start){
40573 var items = this.items;
40575 // look for a next tab that will slide over to
40576 // replace the one being removed
40577 while(index < items.length){
40578 var item = items[++index];
40579 if(item && !item.isHidden()){
40583 // if one isn't found select the previous tab (on the left)
40586 var item = items[--index];
40587 if(item && !item.isHidden()){
40595 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40596 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40598 disableTab : function(id){
40599 var tab = this.items[id];
40600 if(tab && this.active != tab){
40606 * Enables a {@link Roo.TabPanelItem} that is disabled.
40607 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40609 enableTab : function(id){
40610 var tab = this.items[id];
40615 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40616 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40617 * @return {Roo.TabPanelItem} The TabPanelItem.
40619 activate : function(id)
40621 //Roo.log('activite:' + id);
40623 var tab = this.items[id];
40627 if(tab == this.active || tab.disabled){
40631 this.fireEvent("beforetabchange", this, e, tab);
40632 if(e.cancel !== true && !tab.disabled){
40634 this.active.hide();
40636 this.active = this.items[id];
40637 this.active.show();
40638 this.fireEvent("tabchange", this, this.active);
40644 * Gets the active {@link Roo.TabPanelItem}.
40645 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40647 getActiveTab : function(){
40648 return this.active;
40652 * Updates the tab body element to fit the height of the container element
40653 * for overflow scrolling
40654 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40656 syncHeight : function(targetHeight){
40657 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40658 var bm = this.bodyEl.getMargins();
40659 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40660 this.bodyEl.setHeight(newHeight);
40664 onResize : function(){
40665 if(this.monitorResize){
40666 this.autoSizeTabs();
40671 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40673 beginUpdate : function(){
40674 this.updating = true;
40678 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40680 endUpdate : function(){
40681 this.updating = false;
40682 this.autoSizeTabs();
40686 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40688 autoSizeTabs : function()
40690 var count = this.items.length;
40691 var vcount = count - this.hiddenCount;
40694 this.stripEl.hide();
40696 this.stripEl.show();
40699 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40704 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40705 var availWidth = Math.floor(w / vcount);
40706 var b = this.stripBody;
40707 if(b.getWidth() > w){
40708 var tabs = this.items;
40709 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40710 if(availWidth < this.minTabWidth){
40711 /*if(!this.sleft){ // incomplete scrolling code
40712 this.createScrollButtons();
40715 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40718 if(this.currentTabWidth < this.preferredTabWidth){
40719 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40725 * Returns the number of tabs in this TabPanel.
40728 getCount : function(){
40729 return this.items.length;
40733 * Resizes all the tabs to the passed width
40734 * @param {Number} The new width
40736 setTabWidth : function(width){
40737 this.currentTabWidth = width;
40738 for(var i = 0, len = this.items.length; i < len; i++) {
40739 if(!this.items[i].isHidden()) {
40740 this.items[i].setWidth(width);
40746 * Destroys this TabPanel
40747 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40749 destroy : function(removeEl){
40750 Roo.EventManager.removeResizeListener(this.onResize, this);
40751 for(var i = 0, len = this.items.length; i < len; i++){
40752 this.items[i].purgeListeners();
40754 if(removeEl === true){
40755 this.el.update("");
40760 createStrip : function(container)
40762 var strip = document.createElement("nav");
40763 strip.className = Roo.bootstrap.version == 4 ?
40764 "navbar-light bg-light" :
40765 "navbar navbar-default"; //"x-tabs-wrap";
40766 container.appendChild(strip);
40770 createStripList : function(strip)
40772 // div wrapper for retard IE
40773 // returns the "tr" element.
40774 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40775 //'<div class="x-tabs-strip-wrap">'+
40776 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40777 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40778 return strip.firstChild; //.firstChild.firstChild.firstChild;
40780 createBody : function(container)
40782 var body = document.createElement("div");
40783 Roo.id(body, "tab-body");
40784 //Roo.fly(body).addClass("x-tabs-body");
40785 Roo.fly(body).addClass("tab-content");
40786 container.appendChild(body);
40789 createItemBody :function(bodyEl, id){
40790 var body = Roo.getDom(id);
40792 body = document.createElement("div");
40795 //Roo.fly(body).addClass("x-tabs-item-body");
40796 Roo.fly(body).addClass("tab-pane");
40797 bodyEl.insertBefore(body, bodyEl.firstChild);
40801 createStripElements : function(stripEl, text, closable, tpl)
40803 var td = document.createElement("li"); // was td..
40804 td.className = 'nav-item';
40806 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40809 stripEl.appendChild(td);
40811 td.className = "x-tabs-closable";
40812 if(!this.closeTpl){
40813 this.closeTpl = new Roo.Template(
40814 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40815 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40816 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40819 var el = this.closeTpl.overwrite(td, {"text": text});
40820 var close = el.getElementsByTagName("div")[0];
40821 var inner = el.getElementsByTagName("em")[0];
40822 return {"el": el, "close": close, "inner": inner};
40825 // not sure what this is..
40826 // if(!this.tabTpl){
40827 //this.tabTpl = new Roo.Template(
40828 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40829 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40831 // this.tabTpl = new Roo.Template(
40832 // '<a href="#">' +
40833 // '<span unselectable="on"' +
40834 // (this.disableTooltips ? '' : ' title="{text}"') +
40835 // ' >{text}</span></a>'
40841 var template = tpl || this.tabTpl || false;
40844 template = new Roo.Template(
40845 Roo.bootstrap.version == 4 ?
40847 '<a class="nav-link" href="#" unselectable="on"' +
40848 (this.disableTooltips ? '' : ' title="{text}"') +
40851 '<a class="nav-link" href="#">' +
40852 '<span unselectable="on"' +
40853 (this.disableTooltips ? '' : ' title="{text}"') +
40854 ' >{text}</span></a>'
40859 switch (typeof(template)) {
40863 template = new Roo.Template(template);
40869 var el = template.overwrite(td, {"text": text});
40871 var inner = el.getElementsByTagName("span")[0];
40873 return {"el": el, "inner": inner};
40881 * @class Roo.TabPanelItem
40882 * @extends Roo.util.Observable
40883 * Represents an individual item (tab plus body) in a TabPanel.
40884 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40885 * @param {String} id The id of this TabPanelItem
40886 * @param {String} text The text for the tab of this TabPanelItem
40887 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40889 Roo.bootstrap.panel.TabItem = function(config){
40891 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40892 * @type Roo.TabPanel
40894 this.tabPanel = config.panel;
40896 * The id for this TabPanelItem
40899 this.id = config.id;
40901 this.disabled = false;
40903 this.text = config.text;
40905 this.loaded = false;
40906 this.closable = config.closable;
40909 * The body element for this TabPanelItem.
40910 * @type Roo.Element
40912 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40913 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40914 this.bodyEl.setStyle("display", "block");
40915 this.bodyEl.setStyle("zoom", "1");
40916 //this.hideAction();
40918 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40920 this.el = Roo.get(els.el);
40921 this.inner = Roo.get(els.inner, true);
40922 this.textEl = Roo.bootstrap.version == 4 ?
40923 this.el : Roo.get(this.el.dom.firstChild, true);
40925 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40926 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40929 // this.el.on("mousedown", this.onTabMouseDown, this);
40930 this.el.on("click", this.onTabClick, this);
40932 if(config.closable){
40933 var c = Roo.get(els.close, true);
40934 c.dom.title = this.closeText;
40935 c.addClassOnOver("close-over");
40936 c.on("click", this.closeClick, this);
40942 * Fires when this tab becomes the active tab.
40943 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40944 * @param {Roo.TabPanelItem} this
40948 * @event beforeclose
40949 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40950 * @param {Roo.TabPanelItem} this
40951 * @param {Object} e Set cancel to true on this object to cancel the close.
40953 "beforeclose": true,
40956 * Fires when this tab is closed.
40957 * @param {Roo.TabPanelItem} this
40961 * @event deactivate
40962 * Fires when this tab is no longer the active tab.
40963 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40964 * @param {Roo.TabPanelItem} this
40966 "deactivate" : true
40968 this.hidden = false;
40970 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40973 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40975 purgeListeners : function(){
40976 Roo.util.Observable.prototype.purgeListeners.call(this);
40977 this.el.removeAllListeners();
40980 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40983 this.status_node.addClass("active");
40986 this.tabPanel.stripWrap.repaint();
40988 this.fireEvent("activate", this.tabPanel, this);
40992 * Returns true if this tab is the active tab.
40993 * @return {Boolean}
40995 isActive : function(){
40996 return this.tabPanel.getActiveTab() == this;
41000 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41003 this.status_node.removeClass("active");
41005 this.fireEvent("deactivate", this.tabPanel, this);
41008 hideAction : function(){
41009 this.bodyEl.hide();
41010 this.bodyEl.setStyle("position", "absolute");
41011 this.bodyEl.setLeft("-20000px");
41012 this.bodyEl.setTop("-20000px");
41015 showAction : function(){
41016 this.bodyEl.setStyle("position", "relative");
41017 this.bodyEl.setTop("");
41018 this.bodyEl.setLeft("");
41019 this.bodyEl.show();
41023 * Set the tooltip for the tab.
41024 * @param {String} tooltip The tab's tooltip
41026 setTooltip : function(text){
41027 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41028 this.textEl.dom.qtip = text;
41029 this.textEl.dom.removeAttribute('title');
41031 this.textEl.dom.title = text;
41035 onTabClick : function(e){
41036 e.preventDefault();
41037 this.tabPanel.activate(this.id);
41040 onTabMouseDown : function(e){
41041 e.preventDefault();
41042 this.tabPanel.activate(this.id);
41045 getWidth : function(){
41046 return this.inner.getWidth();
41049 setWidth : function(width){
41050 var iwidth = width - this.linode.getPadding("lr");
41051 this.inner.setWidth(iwidth);
41052 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41053 this.linode.setWidth(width);
41057 * Show or hide the tab
41058 * @param {Boolean} hidden True to hide or false to show.
41060 setHidden : function(hidden){
41061 this.hidden = hidden;
41062 this.linode.setStyle("display", hidden ? "none" : "");
41066 * Returns true if this tab is "hidden"
41067 * @return {Boolean}
41069 isHidden : function(){
41070 return this.hidden;
41074 * Returns the text for this tab
41077 getText : function(){
41081 autoSize : function(){
41082 //this.el.beginMeasure();
41083 this.textEl.setWidth(1);
41085 * #2804 [new] Tabs in Roojs
41086 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41088 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41089 //this.el.endMeasure();
41093 * Sets the text for the tab (Note: this also sets the tooltip text)
41094 * @param {String} text The tab's text and tooltip
41096 setText : function(text){
41098 this.textEl.update(text);
41099 this.setTooltip(text);
41100 //if(!this.tabPanel.resizeTabs){
41101 // this.autoSize();
41105 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41107 activate : function(){
41108 this.tabPanel.activate(this.id);
41112 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41114 disable : function(){
41115 if(this.tabPanel.active != this){
41116 this.disabled = true;
41117 this.status_node.addClass("disabled");
41122 * Enables this TabPanelItem if it was previously disabled.
41124 enable : function(){
41125 this.disabled = false;
41126 this.status_node.removeClass("disabled");
41130 * Sets the content for this TabPanelItem.
41131 * @param {String} content The content
41132 * @param {Boolean} loadScripts true to look for and load scripts
41134 setContent : function(content, loadScripts){
41135 this.bodyEl.update(content, loadScripts);
41139 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41140 * @return {Roo.UpdateManager} The UpdateManager
41142 getUpdateManager : function(){
41143 return this.bodyEl.getUpdateManager();
41147 * Set a URL to be used to load the content for this TabPanelItem.
41148 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41149 * @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)
41150 * @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)
41151 * @return {Roo.UpdateManager} The UpdateManager
41153 setUrl : function(url, params, loadOnce){
41154 if(this.refreshDelegate){
41155 this.un('activate', this.refreshDelegate);
41157 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41158 this.on("activate", this.refreshDelegate);
41159 return this.bodyEl.getUpdateManager();
41163 _handleRefresh : function(url, params, loadOnce){
41164 if(!loadOnce || !this.loaded){
41165 var updater = this.bodyEl.getUpdateManager();
41166 updater.update(url, params, this._setLoaded.createDelegate(this));
41171 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41172 * Will fail silently if the setUrl method has not been called.
41173 * This does not activate the panel, just updates its content.
41175 refresh : function(){
41176 if(this.refreshDelegate){
41177 this.loaded = false;
41178 this.refreshDelegate();
41183 _setLoaded : function(){
41184 this.loaded = true;
41188 closeClick : function(e){
41191 this.fireEvent("beforeclose", this, o);
41192 if(o.cancel !== true){
41193 this.tabPanel.removeTab(this.id);
41197 * The text displayed in the tooltip for the close icon.
41200 closeText : "Close this tab"
41203 * This script refer to:
41204 * Title: International Telephone Input
41205 * Author: Jack O'Connor
41206 * Code version: v12.1.12
41207 * Availability: https://github.com/jackocnr/intl-tel-input.git
41210 Roo.bootstrap.PhoneInputData = function() {
41213 "Afghanistan (افغانستان)",
41218 "Albania (Shqipëri)",
41223 "Algeria (الجزائر)",
41248 "Antigua and Barbuda",
41258 "Armenia (Հայաստան)",
41274 "Austria (Österreich)",
41279 "Azerbaijan (Azərbaycan)",
41289 "Bahrain (البحرين)",
41294 "Bangladesh (বাংলাদেশ)",
41304 "Belarus (Беларусь)",
41309 "Belgium (België)",
41339 "Bosnia and Herzegovina (Босна и Херцеговина)",
41354 "British Indian Ocean Territory",
41359 "British Virgin Islands",
41369 "Bulgaria (България)",
41379 "Burundi (Uburundi)",
41384 "Cambodia (កម្ពុជា)",
41389 "Cameroon (Cameroun)",
41398 ["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"]
41401 "Cape Verde (Kabu Verdi)",
41406 "Caribbean Netherlands",
41417 "Central African Republic (République centrafricaine)",
41437 "Christmas Island",
41443 "Cocos (Keeling) Islands",
41454 "Comoros (جزر القمر)",
41459 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41464 "Congo (Republic) (Congo-Brazzaville)",
41484 "Croatia (Hrvatska)",
41505 "Czech Republic (Česká republika)",
41510 "Denmark (Danmark)",
41525 "Dominican Republic (República Dominicana)",
41529 ["809", "829", "849"]
41547 "Equatorial Guinea (Guinea Ecuatorial)",
41567 "Falkland Islands (Islas Malvinas)",
41572 "Faroe Islands (Føroyar)",
41593 "French Guiana (Guyane française)",
41598 "French Polynesia (Polynésie française)",
41613 "Georgia (საქართველო)",
41618 "Germany (Deutschland)",
41638 "Greenland (Kalaallit Nunaat)",
41675 "Guinea-Bissau (Guiné Bissau)",
41700 "Hungary (Magyarország)",
41705 "Iceland (Ísland)",
41725 "Iraq (العراق)",
41741 "Israel (ישראל)",
41768 "Jordan (الأردن)",
41773 "Kazakhstan (Казахстан)",
41794 "Kuwait (الكويت)",
41799 "Kyrgyzstan (Кыргызстан)",
41809 "Latvia (Latvija)",
41814 "Lebanon (لبنان)",
41829 "Libya (ليبيا)",
41839 "Lithuania (Lietuva)",
41854 "Macedonia (FYROM) (Македонија)",
41859 "Madagascar (Madagasikara)",
41889 "Marshall Islands",
41899 "Mauritania (موريتانيا)",
41904 "Mauritius (Moris)",
41925 "Moldova (Republica Moldova)",
41935 "Mongolia (Монгол)",
41940 "Montenegro (Crna Gora)",
41950 "Morocco (المغرب)",
41956 "Mozambique (Moçambique)",
41961 "Myanmar (Burma) (မြန်မာ)",
41966 "Namibia (Namibië)",
41981 "Netherlands (Nederland)",
41986 "New Caledonia (Nouvelle-Calédonie)",
42021 "North Korea (조선 민주주의 인민 공화국)",
42026 "Northern Mariana Islands",
42042 "Pakistan (پاکستان)",
42052 "Palestine (فلسطين)",
42062 "Papua New Guinea",
42104 "Réunion (La Réunion)",
42110 "Romania (România)",
42126 "Saint Barthélemy",
42137 "Saint Kitts and Nevis",
42147 "Saint Martin (Saint-Martin (partie française))",
42153 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42158 "Saint Vincent and the Grenadines",
42173 "São Tomé and Príncipe (São Tomé e Príncipe)",
42178 "Saudi Arabia (المملكة العربية السعودية)",
42183 "Senegal (Sénégal)",
42213 "Slovakia (Slovensko)",
42218 "Slovenia (Slovenija)",
42228 "Somalia (Soomaaliya)",
42238 "South Korea (대한민국)",
42243 "South Sudan (جنوب السودان)",
42253 "Sri Lanka (ශ්රී ලංකාව)",
42258 "Sudan (السودان)",
42268 "Svalbard and Jan Mayen",
42279 "Sweden (Sverige)",
42284 "Switzerland (Schweiz)",
42289 "Syria (سوريا)",
42334 "Trinidad and Tobago",
42339 "Tunisia (تونس)",
42344 "Turkey (Türkiye)",
42354 "Turks and Caicos Islands",
42364 "U.S. Virgin Islands",
42374 "Ukraine (Україна)",
42379 "United Arab Emirates (الإمارات العربية المتحدة)",
42401 "Uzbekistan (Oʻzbekiston)",
42411 "Vatican City (Città del Vaticano)",
42422 "Vietnam (Việt Nam)",
42427 "Wallis and Futuna (Wallis-et-Futuna)",
42432 "Western Sahara (الصحراء الغربية)",
42438 "Yemen (اليمن)",
42462 * This script refer to:
42463 * Title: International Telephone Input
42464 * Author: Jack O'Connor
42465 * Code version: v12.1.12
42466 * Availability: https://github.com/jackocnr/intl-tel-input.git
42470 * @class Roo.bootstrap.PhoneInput
42471 * @extends Roo.bootstrap.TriggerField
42472 * An input with International dial-code selection
42474 * @cfg {String} defaultDialCode default '+852'
42475 * @cfg {Array} preferedCountries default []
42478 * Create a new PhoneInput.
42479 * @param {Object} config Configuration options
42482 Roo.bootstrap.PhoneInput = function(config) {
42483 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42486 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42488 listWidth: undefined,
42490 selectedClass: 'active',
42492 invalidClass : "has-warning",
42494 validClass: 'has-success',
42496 allowed: '0123456789',
42501 * @cfg {String} defaultDialCode The default dial code when initializing the input
42503 defaultDialCode: '+852',
42506 * @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
42508 preferedCountries: false,
42510 getAutoCreate : function()
42512 var data = Roo.bootstrap.PhoneInputData();
42513 var align = this.labelAlign || this.parentLabelAlign();
42516 this.allCountries = [];
42517 this.dialCodeMapping = [];
42519 for (var i = 0; i < data.length; i++) {
42521 this.allCountries[i] = {
42525 priority: c[3] || 0,
42526 areaCodes: c[4] || null
42528 this.dialCodeMapping[c[2]] = {
42531 priority: c[3] || 0,
42532 areaCodes: c[4] || null
42544 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42545 maxlength: this.max_length,
42546 cls : 'form-control tel-input',
42547 autocomplete: 'new-password'
42550 var hiddenInput = {
42553 cls: 'hidden-tel-input'
42557 hiddenInput.name = this.name;
42560 if (this.disabled) {
42561 input.disabled = true;
42564 var flag_container = {
42581 cls: this.hasFeedback ? 'has-feedback' : '',
42587 cls: 'dial-code-holder',
42594 cls: 'roo-select2-container input-group',
42601 if (this.fieldLabel.length) {
42604 tooltip: 'This field is required'
42610 cls: 'control-label',
42616 html: this.fieldLabel
42619 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42625 if(this.indicatorpos == 'right') {
42626 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42633 if(align == 'left') {
42641 if(this.labelWidth > 12){
42642 label.style = "width: " + this.labelWidth + 'px';
42644 if(this.labelWidth < 13 && this.labelmd == 0){
42645 this.labelmd = this.labelWidth;
42647 if(this.labellg > 0){
42648 label.cls += ' col-lg-' + this.labellg;
42649 input.cls += ' col-lg-' + (12 - this.labellg);
42651 if(this.labelmd > 0){
42652 label.cls += ' col-md-' + this.labelmd;
42653 container.cls += ' col-md-' + (12 - this.labelmd);
42655 if(this.labelsm > 0){
42656 label.cls += ' col-sm-' + this.labelsm;
42657 container.cls += ' col-sm-' + (12 - this.labelsm);
42659 if(this.labelxs > 0){
42660 label.cls += ' col-xs-' + this.labelxs;
42661 container.cls += ' col-xs-' + (12 - this.labelxs);
42671 var settings = this;
42673 ['xs','sm','md','lg'].map(function(size){
42674 if (settings[size]) {
42675 cfg.cls += ' col-' + size + '-' + settings[size];
42679 this.store = new Roo.data.Store({
42680 proxy : new Roo.data.MemoryProxy({}),
42681 reader : new Roo.data.JsonReader({
42692 'name' : 'dialCode',
42696 'name' : 'priority',
42700 'name' : 'areaCodes',
42707 if(!this.preferedCountries) {
42708 this.preferedCountries = [
42715 var p = this.preferedCountries.reverse();
42718 for (var i = 0; i < p.length; i++) {
42719 for (var j = 0; j < this.allCountries.length; j++) {
42720 if(this.allCountries[j].iso2 == p[i]) {
42721 var t = this.allCountries[j];
42722 this.allCountries.splice(j,1);
42723 this.allCountries.unshift(t);
42729 this.store.proxy.data = {
42731 data: this.allCountries
42737 initEvents : function()
42740 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42742 this.indicator = this.indicatorEl();
42743 this.flag = this.flagEl();
42744 this.dialCodeHolder = this.dialCodeHolderEl();
42746 this.trigger = this.el.select('div.flag-box',true).first();
42747 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42752 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42753 _this.list.setWidth(lw);
42756 this.list.on('mouseover', this.onViewOver, this);
42757 this.list.on('mousemove', this.onViewMove, this);
42758 this.inputEl().on("keyup", this.onKeyUp, this);
42759 this.inputEl().on("keypress", this.onKeyPress, this);
42761 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42763 this.view = new Roo.View(this.list, this.tpl, {
42764 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42767 this.view.on('click', this.onViewClick, this);
42768 this.setValue(this.defaultDialCode);
42771 onTriggerClick : function(e)
42773 Roo.log('trigger click');
42778 if(this.isExpanded()){
42780 this.hasFocus = false;
42782 this.store.load({});
42783 this.hasFocus = true;
42788 isExpanded : function()
42790 return this.list.isVisible();
42793 collapse : function()
42795 if(!this.isExpanded()){
42799 Roo.get(document).un('mousedown', this.collapseIf, this);
42800 Roo.get(document).un('mousewheel', this.collapseIf, this);
42801 this.fireEvent('collapse', this);
42805 expand : function()
42809 if(this.isExpanded() || !this.hasFocus){
42813 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42814 this.list.setWidth(lw);
42817 this.restrictHeight();
42819 Roo.get(document).on('mousedown', this.collapseIf, this);
42820 Roo.get(document).on('mousewheel', this.collapseIf, this);
42822 this.fireEvent('expand', this);
42825 restrictHeight : function()
42827 this.list.alignTo(this.inputEl(), this.listAlign);
42828 this.list.alignTo(this.inputEl(), this.listAlign);
42831 onViewOver : function(e, t)
42833 if(this.inKeyMode){
42836 var item = this.view.findItemFromChild(t);
42839 var index = this.view.indexOf(item);
42840 this.select(index, false);
42845 onViewClick : function(view, doFocus, el, e)
42847 var index = this.view.getSelectedIndexes()[0];
42849 var r = this.store.getAt(index);
42852 this.onSelect(r, index);
42854 if(doFocus !== false && !this.blockFocus){
42855 this.inputEl().focus();
42859 onViewMove : function(e, t)
42861 this.inKeyMode = false;
42864 select : function(index, scrollIntoView)
42866 this.selectedIndex = index;
42867 this.view.select(index);
42868 if(scrollIntoView !== false){
42869 var el = this.view.getNode(index);
42871 this.list.scrollChildIntoView(el, false);
42876 createList : function()
42878 this.list = Roo.get(document.body).createChild({
42880 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42881 style: 'display:none'
42884 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42887 collapseIf : function(e)
42889 var in_combo = e.within(this.el);
42890 var in_list = e.within(this.list);
42891 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42893 if (in_combo || in_list || is_list) {
42899 onSelect : function(record, index)
42901 if(this.fireEvent('beforeselect', this, record, index) !== false){
42903 this.setFlagClass(record.data.iso2);
42904 this.setDialCode(record.data.dialCode);
42905 this.hasFocus = false;
42907 this.fireEvent('select', this, record, index);
42911 flagEl : function()
42913 var flag = this.el.select('div.flag',true).first();
42920 dialCodeHolderEl : function()
42922 var d = this.el.select('input.dial-code-holder',true).first();
42929 setDialCode : function(v)
42931 this.dialCodeHolder.dom.value = '+'+v;
42934 setFlagClass : function(n)
42936 this.flag.dom.className = 'flag '+n;
42939 getValue : function()
42941 var v = this.inputEl().getValue();
42942 if(this.dialCodeHolder) {
42943 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42948 setValue : function(v)
42950 var d = this.getDialCode(v);
42952 //invalid dial code
42953 if(v.length == 0 || !d || d.length == 0) {
42955 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42956 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42962 this.setFlagClass(this.dialCodeMapping[d].iso2);
42963 this.setDialCode(d);
42964 this.inputEl().dom.value = v.replace('+'+d,'');
42965 this.hiddenEl().dom.value = this.getValue();
42970 getDialCode : function(v)
42974 if (v.length == 0) {
42975 return this.dialCodeHolder.dom.value;
42979 if (v.charAt(0) != "+") {
42982 var numericChars = "";
42983 for (var i = 1; i < v.length; i++) {
42984 var c = v.charAt(i);
42987 if (this.dialCodeMapping[numericChars]) {
42988 dialCode = v.substr(1, i);
42990 if (numericChars.length == 4) {
43000 this.setValue(this.defaultDialCode);
43004 hiddenEl : function()
43006 return this.el.select('input.hidden-tel-input',true).first();
43009 // after setting val
43010 onKeyUp : function(e){
43011 this.setValue(this.getValue());
43014 onKeyPress : function(e){
43015 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43022 * @class Roo.bootstrap.MoneyField
43023 * @extends Roo.bootstrap.ComboBox
43024 * Bootstrap MoneyField class
43027 * Create a new MoneyField.
43028 * @param {Object} config Configuration options
43031 Roo.bootstrap.MoneyField = function(config) {
43033 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43037 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43040 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43042 allowDecimals : true,
43044 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43046 decimalSeparator : ".",
43048 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43050 decimalPrecision : 0,
43052 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43054 allowNegative : true,
43056 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43060 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43062 minValue : Number.NEGATIVE_INFINITY,
43064 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43066 maxValue : Number.MAX_VALUE,
43068 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43070 minText : "The minimum value for this field is {0}",
43072 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43074 maxText : "The maximum value for this field is {0}",
43076 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43077 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43079 nanText : "{0} is not a valid number",
43081 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43085 * @cfg {String} defaults currency of the MoneyField
43086 * value should be in lkey
43088 defaultCurrency : false,
43090 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43092 thousandsDelimiter : false,
43094 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43105 getAutoCreate : function()
43107 var align = this.labelAlign || this.parentLabelAlign();
43119 cls : 'form-control roo-money-amount-input',
43120 autocomplete: 'new-password'
43123 var hiddenInput = {
43127 cls: 'hidden-number-input'
43130 if(this.max_length) {
43131 input.maxlength = this.max_length;
43135 hiddenInput.name = this.name;
43138 if (this.disabled) {
43139 input.disabled = true;
43142 var clg = 12 - this.inputlg;
43143 var cmd = 12 - this.inputmd;
43144 var csm = 12 - this.inputsm;
43145 var cxs = 12 - this.inputxs;
43149 cls : 'row roo-money-field',
43153 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43157 cls: 'roo-select2-container input-group',
43161 cls : 'form-control roo-money-currency-input',
43162 autocomplete: 'new-password',
43164 name : this.currencyName
43168 cls : 'input-group-addon',
43182 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43186 cls: this.hasFeedback ? 'has-feedback' : '',
43197 if (this.fieldLabel.length) {
43200 tooltip: 'This field is required'
43206 cls: 'control-label',
43212 html: this.fieldLabel
43215 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43221 if(this.indicatorpos == 'right') {
43222 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43229 if(align == 'left') {
43237 if(this.labelWidth > 12){
43238 label.style = "width: " + this.labelWidth + 'px';
43240 if(this.labelWidth < 13 && this.labelmd == 0){
43241 this.labelmd = this.labelWidth;
43243 if(this.labellg > 0){
43244 label.cls += ' col-lg-' + this.labellg;
43245 input.cls += ' col-lg-' + (12 - this.labellg);
43247 if(this.labelmd > 0){
43248 label.cls += ' col-md-' + this.labelmd;
43249 container.cls += ' col-md-' + (12 - this.labelmd);
43251 if(this.labelsm > 0){
43252 label.cls += ' col-sm-' + this.labelsm;
43253 container.cls += ' col-sm-' + (12 - this.labelsm);
43255 if(this.labelxs > 0){
43256 label.cls += ' col-xs-' + this.labelxs;
43257 container.cls += ' col-xs-' + (12 - this.labelxs);
43268 var settings = this;
43270 ['xs','sm','md','lg'].map(function(size){
43271 if (settings[size]) {
43272 cfg.cls += ' col-' + size + '-' + settings[size];
43279 initEvents : function()
43281 this.indicator = this.indicatorEl();
43283 this.initCurrencyEvent();
43285 this.initNumberEvent();
43288 initCurrencyEvent : function()
43291 throw "can not find store for combo";
43294 this.store = Roo.factory(this.store, Roo.data);
43295 this.store.parent = this;
43299 this.triggerEl = this.el.select('.input-group-addon', true).first();
43301 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43306 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43307 _this.list.setWidth(lw);
43310 this.list.on('mouseover', this.onViewOver, this);
43311 this.list.on('mousemove', this.onViewMove, this);
43312 this.list.on('scroll', this.onViewScroll, this);
43315 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43318 this.view = new Roo.View(this.list, this.tpl, {
43319 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43322 this.view.on('click', this.onViewClick, this);
43324 this.store.on('beforeload', this.onBeforeLoad, this);
43325 this.store.on('load', this.onLoad, this);
43326 this.store.on('loadexception', this.onLoadException, this);
43328 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43329 "up" : function(e){
43330 this.inKeyMode = true;
43334 "down" : function(e){
43335 if(!this.isExpanded()){
43336 this.onTriggerClick();
43338 this.inKeyMode = true;
43343 "enter" : function(e){
43346 if(this.fireEvent("specialkey", this, e)){
43347 this.onViewClick(false);
43353 "esc" : function(e){
43357 "tab" : function(e){
43360 if(this.fireEvent("specialkey", this, e)){
43361 this.onViewClick(false);
43369 doRelay : function(foo, bar, hname){
43370 if(hname == 'down' || this.scope.isExpanded()){
43371 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43379 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43383 initNumberEvent : function(e)
43385 this.inputEl().on("keydown" , this.fireKey, this);
43386 this.inputEl().on("focus", this.onFocus, this);
43387 this.inputEl().on("blur", this.onBlur, this);
43389 this.inputEl().relayEvent('keyup', this);
43391 if(this.indicator){
43392 this.indicator.addClass('invisible');
43395 this.originalValue = this.getValue();
43397 if(this.validationEvent == 'keyup'){
43398 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43399 this.inputEl().on('keyup', this.filterValidation, this);
43401 else if(this.validationEvent !== false){
43402 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43405 if(this.selectOnFocus){
43406 this.on("focus", this.preFocus, this);
43409 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43410 this.inputEl().on("keypress", this.filterKeys, this);
43412 this.inputEl().relayEvent('keypress', this);
43415 var allowed = "0123456789";
43417 if(this.allowDecimals){
43418 allowed += this.decimalSeparator;
43421 if(this.allowNegative){
43425 if(this.thousandsDelimiter) {
43429 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43431 var keyPress = function(e){
43433 var k = e.getKey();
43435 var c = e.getCharCode();
43438 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43439 allowed.indexOf(String.fromCharCode(c)) === -1
43445 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43449 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43454 this.inputEl().on("keypress", keyPress, this);
43458 onTriggerClick : function(e)
43465 this.loadNext = false;
43467 if(this.isExpanded()){
43472 this.hasFocus = true;
43474 if(this.triggerAction == 'all') {
43475 this.doQuery(this.allQuery, true);
43479 this.doQuery(this.getRawValue());
43482 getCurrency : function()
43484 var v = this.currencyEl().getValue();
43489 restrictHeight : function()
43491 this.list.alignTo(this.currencyEl(), this.listAlign);
43492 this.list.alignTo(this.currencyEl(), this.listAlign);
43495 onViewClick : function(view, doFocus, el, e)
43497 var index = this.view.getSelectedIndexes()[0];
43499 var r = this.store.getAt(index);
43502 this.onSelect(r, index);
43506 onSelect : function(record, index){
43508 if(this.fireEvent('beforeselect', this, record, index) !== false){
43510 this.setFromCurrencyData(index > -1 ? record.data : false);
43514 this.fireEvent('select', this, record, index);
43518 setFromCurrencyData : function(o)
43522 this.lastCurrency = o;
43524 if (this.currencyField) {
43525 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43527 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43530 this.lastSelectionText = currency;
43532 //setting default currency
43533 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43534 this.setCurrency(this.defaultCurrency);
43538 this.setCurrency(currency);
43541 setFromData : function(o)
43545 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43547 this.setFromCurrencyData(c);
43552 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43554 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43557 this.setValue(value);
43561 setCurrency : function(v)
43563 this.currencyValue = v;
43566 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43571 setValue : function(v)
43573 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43579 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43581 this.inputEl().dom.value = (v == '') ? '' :
43582 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43584 if(!this.allowZero && v === '0') {
43585 this.hiddenEl().dom.value = '';
43586 this.inputEl().dom.value = '';
43593 getRawValue : function()
43595 var v = this.inputEl().getValue();
43600 getValue : function()
43602 return this.fixPrecision(this.parseValue(this.getRawValue()));
43605 parseValue : function(value)
43607 if(this.thousandsDelimiter) {
43609 r = new RegExp(",", "g");
43610 value = value.replace(r, "");
43613 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43614 return isNaN(value) ? '' : value;
43618 fixPrecision : function(value)
43620 if(this.thousandsDelimiter) {
43622 r = new RegExp(",", "g");
43623 value = value.replace(r, "");
43626 var nan = isNaN(value);
43628 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43629 return nan ? '' : value;
43631 return parseFloat(value).toFixed(this.decimalPrecision);
43634 decimalPrecisionFcn : function(v)
43636 return Math.floor(v);
43639 validateValue : function(value)
43641 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43645 var num = this.parseValue(value);
43648 this.markInvalid(String.format(this.nanText, value));
43652 if(num < this.minValue){
43653 this.markInvalid(String.format(this.minText, this.minValue));
43657 if(num > this.maxValue){
43658 this.markInvalid(String.format(this.maxText, this.maxValue));
43665 validate : function()
43667 if(this.disabled || this.allowBlank){
43672 var currency = this.getCurrency();
43674 if(this.validateValue(this.getRawValue()) && currency.length){
43679 this.markInvalid();
43683 getName: function()
43688 beforeBlur : function()
43694 var v = this.parseValue(this.getRawValue());
43701 onBlur : function()
43705 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43706 //this.el.removeClass(this.focusClass);
43709 this.hasFocus = false;
43711 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43715 var v = this.getValue();
43717 if(String(v) !== String(this.startValue)){
43718 this.fireEvent('change', this, v, this.startValue);
43721 this.fireEvent("blur", this);
43724 inputEl : function()
43726 return this.el.select('.roo-money-amount-input', true).first();
43729 currencyEl : function()
43731 return this.el.select('.roo-money-currency-input', true).first();
43734 hiddenEl : function()
43736 return this.el.select('input.hidden-number-input',true).first();
43740 * @class Roo.bootstrap.BezierSignature
43741 * @extends Roo.bootstrap.Component
43742 * Bootstrap BezierSignature class
43743 * This script refer to:
43744 * Title: Signature Pad
43746 * Availability: https://github.com/szimek/signature_pad
43749 * Create a new BezierSignature
43750 * @param {Object} config The config object
43753 Roo.bootstrap.BezierSignature = function(config){
43754 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43760 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43767 mouse_btn_down: true,
43770 * @cfg {int} canvas height
43772 canvas_height: '200px',
43775 * @cfg {float|function} Radius of a single dot.
43780 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43785 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43790 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43795 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43800 * @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.
43802 bg_color: 'rgba(0, 0, 0, 0)',
43805 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43807 dot_color: 'black',
43810 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43812 velocity_filter_weight: 0.7,
43815 * @cfg {function} Callback when stroke begin.
43820 * @cfg {function} Callback when stroke end.
43824 getAutoCreate : function()
43826 var cls = 'roo-signature column';
43829 cls += ' ' + this.cls;
43839 for(var i = 0; i < col_sizes.length; i++) {
43840 if(this[col_sizes[i]]) {
43841 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43851 cls: 'roo-signature-body',
43855 cls: 'roo-signature-body-canvas',
43856 height: this.canvas_height,
43857 width: this.canvas_width
43864 style: 'display: none'
43872 initEvents: function()
43874 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43876 var canvas = this.canvasEl();
43878 // mouse && touch event swapping...
43879 canvas.dom.style.touchAction = 'none';
43880 canvas.dom.style.msTouchAction = 'none';
43882 this.mouse_btn_down = false;
43883 canvas.on('mousedown', this._handleMouseDown, this);
43884 canvas.on('mousemove', this._handleMouseMove, this);
43885 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43887 if (window.PointerEvent) {
43888 canvas.on('pointerdown', this._handleMouseDown, this);
43889 canvas.on('pointermove', this._handleMouseMove, this);
43890 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43893 if ('ontouchstart' in window) {
43894 canvas.on('touchstart', this._handleTouchStart, this);
43895 canvas.on('touchmove', this._handleTouchMove, this);
43896 canvas.on('touchend', this._handleTouchEnd, this);
43899 Roo.EventManager.onWindowResize(this.resize, this, true);
43901 // file input event
43902 this.fileEl().on('change', this.uploadImage, this);
43909 resize: function(){
43911 var canvas = this.canvasEl().dom;
43912 var ctx = this.canvasElCtx();
43913 var img_data = false;
43915 if(canvas.width > 0) {
43916 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43918 // setting canvas width will clean img data
43921 var style = window.getComputedStyle ?
43922 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43924 var padding_left = parseInt(style.paddingLeft) || 0;
43925 var padding_right = parseInt(style.paddingRight) || 0;
43927 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43930 ctx.putImageData(img_data, 0, 0);
43934 _handleMouseDown: function(e)
43936 if (e.browserEvent.which === 1) {
43937 this.mouse_btn_down = true;
43938 this.strokeBegin(e);
43942 _handleMouseMove: function (e)
43944 if (this.mouse_btn_down) {
43945 this.strokeMoveUpdate(e);
43949 _handleMouseUp: function (e)
43951 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43952 this.mouse_btn_down = false;
43957 _handleTouchStart: function (e) {
43959 e.preventDefault();
43960 if (e.browserEvent.targetTouches.length === 1) {
43961 // var touch = e.browserEvent.changedTouches[0];
43962 // this.strokeBegin(touch);
43964 this.strokeBegin(e); // assume e catching the correct xy...
43968 _handleTouchMove: function (e) {
43969 e.preventDefault();
43970 // var touch = event.targetTouches[0];
43971 // _this._strokeMoveUpdate(touch);
43972 this.strokeMoveUpdate(e);
43975 _handleTouchEnd: function (e) {
43976 var wasCanvasTouched = e.target === this.canvasEl().dom;
43977 if (wasCanvasTouched) {
43978 e.preventDefault();
43979 // var touch = event.changedTouches[0];
43980 // _this._strokeEnd(touch);
43985 reset: function () {
43986 this._lastPoints = [];
43987 this._lastVelocity = 0;
43988 this._lastWidth = (this.min_width + this.max_width) / 2;
43989 this.canvasElCtx().fillStyle = this.dot_color;
43992 strokeMoveUpdate: function(e)
43994 this.strokeUpdate(e);
43996 if (this.throttle) {
43997 this.throttleStroke(this.strokeUpdate, this.throttle);
44000 this.strokeUpdate(e);
44004 strokeBegin: function(e)
44006 var newPointGroup = {
44007 color: this.dot_color,
44011 if (typeof this.onBegin === 'function') {
44015 this.curve_data.push(newPointGroup);
44017 this.strokeUpdate(e);
44020 strokeUpdate: function(e)
44022 var rect = this.canvasEl().dom.getBoundingClientRect();
44023 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44024 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44025 var lastPoints = lastPointGroup.points;
44026 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44027 var isLastPointTooClose = lastPoint
44028 ? point.distanceTo(lastPoint) <= this.min_distance
44030 var color = lastPointGroup.color;
44031 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44032 var curve = this.addPoint(point);
44034 this.drawDot({color: color, point: point});
44037 this.drawCurve({color: color, curve: curve});
44047 strokeEnd: function(e)
44049 this.strokeUpdate(e);
44050 if (typeof this.onEnd === 'function') {
44055 addPoint: function (point) {
44056 var _lastPoints = this._lastPoints;
44057 _lastPoints.push(point);
44058 if (_lastPoints.length > 2) {
44059 if (_lastPoints.length === 3) {
44060 _lastPoints.unshift(_lastPoints[0]);
44062 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44063 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44064 _lastPoints.shift();
44070 calculateCurveWidths: function (startPoint, endPoint) {
44071 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44072 (1 - this.velocity_filter_weight) * this._lastVelocity;
44074 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44077 start: this._lastWidth
44080 this._lastVelocity = velocity;
44081 this._lastWidth = newWidth;
44085 drawDot: function (_a) {
44086 var color = _a.color, point = _a.point;
44087 var ctx = this.canvasElCtx();
44088 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44090 this.drawCurveSegment(point.x, point.y, width);
44092 ctx.fillStyle = color;
44096 drawCurve: function (_a) {
44097 var color = _a.color, curve = _a.curve;
44098 var ctx = this.canvasElCtx();
44099 var widthDelta = curve.endWidth - curve.startWidth;
44100 var drawSteps = Math.floor(curve.length()) * 2;
44102 ctx.fillStyle = color;
44103 for (var i = 0; i < drawSteps; i += 1) {
44104 var t = i / drawSteps;
44110 var x = uuu * curve.startPoint.x;
44111 x += 3 * uu * t * curve.control1.x;
44112 x += 3 * u * tt * curve.control2.x;
44113 x += ttt * curve.endPoint.x;
44114 var y = uuu * curve.startPoint.y;
44115 y += 3 * uu * t * curve.control1.y;
44116 y += 3 * u * tt * curve.control2.y;
44117 y += ttt * curve.endPoint.y;
44118 var width = curve.startWidth + ttt * widthDelta;
44119 this.drawCurveSegment(x, y, width);
44125 drawCurveSegment: function (x, y, width) {
44126 var ctx = this.canvasElCtx();
44128 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44129 this.is_empty = false;
44134 var ctx = this.canvasElCtx();
44135 var canvas = this.canvasEl().dom;
44136 ctx.fillStyle = this.bg_color;
44137 ctx.clearRect(0, 0, canvas.width, canvas.height);
44138 ctx.fillRect(0, 0, canvas.width, canvas.height);
44139 this.curve_data = [];
44141 this.is_empty = true;
44146 return this.el.select('input',true).first();
44149 canvasEl: function()
44151 return this.el.select('canvas',true).first();
44154 canvasElCtx: function()
44156 return this.el.select('canvas',true).first().dom.getContext('2d');
44159 getImage: function(type)
44161 if(this.is_empty) {
44166 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44169 drawFromImage: function(img_src)
44171 var img = new Image();
44173 img.onload = function(){
44174 this.canvasElCtx().drawImage(img, 0, 0);
44179 this.is_empty = false;
44182 selectImage: function()
44184 this.fileEl().dom.click();
44187 uploadImage: function(e)
44189 var reader = new FileReader();
44191 reader.onload = function(e){
44192 var img = new Image();
44193 img.onload = function(){
44195 this.canvasElCtx().drawImage(img, 0, 0);
44197 img.src = e.target.result;
44200 reader.readAsDataURL(e.target.files[0]);
44203 // Bezier Point Constructor
44204 Point: (function () {
44205 function Point(x, y, time) {
44208 this.time = time || Date.now();
44210 Point.prototype.distanceTo = function (start) {
44211 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44213 Point.prototype.equals = function (other) {
44214 return this.x === other.x && this.y === other.y && this.time === other.time;
44216 Point.prototype.velocityFrom = function (start) {
44217 return this.time !== start.time
44218 ? this.distanceTo(start) / (this.time - start.time)
44225 // Bezier Constructor
44226 Bezier: (function () {
44227 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44228 this.startPoint = startPoint;
44229 this.control2 = control2;
44230 this.control1 = control1;
44231 this.endPoint = endPoint;
44232 this.startWidth = startWidth;
44233 this.endWidth = endWidth;
44235 Bezier.fromPoints = function (points, widths, scope) {
44236 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44237 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44238 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44240 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44241 var dx1 = s1.x - s2.x;
44242 var dy1 = s1.y - s2.y;
44243 var dx2 = s2.x - s3.x;
44244 var dy2 = s2.y - s3.y;
44245 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44246 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44247 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44248 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44249 var dxm = m1.x - m2.x;
44250 var dym = m1.y - m2.y;
44251 var k = l2 / (l1 + l2);
44252 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44253 var tx = s2.x - cm.x;
44254 var ty = s2.y - cm.y;
44256 c1: new scope.Point(m1.x + tx, m1.y + ty),
44257 c2: new scope.Point(m2.x + tx, m2.y + ty)
44260 Bezier.prototype.length = function () {
44265 for (var i = 0; i <= steps; i += 1) {
44267 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44268 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44270 var xdiff = cx - px;
44271 var ydiff = cy - py;
44272 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44279 Bezier.prototype.point = function (t, start, c1, c2, end) {
44280 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44281 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44282 + (3.0 * c2 * (1.0 - t) * t * t)
44283 + (end * t * t * t);
44288 throttleStroke: function(fn, wait) {
44289 if (wait === void 0) { wait = 250; }
44291 var timeout = null;
44295 var later = function () {
44296 previous = Date.now();
44298 result = fn.apply(storedContext, storedArgs);
44300 storedContext = null;
44304 return function wrapper() {
44306 for (var _i = 0; _i < arguments.length; _i++) {
44307 args[_i] = arguments[_i];
44309 var now = Date.now();
44310 var remaining = wait - (now - previous);
44311 storedContext = this;
44313 if (remaining <= 0 || remaining > wait) {
44315 clearTimeout(timeout);
44319 result = fn.apply(storedContext, storedArgs);
44321 storedContext = null;
44325 else if (!timeout) {
44326 timeout = window.setTimeout(later, remaining);