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.
5735 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
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, {
5772 getAutoCreate : function()
5774 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5780 if (Roo.bootstrap.version == 4) {
5781 if (['tabs','pills'].indexOf(this.type) != -1) {
5782 cfg.cls += ' nav-' + this.type;
5784 // trying to remove so header bar can right align top?
5785 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5786 // do not use on header bar...
5787 cfg.cls += ' navbar-nav';
5792 if (['tabs','pills'].indexOf(this.type) != -1) {
5793 cfg.cls += ' nav-' + this.type
5795 if (this.type !== 'nav') {
5796 Roo.log('nav type must be nav/tabs/pills')
5798 cfg.cls += ' navbar-nav'
5802 if (this.parent() && this.parent().sidebar) {
5805 cls: 'dashboard-menu sidebar-menu'
5811 if (this.form === true) {
5814 cls: 'navbar-form form-inline'
5816 //nav navbar-right ml-md-auto
5817 if (this.align === 'right') {
5818 cfg.cls += ' navbar-right ml-md-auto';
5820 cfg.cls += ' navbar-left';
5824 if (this.align === 'right') {
5825 cfg.cls += ' navbar-right ml-md-auto';
5827 cfg.cls += ' mr-auto';
5831 cfg.cls += ' navbar-inverse';
5839 * sets the active Navigation item
5840 * @param {Roo.bootstrap.NavItem} the new current navitem
5842 setActiveItem : function(item)
5845 Roo.each(this.navItems, function(v){
5850 v.setActive(false, true);
5857 item.setActive(true, true);
5858 this.fireEvent('changed', this, item, prev);
5863 * gets the active Navigation item
5864 * @return {Roo.bootstrap.NavItem} the current navitem
5866 getActive : function()
5870 Roo.each(this.navItems, function(v){
5881 indexOfNav : function()
5885 Roo.each(this.navItems, function(v,i){
5896 * adds a Navigation item
5897 * @param {Roo.bootstrap.NavItem} the navitem to add
5899 addItem : function(cfg)
5901 if (this.form && Roo.bootstrap.version == 4) {
5904 var cn = new Roo.bootstrap.NavItem(cfg);
5906 cn.parentId = this.id;
5907 cn.onRender(this.el, null);
5911 * register a Navigation item
5912 * @param {Roo.bootstrap.NavItem} the navitem to add
5914 register : function(item)
5916 this.navItems.push( item);
5917 item.navId = this.navId;
5922 * clear all the Navigation item
5925 clearAll : function()
5928 this.el.dom.innerHTML = '';
5931 getNavItem: function(tabId)
5934 Roo.each(this.navItems, function(e) {
5935 if (e.tabId == tabId) {
5945 setActiveNext : function()
5947 var i = this.indexOfNav(this.getActive());
5948 if (i > this.navItems.length) {
5951 this.setActiveItem(this.navItems[i+1]);
5953 setActivePrev : function()
5955 var i = this.indexOfNav(this.getActive());
5959 this.setActiveItem(this.navItems[i-1]);
5961 clearWasActive : function(except) {
5962 Roo.each(this.navItems, function(e) {
5963 if (e.tabId != except.tabId && e.was_active) {
5964 e.was_active = false;
5971 getWasActive : function ()
5974 Roo.each(this.navItems, function(e) {
5989 Roo.apply(Roo.bootstrap.NavGroup, {
5993 * register a Navigation Group
5994 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996 register : function(navgrp)
5998 this.groups[navgrp.navId] = navgrp;
6002 * fetch a Navigation Group based on the navigation ID
6003 * @param {string} the navgroup to add
6004 * @returns {Roo.bootstrap.NavGroup} the navgroup
6006 get: function(navId) {
6007 if (typeof(this.groups[navId]) == 'undefined') {
6009 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011 return this.groups[navId] ;
6026 * @class Roo.bootstrap.NavItem
6027 * @extends Roo.bootstrap.Component
6028 * Bootstrap Navbar.NavItem class
6029 * @cfg {String} href link to
6030 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6031 * @cfg {Boolean} button_outline show and outlined button
6032 * @cfg {String} html content of button
6033 * @cfg {String} badge text inside badge
6034 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6035 * @cfg {String} glyphicon DEPRICATED - use fa
6036 * @cfg {String} icon DEPRICATED - use fa
6037 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6038 * @cfg {Boolean} active Is item active
6039 * @cfg {Boolean} disabled Is item disabled
6040 * @cfg {String} linkcls Link Class
6041 * @cfg {Boolean} preventDefault (true | false) default false
6042 * @cfg {String} tabId the tab that this item activates.
6043 * @cfg {String} tagtype (a|span) render as a href or span?
6044 * @cfg {Boolean} animateRef (true|false) link to element default false
6047 * Create a new Navbar Item
6048 * @param {Object} config The config object
6050 Roo.bootstrap.NavItem = function(config){
6051 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056 * The raw click event for the entire grid.
6057 * @param {Roo.EventObject} e
6062 * Fires when the active item active state changes
6063 * @param {Roo.bootstrap.NavItem} this
6064 * @param {boolean} state the new state
6070 * Fires when scroll to element
6071 * @param {Roo.bootstrap.NavItem} this
6072 * @param {Object} options
6073 * @param {Roo.EventObject} e
6081 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6090 preventDefault : false,
6098 button_outline : false,
6102 getAutoCreate : function(){
6109 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6112 cfg.cls += ' active' ;
6114 if (this.disabled) {
6115 cfg.cls += ' disabled';
6119 if (this.button_weight.length) {
6120 cfg.tag = this.href ? 'a' : 'button';
6121 cfg.html = this.html || '';
6122 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124 cfg.href = this.href;
6127 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6130 // menu .. should add dropdown-menu class - so no need for carat..
6132 if (this.badge !== '') {
6134 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6143 href : this.href || "#",
6144 html: this.html || ''
6147 if (this.tagtype == 'a') {
6148 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6152 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6155 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157 if(this.glyphicon) {
6158 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6163 cfg.cn[0].html += " <span class='caret'></span>";
6167 if (this.badge !== '') {
6169 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6177 onRender : function(ct, position)
6179 // Roo.log("Call onRender: " + this.xtype);
6180 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6184 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6185 this.navLink = this.el.select('.nav-link',true).first();
6190 initEvents: function()
6192 if (typeof (this.menu) != 'undefined') {
6193 this.menu.parentType = this.xtype;
6194 this.menu.triggerEl = this.el;
6195 this.menu = this.addxtype(Roo.apply({}, this.menu));
6198 this.el.on('click', this.onClick, this);
6200 //if(this.tagtype == 'span'){
6201 // this.el.select('span',true).on('click', this.onClick, this);
6204 // at this point parent should be available..
6205 this.parent().register(this);
6208 onClick : function(e)
6210 if (e.getTarget('.dropdown-menu-item')) {
6211 // did you click on a menu itemm.... - then don't trigger onclick..
6216 this.preventDefault ||
6219 Roo.log("NavItem - prevent Default?");
6223 if (this.disabled) {
6227 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6228 if (tg && tg.transition) {
6229 Roo.log("waiting for the transitionend");
6235 //Roo.log("fire event clicked");
6236 if(this.fireEvent('click', this, e) === false){
6240 if(this.tagtype == 'span'){
6244 //Roo.log(this.href);
6245 var ael = this.el.select('a',true).first();
6248 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6249 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6250 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6251 return; // ignore... - it's a 'hash' to another page.
6253 Roo.log("NavItem - prevent Default?");
6255 this.scrollToElement(e);
6259 var p = this.parent();
6261 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6262 if (typeof(p.setActiveItem) !== 'undefined') {
6263 p.setActiveItem(this);
6267 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6268 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6269 // remove the collapsed menu expand...
6270 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6274 isActive: function () {
6277 setActive : function(state, fire, is_was_active)
6279 if (this.active && !state && this.navId) {
6280 this.was_active = true;
6281 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283 nv.clearWasActive(this);
6287 this.active = state;
6290 this.el.removeClass('active');
6291 this.navLink ? this.navLink.removeClass('active') : false;
6292 } else if (!this.el.hasClass('active')) {
6294 this.el.addClass('active');
6295 if (Roo.bootstrap.version == 4 && this.navLink ) {
6296 this.navLink.addClass('active');
6301 this.fireEvent('changed', this, state);
6304 // show a panel if it's registered and related..
6306 if (!this.navId || !this.tabId || !state || is_was_active) {
6310 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6314 var pan = tg.getPanelByName(this.tabId);
6318 // if we can not flip to new panel - go back to old nav highlight..
6319 if (false == tg.showPanel(pan)) {
6320 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322 var onav = nv.getWasActive();
6324 onav.setActive(true, false, true);
6333 // this should not be here...
6334 setDisabled : function(state)
6336 this.disabled = state;
6338 this.el.removeClass('disabled');
6339 } else if (!this.el.hasClass('disabled')) {
6340 this.el.addClass('disabled');
6346 * Fetch the element to display the tooltip on.
6347 * @return {Roo.Element} defaults to this.el
6349 tooltipEl : function()
6351 return this.el.select('' + this.tagtype + '', true).first();
6354 scrollToElement : function(e)
6356 var c = document.body;
6359 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6362 c = document.documentElement;
6365 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6371 var o = target.calcOffsetsTo(c);
6378 this.fireEvent('scrollto', this, options, e);
6380 Roo.get(c).scrollTo('top', options.value, true);
6393 * <span> icon </span>
6394 * <span> text </span>
6395 * <span>badge </span>
6399 * @class Roo.bootstrap.NavSidebarItem
6400 * @extends Roo.bootstrap.NavItem
6401 * Bootstrap Navbar.NavSidebarItem class
6402 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6403 * {Boolean} open is the menu open
6404 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6405 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6406 * {String} buttonSize (sm|md|lg)the extra classes for the button
6407 * {Boolean} showArrow show arrow next to the text (default true)
6409 * Create a new Navbar Button
6410 * @param {Object} config The config object
6412 Roo.bootstrap.NavSidebarItem = function(config){
6413 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418 * The raw click event for the entire grid.
6419 * @param {Roo.EventObject} e
6424 * Fires when the active item active state changes
6425 * @param {Roo.bootstrap.NavSidebarItem} this
6426 * @param {boolean} state the new state
6434 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6436 badgeWeight : 'default',
6442 buttonWeight : 'default',
6448 getAutoCreate : function(){
6453 href : this.href || '#',
6459 if(this.buttonView){
6462 href : this.href || '#',
6463 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6476 cfg.cls += ' active';
6479 if (this.disabled) {
6480 cfg.cls += ' disabled';
6483 cfg.cls += ' open x-open';
6486 if (this.glyphicon || this.icon) {
6487 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6488 a.cn.push({ tag : 'i', cls : c }) ;
6491 if(!this.buttonView){
6494 html : this.html || ''
6501 if (this.badge !== '') {
6502 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6508 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6511 a.cls += ' dropdown-toggle treeview' ;
6517 initEvents : function()
6519 if (typeof (this.menu) != 'undefined') {
6520 this.menu.parentType = this.xtype;
6521 this.menu.triggerEl = this.el;
6522 this.menu = this.addxtype(Roo.apply({}, this.menu));
6525 this.el.on('click', this.onClick, this);
6527 if(this.badge !== ''){
6528 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533 onClick : function(e)
6540 if(this.preventDefault){
6544 this.fireEvent('click', this, e);
6547 disable : function()
6549 this.setDisabled(true);
6554 this.setDisabled(false);
6557 setDisabled : function(state)
6559 if(this.disabled == state){
6563 this.disabled = state;
6566 this.el.addClass('disabled');
6570 this.el.removeClass('disabled');
6575 setActive : function(state)
6577 if(this.active == state){
6581 this.active = state;
6584 this.el.addClass('active');
6588 this.el.removeClass('active');
6593 isActive: function ()
6598 setBadge : function(str)
6604 this.badgeEl.dom.innerHTML = str;
6619 Roo.namespace('Roo.bootstrap.breadcrumb');
6623 * @class Roo.bootstrap.breadcrumb.Nav
6624 * @extends Roo.bootstrap.Component
6625 * Bootstrap Breadcrumb Nav Class
6627 * @children Roo.bootstrap.breadcrumb.Item
6630 * Create a new breadcrumb.Nav
6631 * @param {Object} config The config object
6635 Roo.bootstrap.breadcrumb.Nav = function(config){
6636 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6643 getAutoCreate : function()
6660 initEvents: function()
6662 this.olEl = this.el.select('ol',true).first();
6664 getChildContainer : function()
6680 * @class Roo.bootstrap.breadcrumb.Nav
6681 * @extends Roo.bootstrap.Component
6682 * Bootstrap Breadcrumb Nav Class
6684 * @children Roo.bootstrap.breadcrumb.Component
6685 * @cfg {String} html the content of the link.
6686 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6687 * @cfg {Boolean} active is it active
6691 * Create a new breadcrumb.Nav
6692 * @param {Object} config The config object
6695 Roo.bootstrap.breadcrumb.Item = function(config){
6696 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701 * The img click event for the img.
6702 * @param {Roo.EventObject} e
6709 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6714 getAutoCreate : function()
6719 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721 if (this.href !== false) {
6728 cfg.html = this.html;
6734 initEvents: function()
6737 this.el.select('a', true).first().on('click',this.onClick, this)
6741 onClick : function(e)
6744 this.fireEvent('click',this, e);
6757 * @class Roo.bootstrap.Row
6758 * @extends Roo.bootstrap.Component
6759 * Bootstrap Row class (contains columns...)
6763 * @param {Object} config The config object
6766 Roo.bootstrap.Row = function(config){
6767 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6770 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6772 getAutoCreate : function(){
6791 * @class Roo.bootstrap.Pagination
6792 * @extends Roo.bootstrap.Component
6793 * Bootstrap Pagination class
6794 * @cfg {String} size xs | sm | md | lg
6795 * @cfg {Boolean} inverse false | true
6798 * Create a new Pagination
6799 * @param {Object} config The config object
6802 Roo.bootstrap.Pagination = function(config){
6803 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6806 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6812 getAutoCreate : function(){
6818 cfg.cls += ' inverse';
6824 cfg.cls += " " + this.cls;
6842 * @class Roo.bootstrap.PaginationItem
6843 * @extends Roo.bootstrap.Component
6844 * Bootstrap PaginationItem class
6845 * @cfg {String} html text
6846 * @cfg {String} href the link
6847 * @cfg {Boolean} preventDefault (true | false) default true
6848 * @cfg {Boolean} active (true | false) default false
6849 * @cfg {Boolean} disabled default false
6853 * Create a new PaginationItem
6854 * @param {Object} config The config object
6858 Roo.bootstrap.PaginationItem = function(config){
6859 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864 * The raw click event for the entire grid.
6865 * @param {Roo.EventObject} e
6871 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6875 preventDefault: true,
6880 getAutoCreate : function(){
6886 href : this.href ? this.href : '#',
6887 html : this.html ? this.html : ''
6897 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6901 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6907 initEvents: function() {
6909 this.el.on('click', this.onClick, this);
6912 onClick : function(e)
6914 Roo.log('PaginationItem on click ');
6915 if(this.preventDefault){
6923 this.fireEvent('click', this, e);
6939 * @class Roo.bootstrap.Slider
6940 * @extends Roo.bootstrap.Component
6941 * Bootstrap Slider class
6944 * Create a new Slider
6945 * @param {Object} config The config object
6948 Roo.bootstrap.Slider = function(config){
6949 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6952 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6954 getAutoCreate : function(){
6958 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6962 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6974 * Ext JS Library 1.1.1
6975 * Copyright(c) 2006-2007, Ext JS, LLC.
6977 * Originally Released Under LGPL - original licence link has changed is not relivant.
6980 * <script type="text/javascript">
6985 * @class Roo.grid.ColumnModel
6986 * @extends Roo.util.Observable
6987 * This is the default implementation of a ColumnModel used by the Grid. It defines
6988 * the columns in the grid.
6991 var colModel = new Roo.grid.ColumnModel([
6992 {header: "Ticker", width: 60, sortable: true, locked: true},
6993 {header: "Company Name", width: 150, sortable: true},
6994 {header: "Market Cap.", width: 100, sortable: true},
6995 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6996 {header: "Employees", width: 100, sortable: true, resizable: false}
7001 * The config options listed for this class are options which may appear in each
7002 * individual column definition.
7003 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005 * @param {Object} config An Array of column config objects. See this class's
7006 * config objects for details.
7008 Roo.grid.ColumnModel = function(config){
7010 * The config passed into the constructor
7012 this.config = config;
7015 // if no id, create one
7016 // if the column does not have a dataIndex mapping,
7017 // map it to the order it is in the config
7018 for(var i = 0, len = config.length; i < len; i++){
7020 if(typeof c.dataIndex == "undefined"){
7023 if(typeof c.renderer == "string"){
7024 c.renderer = Roo.util.Format[c.renderer];
7026 if(typeof c.id == "undefined"){
7029 if(c.editor && c.editor.xtype){
7030 c.editor = Roo.factory(c.editor, Roo.grid);
7032 if(c.editor && c.editor.isFormField){
7033 c.editor = new Roo.grid.GridEditor(c.editor);
7035 this.lookup[c.id] = c;
7039 * The width of columns which have no width specified (defaults to 100)
7042 this.defaultWidth = 100;
7045 * Default sortable of columns which have no sortable specified (defaults to false)
7048 this.defaultSortable = false;
7052 * @event widthchange
7053 * Fires when the width of a column changes.
7054 * @param {ColumnModel} this
7055 * @param {Number} columnIndex The column index
7056 * @param {Number} newWidth The new width
7058 "widthchange": true,
7060 * @event headerchange
7061 * Fires when the text of a header changes.
7062 * @param {ColumnModel} this
7063 * @param {Number} columnIndex The column index
7064 * @param {Number} newText The new header text
7066 "headerchange": true,
7068 * @event hiddenchange
7069 * Fires when a column is hidden or "unhidden".
7070 * @param {ColumnModel} this
7071 * @param {Number} columnIndex The column index
7072 * @param {Boolean} hidden true if hidden, false otherwise
7074 "hiddenchange": true,
7076 * @event columnmoved
7077 * Fires when a column is moved.
7078 * @param {ColumnModel} this
7079 * @param {Number} oldIndex
7080 * @param {Number} newIndex
7082 "columnmoved" : true,
7084 * @event columlockchange
7085 * Fires when a column's locked state is changed
7086 * @param {ColumnModel} this
7087 * @param {Number} colIndex
7088 * @param {Boolean} locked true if locked
7090 "columnlockchange" : true
7092 Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096 * @cfg {String} header The header text to display in the Grid view.
7099 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7100 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7101 * specified, the column's index is used as an index into the Record's data Array.
7104 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7105 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7108 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7109 * Defaults to the value of the {@link #defaultSortable} property.
7110 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7113 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7116 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7119 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7122 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7125 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7126 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7127 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7128 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7131 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7134 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7137 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7140 * @cfg {String} cursor (Optional)
7143 * @cfg {String} tooltip (Optional)
7146 * @cfg {Number} xs (Optional)
7149 * @cfg {Number} sm (Optional)
7152 * @cfg {Number} md (Optional)
7155 * @cfg {Number} lg (Optional)
7158 * Returns the id of the column at the specified index.
7159 * @param {Number} index The column index
7160 * @return {String} the id
7162 getColumnId : function(index){
7163 return this.config[index].id;
7167 * Returns the column for a specified id.
7168 * @param {String} id The column id
7169 * @return {Object} the column
7171 getColumnById : function(id){
7172 return this.lookup[id];
7177 * Returns the column for a specified dataIndex.
7178 * @param {String} dataIndex The column dataIndex
7179 * @return {Object|Boolean} the column or false if not found
7181 getColumnByDataIndex: function(dataIndex){
7182 var index = this.findColumnIndex(dataIndex);
7183 return index > -1 ? this.config[index] : false;
7187 * Returns the index for a specified column id.
7188 * @param {String} id The column id
7189 * @return {Number} the index, or -1 if not found
7191 getIndexById : function(id){
7192 for(var i = 0, len = this.config.length; i < len; i++){
7193 if(this.config[i].id == id){
7201 * Returns the index for a specified column dataIndex.
7202 * @param {String} dataIndex The column dataIndex
7203 * @return {Number} the index, or -1 if not found
7206 findColumnIndex : function(dataIndex){
7207 for(var i = 0, len = this.config.length; i < len; i++){
7208 if(this.config[i].dataIndex == dataIndex){
7216 moveColumn : function(oldIndex, newIndex){
7217 var c = this.config[oldIndex];
7218 this.config.splice(oldIndex, 1);
7219 this.config.splice(newIndex, 0, c);
7220 this.dataMap = null;
7221 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7224 isLocked : function(colIndex){
7225 return this.config[colIndex].locked === true;
7228 setLocked : function(colIndex, value, suppressEvent){
7229 if(this.isLocked(colIndex) == value){
7232 this.config[colIndex].locked = value;
7234 this.fireEvent("columnlockchange", this, colIndex, value);
7238 getTotalLockedWidth : function(){
7240 for(var i = 0; i < this.config.length; i++){
7241 if(this.isLocked(i) && !this.isHidden(i)){
7242 this.totalWidth += this.getColumnWidth(i);
7248 getLockedCount : function(){
7249 for(var i = 0, len = this.config.length; i < len; i++){
7250 if(!this.isLocked(i)){
7255 return this.config.length;
7259 * Returns the number of columns.
7262 getColumnCount : function(visibleOnly){
7263 if(visibleOnly === true){
7265 for(var i = 0, len = this.config.length; i < len; i++){
7266 if(!this.isHidden(i)){
7272 return this.config.length;
7276 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7277 * @param {Function} fn
7278 * @param {Object} scope (optional)
7279 * @return {Array} result
7281 getColumnsBy : function(fn, scope){
7283 for(var i = 0, len = this.config.length; i < len; i++){
7284 var c = this.config[i];
7285 if(fn.call(scope||this, c, i) === true){
7293 * Returns true if the specified column is sortable.
7294 * @param {Number} col The column index
7297 isSortable : function(col){
7298 if(typeof this.config[col].sortable == "undefined"){
7299 return this.defaultSortable;
7301 return this.config[col].sortable;
7305 * Returns the rendering (formatting) function defined for the column.
7306 * @param {Number} col The column index.
7307 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309 getRenderer : function(col){
7310 if(!this.config[col].renderer){
7311 return Roo.grid.ColumnModel.defaultRenderer;
7313 return this.config[col].renderer;
7317 * Sets the rendering (formatting) function for a column.
7318 * @param {Number} col The column index
7319 * @param {Function} fn The function to use to process the cell's raw data
7320 * to return HTML markup for the grid view. The render function is called with
7321 * the following parameters:<ul>
7322 * <li>Data value.</li>
7323 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7324 * <li>css A CSS style string to apply to the table cell.</li>
7325 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7326 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7327 * <li>Row index</li>
7328 * <li>Column index</li>
7329 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331 setRenderer : function(col, fn){
7332 this.config[col].renderer = fn;
7336 * Returns the width for the specified column.
7337 * @param {Number} col The column index
7340 getColumnWidth : function(col){
7341 return this.config[col].width * 1 || this.defaultWidth;
7345 * Sets the width for a column.
7346 * @param {Number} col The column index
7347 * @param {Number} width The new width
7349 setColumnWidth : function(col, width, suppressEvent){
7350 this.config[col].width = width;
7351 this.totalWidth = null;
7353 this.fireEvent("widthchange", this, col, width);
7358 * Returns the total width of all columns.
7359 * @param {Boolean} includeHidden True to include hidden column widths
7362 getTotalWidth : function(includeHidden){
7363 if(!this.totalWidth){
7364 this.totalWidth = 0;
7365 for(var i = 0, len = this.config.length; i < len; i++){
7366 if(includeHidden || !this.isHidden(i)){
7367 this.totalWidth += this.getColumnWidth(i);
7371 return this.totalWidth;
7375 * Returns the header for the specified column.
7376 * @param {Number} col The column index
7379 getColumnHeader : function(col){
7380 return this.config[col].header;
7384 * Sets the header for a column.
7385 * @param {Number} col The column index
7386 * @param {String} header The new header
7388 setColumnHeader : function(col, header){
7389 this.config[col].header = header;
7390 this.fireEvent("headerchange", this, col, header);
7394 * Returns the tooltip for the specified column.
7395 * @param {Number} col The column index
7398 getColumnTooltip : function(col){
7399 return this.config[col].tooltip;
7402 * Sets the tooltip for a column.
7403 * @param {Number} col The column index
7404 * @param {String} tooltip The new tooltip
7406 setColumnTooltip : function(col, tooltip){
7407 this.config[col].tooltip = tooltip;
7411 * Returns the dataIndex for the specified column.
7412 * @param {Number} col The column index
7415 getDataIndex : function(col){
7416 return this.config[col].dataIndex;
7420 * Sets the dataIndex for a column.
7421 * @param {Number} col The column index
7422 * @param {Number} dataIndex The new dataIndex
7424 setDataIndex : function(col, dataIndex){
7425 this.config[col].dataIndex = dataIndex;
7431 * Returns true if the cell is editable.
7432 * @param {Number} colIndex The column index
7433 * @param {Number} rowIndex The row index - this is nto actually used..?
7436 isCellEditable : function(colIndex, rowIndex){
7437 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7441 * Returns the editor defined for the cell/column.
7442 * return false or null to disable editing.
7443 * @param {Number} colIndex The column index
7444 * @param {Number} rowIndex The row index
7447 getCellEditor : function(colIndex, rowIndex){
7448 return this.config[colIndex].editor;
7452 * Sets if a column is editable.
7453 * @param {Number} col The column index
7454 * @param {Boolean} editable True if the column is editable
7456 setEditable : function(col, editable){
7457 this.config[col].editable = editable;
7462 * Returns true if the column is hidden.
7463 * @param {Number} colIndex The column index
7466 isHidden : function(colIndex){
7467 return this.config[colIndex].hidden;
7472 * Returns true if the column width cannot be changed
7474 isFixed : function(colIndex){
7475 return this.config[colIndex].fixed;
7479 * Returns true if the column can be resized
7482 isResizable : function(colIndex){
7483 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7486 * Sets if a column is hidden.
7487 * @param {Number} colIndex The column index
7488 * @param {Boolean} hidden True if the column is hidden
7490 setHidden : function(colIndex, hidden){
7491 this.config[colIndex].hidden = hidden;
7492 this.totalWidth = null;
7493 this.fireEvent("hiddenchange", this, colIndex, hidden);
7497 * Sets the editor for a column.
7498 * @param {Number} col The column index
7499 * @param {Object} editor The editor object
7501 setEditor : function(col, editor){
7502 this.config[col].editor = editor;
7506 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 if(typeof value == "object") {
7511 if(typeof value == "string" && value.length < 1){
7515 return String.format("{0}", value);
7518 // Alias for backwards compatibility
7519 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7522 * Ext JS Library 1.1.1
7523 * Copyright(c) 2006-2007, Ext JS, LLC.
7525 * Originally Released Under LGPL - original licence link has changed is not relivant.
7528 * <script type="text/javascript">
7532 * @class Roo.LoadMask
7533 * A simple utility class for generically masking elements while loading data. If the element being masked has
7534 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7535 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7536 * element's UpdateManager load indicator and will be destroyed after the initial load.
7538 * Create a new LoadMask
7539 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7540 * @param {Object} config The config object
7542 Roo.LoadMask = function(el, config){
7543 this.el = Roo.get(el);
7544 Roo.apply(this, config);
7546 this.store.on('beforeload', this.onBeforeLoad, this);
7547 this.store.on('load', this.onLoad, this);
7548 this.store.on('loadexception', this.onLoadException, this);
7549 this.removeMask = false;
7551 var um = this.el.getUpdateManager();
7552 um.showLoadIndicator = false; // disable the default indicator
7553 um.on('beforeupdate', this.onBeforeLoad, this);
7554 um.on('update', this.onLoad, this);
7555 um.on('failure', this.onLoad, this);
7556 this.removeMask = true;
7560 Roo.LoadMask.prototype = {
7562 * @cfg {Boolean} removeMask
7563 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7564 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7568 * The text to display in a centered loading message box (defaults to 'Loading...')
7572 * @cfg {String} msgCls
7573 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575 msgCls : 'x-mask-loading',
7578 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7584 * Disables the mask to prevent it from being displayed
7586 disable : function(){
7587 this.disabled = true;
7591 * Enables the mask so that it can be displayed
7593 enable : function(){
7594 this.disabled = false;
7597 onLoadException : function()
7601 if (typeof(arguments[3]) != 'undefined') {
7602 Roo.MessageBox.alert("Error loading",arguments[3]);
7606 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7607 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7614 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7623 onBeforeLoad : function(){
7625 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630 destroy : function(){
7632 this.store.un('beforeload', this.onBeforeLoad, this);
7633 this.store.un('load', this.onLoad, this);
7634 this.store.un('loadexception', this.onLoadException, this);
7636 var um = this.el.getUpdateManager();
7637 um.un('beforeupdate', this.onBeforeLoad, this);
7638 um.un('update', this.onLoad, this);
7639 um.un('failure', this.onLoad, this);
7650 * @class Roo.bootstrap.Table
7651 * @extends Roo.bootstrap.Component
7652 * Bootstrap Table class
7653 * @cfg {String} cls table class
7654 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7655 * @cfg {String} bgcolor Specifies the background color for a table
7656 * @cfg {Number} border Specifies whether the table cells should have borders or not
7657 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7658 * @cfg {Number} cellspacing Specifies the space between cells
7659 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7660 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7661 * @cfg {String} sortable Specifies that the table should be sortable
7662 * @cfg {String} summary Specifies a summary of the content of a table
7663 * @cfg {Number} width Specifies the width of a table
7664 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666 * @cfg {boolean} striped Should the rows be alternative striped
7667 * @cfg {boolean} bordered Add borders to the table
7668 * @cfg {boolean} hover Add hover highlighting
7669 * @cfg {boolean} condensed Format condensed
7670 * @cfg {boolean} responsive Format condensed
7671 * @cfg {Boolean} loadMask (true|false) default false
7672 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7673 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7674 * @cfg {Boolean} rowSelection (true|false) default false
7675 * @cfg {Boolean} cellSelection (true|false) default false
7676 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7677 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7678 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7679 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7683 * Create a new Table
7684 * @param {Object} config The config object
7687 Roo.bootstrap.Table = function(config){
7688 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7694 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7695 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7696 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700 this.sm.grid = this;
7701 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7702 this.sm = this.selModel;
7703 this.sm.xmodule = this.xmodule || false;
7706 if (this.cm && typeof(this.cm.config) == 'undefined') {
7707 this.colModel = new Roo.grid.ColumnModel(this.cm);
7708 this.cm = this.colModel;
7709 this.cm.xmodule = this.xmodule || false;
7712 this.store= Roo.factory(this.store, Roo.data);
7713 this.ds = this.store;
7714 this.ds.xmodule = this.xmodule || false;
7717 if (this.footer && this.store) {
7718 this.footer.dataSource = this.ds;
7719 this.footer = Roo.factory(this.footer);
7726 * Fires when a cell is clicked
7727 * @param {Roo.bootstrap.Table} this
7728 * @param {Roo.Element} el
7729 * @param {Number} rowIndex
7730 * @param {Number} columnIndex
7731 * @param {Roo.EventObject} e
7735 * @event celldblclick
7736 * Fires when a cell is double clicked
7737 * @param {Roo.bootstrap.Table} this
7738 * @param {Roo.Element} el
7739 * @param {Number} rowIndex
7740 * @param {Number} columnIndex
7741 * @param {Roo.EventObject} e
7743 "celldblclick" : true,
7746 * Fires when a row is clicked
7747 * @param {Roo.bootstrap.Table} this
7748 * @param {Roo.Element} el
7749 * @param {Number} rowIndex
7750 * @param {Roo.EventObject} e
7754 * @event rowdblclick
7755 * Fires when a row is double clicked
7756 * @param {Roo.bootstrap.Table} this
7757 * @param {Roo.Element} el
7758 * @param {Number} rowIndex
7759 * @param {Roo.EventObject} e
7761 "rowdblclick" : true,
7764 * Fires when a mouseover occur
7765 * @param {Roo.bootstrap.Table} this
7766 * @param {Roo.Element} el
7767 * @param {Number} rowIndex
7768 * @param {Number} columnIndex
7769 * @param {Roo.EventObject} e
7774 * Fires when a mouseout occur
7775 * @param {Roo.bootstrap.Table} this
7776 * @param {Roo.Element} el
7777 * @param {Number} rowIndex
7778 * @param {Number} columnIndex
7779 * @param {Roo.EventObject} e
7784 * Fires when a row is rendered, so you can change add a style to it.
7785 * @param {Roo.bootstrap.Table} this
7786 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7790 * @event rowsrendered
7791 * Fires when all the rows have been rendered
7792 * @param {Roo.bootstrap.Table} this
7794 'rowsrendered' : true,
7796 * @event contextmenu
7797 * The raw contextmenu event for the entire grid.
7798 * @param {Roo.EventObject} e
7800 "contextmenu" : true,
7802 * @event rowcontextmenu
7803 * Fires when a row is right clicked
7804 * @param {Roo.bootstrap.Table} this
7805 * @param {Number} rowIndex
7806 * @param {Roo.EventObject} e
7808 "rowcontextmenu" : true,
7810 * @event cellcontextmenu
7811 * Fires when a cell is right clicked
7812 * @param {Roo.bootstrap.Table} this
7813 * @param {Number} rowIndex
7814 * @param {Number} cellIndex
7815 * @param {Roo.EventObject} e
7817 "cellcontextmenu" : true,
7819 * @event headercontextmenu
7820 * Fires when a header is right clicked
7821 * @param {Roo.bootstrap.Table} this
7822 * @param {Number} columnIndex
7823 * @param {Roo.EventObject} e
7825 "headercontextmenu" : true
7829 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7855 rowSelection : false,
7856 cellSelection : false,
7859 // Roo.Element - the tbody
7861 // Roo.Element - thead element
7864 container: false, // used by gridpanel...
7870 auto_hide_footer : false,
7872 getAutoCreate : function()
7874 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7881 if (this.scrollBody) {
7882 cfg.cls += ' table-body-fixed';
7885 cfg.cls += ' table-striped';
7889 cfg.cls += ' table-hover';
7891 if (this.bordered) {
7892 cfg.cls += ' table-bordered';
7894 if (this.condensed) {
7895 cfg.cls += ' table-condensed';
7897 if (this.responsive) {
7898 cfg.cls += ' table-responsive';
7902 cfg.cls+= ' ' +this.cls;
7905 // this lot should be simplifed...
7918 ].forEach(function(k) {
7926 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7929 if(this.store || this.cm){
7930 if(this.headerShow){
7931 cfg.cn.push(this.renderHeader());
7934 cfg.cn.push(this.renderBody());
7936 if(this.footerShow){
7937 cfg.cn.push(this.renderFooter());
7939 // where does this come from?
7940 //cfg.cls+= ' TableGrid';
7943 return { cn : [ cfg ] };
7946 initEvents : function()
7948 if(!this.store || !this.cm){
7951 if (this.selModel) {
7952 this.selModel.initEvents();
7956 //Roo.log('initEvents with ds!!!!');
7958 this.mainBody = this.el.select('tbody', true).first();
7959 this.mainHead = this.el.select('thead', true).first();
7960 this.mainFoot = this.el.select('tfoot', true).first();
7966 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7967 e.on('click', _this.sort, _this);
7970 this.mainBody.on("click", this.onClick, this);
7971 this.mainBody.on("dblclick", this.onDblClick, this);
7973 // why is this done????? = it breaks dialogs??
7974 //this.parent().el.setStyle('position', 'relative');
7978 this.footer.parentId = this.id;
7979 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7982 this.el.select('tfoot tr td').first().addClass('hide');
7987 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7990 this.store.on('load', this.onLoad, this);
7991 this.store.on('beforeload', this.onBeforeLoad, this);
7992 this.store.on('update', this.onUpdate, this);
7993 this.store.on('add', this.onAdd, this);
7994 this.store.on("clear", this.clear, this);
7996 this.el.on("contextmenu", this.onContextMenu, this);
7998 this.mainBody.on('scroll', this.onBodyScroll, this);
8000 this.cm.on("headerchange", this.onHeaderChange, this);
8002 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8006 onContextMenu : function(e, t)
8008 this.processEvent("contextmenu", e);
8011 processEvent : function(name, e)
8013 if (name != 'touchstart' ) {
8014 this.fireEvent(name, e);
8017 var t = e.getTarget();
8019 var cell = Roo.get(t);
8025 if(cell.findParent('tfoot', false, true)){
8029 if(cell.findParent('thead', false, true)){
8031 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8032 cell = Roo.get(t).findParent('th', false, true);
8034 Roo.log("failed to find th in thead?");
8035 Roo.log(e.getTarget());
8040 var cellIndex = cell.dom.cellIndex;
8042 var ename = name == 'touchstart' ? 'click' : name;
8043 this.fireEvent("header" + ename, this, cellIndex, e);
8048 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8049 cell = Roo.get(t).findParent('td', false, true);
8051 Roo.log("failed to find th in tbody?");
8052 Roo.log(e.getTarget());
8057 var row = cell.findParent('tr', false, true);
8058 var cellIndex = cell.dom.cellIndex;
8059 var rowIndex = row.dom.rowIndex - 1;
8063 this.fireEvent("row" + name, this, rowIndex, e);
8067 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8073 onMouseover : function(e, el)
8075 var cell = Roo.get(el);
8081 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8082 cell = cell.findParent('td', false, true);
8085 var row = cell.findParent('tr', false, true);
8086 var cellIndex = cell.dom.cellIndex;
8087 var rowIndex = row.dom.rowIndex - 1; // start from 0
8089 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8093 onMouseout : function(e, el)
8095 var cell = Roo.get(el);
8101 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8102 cell = cell.findParent('td', false, true);
8105 var row = cell.findParent('tr', false, true);
8106 var cellIndex = cell.dom.cellIndex;
8107 var rowIndex = row.dom.rowIndex - 1; // start from 0
8109 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8113 onClick : function(e, el)
8115 var cell = Roo.get(el);
8117 if(!cell || (!this.cellSelection && !this.rowSelection)){
8121 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8122 cell = cell.findParent('td', false, true);
8125 if(!cell || typeof(cell) == 'undefined'){
8129 var row = cell.findParent('tr', false, true);
8131 if(!row || typeof(row) == 'undefined'){
8135 var cellIndex = cell.dom.cellIndex;
8136 var rowIndex = this.getRowIndex(row);
8138 // why??? - should these not be based on SelectionModel?
8139 if(this.cellSelection){
8140 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8143 if(this.rowSelection){
8144 this.fireEvent('rowclick', this, row, rowIndex, e);
8150 onDblClick : function(e,el)
8152 var cell = Roo.get(el);
8154 if(!cell || (!this.cellSelection && !this.rowSelection)){
8158 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8159 cell = cell.findParent('td', false, true);
8162 if(!cell || typeof(cell) == 'undefined'){
8166 var row = cell.findParent('tr', false, true);
8168 if(!row || typeof(row) == 'undefined'){
8172 var cellIndex = cell.dom.cellIndex;
8173 var rowIndex = this.getRowIndex(row);
8175 if(this.cellSelection){
8176 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8179 if(this.rowSelection){
8180 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8184 sort : function(e,el)
8186 var col = Roo.get(el);
8188 if(!col.hasClass('sortable')){
8192 var sort = col.attr('sort');
8195 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8199 this.store.sortInfo = {field : sort, direction : dir};
8202 Roo.log("calling footer first");
8203 this.footer.onClick('first');
8206 this.store.load({ params : { start : 0 } });
8210 renderHeader : function()
8218 this.totalWidth = 0;
8220 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222 var config = cm.config[i];
8226 cls : 'x-hcol-' + i,
8228 html: cm.getColumnHeader(i)
8233 if(typeof(config.sortable) != 'undefined' && config.sortable){
8235 c.html = '<i class="glyphicon"></i>' + c.html;
8238 // could use BS4 hidden-..-down
8240 if(typeof(config.lgHeader) != 'undefined'){
8241 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8244 if(typeof(config.mdHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8248 if(typeof(config.smHeader) != 'undefined'){
8249 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8252 if(typeof(config.xsHeader) != 'undefined'){
8253 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8260 if(typeof(config.tooltip) != 'undefined'){
8261 c.tooltip = config.tooltip;
8264 if(typeof(config.colspan) != 'undefined'){
8265 c.colspan = config.colspan;
8268 if(typeof(config.hidden) != 'undefined' && config.hidden){
8269 c.style += ' display:none;';
8272 if(typeof(config.dataIndex) != 'undefined'){
8273 c.sort = config.dataIndex;
8278 if(typeof(config.align) != 'undefined' && config.align.length){
8279 c.style += ' text-align:' + config.align + ';';
8282 if(typeof(config.width) != 'undefined'){
8283 c.style += ' width:' + config.width + 'px;';
8284 this.totalWidth += config.width;
8286 this.totalWidth += 100; // assume minimum of 100 per column?
8289 if(typeof(config.cls) != 'undefined'){
8290 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8293 ['xs','sm','md','lg'].map(function(size){
8295 if(typeof(config[size]) == 'undefined'){
8299 if (!config[size]) { // 0 = hidden
8300 // BS 4 '0' is treated as hide that column and below.
8301 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8305 c.cls += ' col-' + size + '-' + config[size] + (
8306 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8318 renderBody : function()
8328 colspan : this.cm.getColumnCount()
8338 renderFooter : function()
8348 colspan : this.cm.getColumnCount()
8362 // Roo.log('ds onload');
8367 var ds = this.store;
8369 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8370 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8371 if (_this.store.sortInfo) {
8373 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8374 e.select('i', true).addClass(['glyphicon-arrow-up']);
8377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8378 e.select('i', true).addClass(['glyphicon-arrow-down']);
8383 var tbody = this.mainBody;
8385 if(ds.getCount() > 0){
8386 ds.data.each(function(d,rowIndex){
8387 var row = this.renderRow(cm, ds, rowIndex);
8389 tbody.createChild(row);
8393 if(row.cellObjects.length){
8394 Roo.each(row.cellObjects, function(r){
8395 _this.renderCellObject(r);
8402 var tfoot = this.el.select('tfoot', true).first();
8404 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408 var total = this.ds.getTotalCount();
8410 if(this.footer.pageSize < total){
8411 this.mainFoot.show();
8415 Roo.each(this.el.select('tbody td', true).elements, function(e){
8416 e.on('mouseover', _this.onMouseover, _this);
8419 Roo.each(this.el.select('tbody td', true).elements, function(e){
8420 e.on('mouseout', _this.onMouseout, _this);
8422 this.fireEvent('rowsrendered', this);
8428 onUpdate : function(ds,record)
8430 this.refreshRow(record);
8434 onRemove : function(ds, record, index, isUpdate){
8435 if(isUpdate !== true){
8436 this.fireEvent("beforerowremoved", this, index, record);
8438 var bt = this.mainBody.dom;
8440 var rows = this.el.select('tbody > tr', true).elements;
8442 if(typeof(rows[index]) != 'undefined'){
8443 bt.removeChild(rows[index].dom);
8446 // if(bt.rows[index]){
8447 // bt.removeChild(bt.rows[index]);
8450 if(isUpdate !== true){
8451 //this.stripeRows(index);
8452 //this.syncRowHeights(index, index);
8454 this.fireEvent("rowremoved", this, index, record);
8458 onAdd : function(ds, records, rowIndex)
8460 //Roo.log('on Add called');
8461 // - note this does not handle multiple adding very well..
8462 var bt = this.mainBody.dom;
8463 for (var i =0 ; i < records.length;i++) {
8464 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8465 //Roo.log(records[i]);
8466 //Roo.log(this.store.getAt(rowIndex+i));
8467 this.insertRow(this.store, rowIndex + i, false);
8474 refreshRow : function(record){
8475 var ds = this.store, index;
8476 if(typeof record == 'number'){
8478 record = ds.getAt(index);
8480 index = ds.indexOf(record);
8482 return; // should not happen - but seems to
8485 this.insertRow(ds, index, true);
8487 this.onRemove(ds, record, index+1, true);
8489 //this.syncRowHeights(index, index);
8491 this.fireEvent("rowupdated", this, index, record);
8494 insertRow : function(dm, rowIndex, isUpdate){
8497 this.fireEvent("beforerowsinserted", this, rowIndex);
8499 //var s = this.getScrollState();
8500 var row = this.renderRow(this.cm, this.store, rowIndex);
8501 // insert before rowIndex..
8502 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8506 if(row.cellObjects.length){
8507 Roo.each(row.cellObjects, function(r){
8508 _this.renderCellObject(r);
8513 this.fireEvent("rowsinserted", this, rowIndex);
8514 //this.syncRowHeights(firstRow, lastRow);
8515 //this.stripeRows(firstRow);
8522 getRowDom : function(rowIndex)
8524 var rows = this.el.select('tbody > tr', true).elements;
8526 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8529 // returns the object tree for a tr..
8532 renderRow : function(cm, ds, rowIndex)
8534 var d = ds.getAt(rowIndex);
8538 cls : 'x-row-' + rowIndex,
8542 var cellObjects = [];
8544 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8545 var config = cm.config[i];
8547 var renderer = cm.getRenderer(i);
8551 if(typeof(renderer) !== 'undefined'){
8552 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8555 // and are rendered into the cells after the row is rendered - using the id for the element.
8557 if(typeof(value) === 'object'){
8567 rowIndex : rowIndex,
8572 this.fireEvent('rowclass', this, rowcfg);
8576 cls : rowcfg.rowClass + ' x-col-' + i,
8578 html: (typeof(value) === 'object') ? '' : value
8585 if(typeof(config.colspan) != 'undefined'){
8586 td.colspan = config.colspan;
8589 if(typeof(config.hidden) != 'undefined' && config.hidden){
8590 td.style += ' display:none;';
8593 if(typeof(config.align) != 'undefined' && config.align.length){
8594 td.style += ' text-align:' + config.align + ';';
8596 if(typeof(config.valign) != 'undefined' && config.valign.length){
8597 td.style += ' vertical-align:' + config.valign + ';';
8600 if(typeof(config.width) != 'undefined'){
8601 td.style += ' width:' + config.width + 'px;';
8604 if(typeof(config.cursor) != 'undefined'){
8605 td.style += ' cursor:' + config.cursor + ';';
8608 if(typeof(config.cls) != 'undefined'){
8609 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8612 ['xs','sm','md','lg'].map(function(size){
8614 if(typeof(config[size]) == 'undefined'){
8620 if (!config[size]) { // 0 = hidden
8621 // BS 4 '0' is treated as hide that column and below.
8622 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8626 td.cls += ' col-' + size + '-' + config[size] + (
8627 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8637 row.cellObjects = cellObjects;
8645 onBeforeLoad : function()
8654 this.el.select('tbody', true).first().dom.innerHTML = '';
8657 * Show or hide a row.
8658 * @param {Number} rowIndex to show or hide
8659 * @param {Boolean} state hide
8661 setRowVisibility : function(rowIndex, state)
8663 var bt = this.mainBody.dom;
8665 var rows = this.el.select('tbody > tr', true).elements;
8667 if(typeof(rows[rowIndex]) == 'undefined'){
8670 rows[rowIndex].dom.style.display = state ? '' : 'none';
8674 getSelectionModel : function(){
8676 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678 return this.selModel;
8681 * Render the Roo.bootstrap object from renderder
8683 renderCellObject : function(r)
8687 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689 var t = r.cfg.render(r.container);
8692 Roo.each(r.cfg.cn, function(c){
8694 container: t.getChildContainer(),
8697 _this.renderCellObject(child);
8702 getRowIndex : function(row)
8706 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8717 * Returns the grid's underlying element = used by panel.Grid
8718 * @return {Element} The element
8720 getGridEl : function(){
8724 * Forces a resize - used by panel.Grid
8725 * @return {Element} The element
8727 autoSize : function()
8729 //var ctr = Roo.get(this.container.dom.parentElement);
8730 var ctr = Roo.get(this.el.dom);
8732 var thd = this.getGridEl().select('thead',true).first();
8733 var tbd = this.getGridEl().select('tbody', true).first();
8734 var tfd = this.getGridEl().select('tfoot', true).first();
8736 var cw = ctr.getWidth();
8737 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8741 tbd.setWidth(ctr.getWidth());
8742 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8743 // this needs fixing for various usage - currently only hydra job advers I think..
8745 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8750 cw = Math.max(cw, this.totalWidth);
8751 this.getGridEl().select('tbody tr',true).setWidth(cw);
8753 // resize 'expandable coloumn?
8755 return; // we doe not have a view in this design..
8758 onBodyScroll: function()
8760 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762 this.mainHead.setStyle({
8763 'position' : 'relative',
8764 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8770 var scrollHeight = this.mainBody.dom.scrollHeight;
8772 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774 var height = this.mainBody.getHeight();
8776 if(scrollHeight - height == scrollTop) {
8778 var total = this.ds.getTotalCount();
8780 if(this.footer.cursor + this.footer.pageSize < total){
8782 this.footer.ds.load({
8784 start : this.footer.cursor + this.footer.pageSize,
8785 limit : this.footer.pageSize
8795 onHeaderChange : function()
8797 var header = this.renderHeader();
8798 var table = this.el.select('table', true).first();
8800 this.mainHead.remove();
8801 this.mainHead = table.createChild(header, this.mainBody, false);
8804 onHiddenChange : function(colModel, colIndex, hidden)
8806 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8807 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809 this.CSS.updateRule(thSelector, "display", "");
8810 this.CSS.updateRule(tdSelector, "display", "");
8813 this.CSS.updateRule(thSelector, "display", "none");
8814 this.CSS.updateRule(tdSelector, "display", "none");
8817 this.onHeaderChange();
8821 setColumnWidth: function(col_index, width)
8823 // width = "md-2 xs-2..."
8824 if(!this.colModel.config[col_index]) {
8828 var w = width.split(" ");
8830 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8835 for(var j = 0; j < w.length; j++) {
8841 var size_cls = w[j].split("-");
8843 if(!Number.isInteger(size_cls[1] * 1)) {
8847 if(!this.colModel.config[col_index][size_cls[0]]) {
8851 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8855 h_row[0].classList.replace(
8856 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8857 "col-"+size_cls[0]+"-"+size_cls[1]
8860 for(var i = 0; i < rows.length; i++) {
8862 var size_cls = w[j].split("-");
8864 if(!Number.isInteger(size_cls[1] * 1)) {
8868 if(!this.colModel.config[col_index][size_cls[0]]) {
8872 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8876 rows[i].classList.replace(
8877 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8878 "col-"+size_cls[0]+"-"+size_cls[1]
8882 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8897 * @class Roo.bootstrap.TableCell
8898 * @extends Roo.bootstrap.Component
8899 * Bootstrap TableCell class
8900 * @cfg {String} html cell contain text
8901 * @cfg {String} cls cell class
8902 * @cfg {String} tag cell tag (td|th) default td
8903 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8904 * @cfg {String} align Aligns the content in a cell
8905 * @cfg {String} axis Categorizes cells
8906 * @cfg {String} bgcolor Specifies the background color of a cell
8907 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8908 * @cfg {Number} colspan Specifies the number of columns a cell should span
8909 * @cfg {String} headers Specifies one or more header cells a cell is related to
8910 * @cfg {Number} height Sets the height of a cell
8911 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8912 * @cfg {Number} rowspan Sets the number of rows a cell should span
8913 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8914 * @cfg {String} valign Vertical aligns the content in a cell
8915 * @cfg {Number} width Specifies the width of a cell
8918 * Create a new TableCell
8919 * @param {Object} config The config object
8922 Roo.bootstrap.TableCell = function(config){
8923 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8926 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8946 getAutoCreate : function(){
8947 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8967 cfg.align=this.align
8973 cfg.bgcolor=this.bgcolor
8976 cfg.charoff=this.charoff
8979 cfg.colspan=this.colspan
8982 cfg.headers=this.headers
8985 cfg.height=this.height
8988 cfg.nowrap=this.nowrap
8991 cfg.rowspan=this.rowspan
8994 cfg.scope=this.scope
8997 cfg.valign=this.valign
9000 cfg.width=this.width
9019 * @class Roo.bootstrap.TableRow
9020 * @extends Roo.bootstrap.Component
9021 * Bootstrap TableRow class
9022 * @cfg {String} cls row class
9023 * @cfg {String} align Aligns the content in a table row
9024 * @cfg {String} bgcolor Specifies a background color for a table row
9025 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9026 * @cfg {String} valign Vertical aligns the content in a table row
9029 * Create a new TableRow
9030 * @param {Object} config The config object
9033 Roo.bootstrap.TableRow = function(config){
9034 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9037 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9045 getAutoCreate : function(){
9046 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9056 cfg.align = this.align;
9059 cfg.bgcolor = this.bgcolor;
9062 cfg.charoff = this.charoff;
9065 cfg.valign = this.valign;
9083 * @class Roo.bootstrap.TableBody
9084 * @extends Roo.bootstrap.Component
9085 * Bootstrap TableBody class
9086 * @cfg {String} cls element class
9087 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9088 * @cfg {String} align Aligns the content inside the element
9089 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9090 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9093 * Create a new TableBody
9094 * @param {Object} config The config object
9097 Roo.bootstrap.TableBody = function(config){
9098 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9101 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9109 getAutoCreate : function(){
9110 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9124 cfg.align = this.align;
9127 cfg.charoff = this.charoff;
9130 cfg.valign = this.valign;
9137 // initEvents : function()
9144 // this.store = Roo.factory(this.store, Roo.data);
9145 // this.store.on('load', this.onLoad, this);
9147 // this.store.load();
9151 // onLoad: function ()
9153 // this.fireEvent('load', this);
9163 * Ext JS Library 1.1.1
9164 * Copyright(c) 2006-2007, Ext JS, LLC.
9166 * Originally Released Under LGPL - original licence link has changed is not relivant.
9169 * <script type="text/javascript">
9172 // as we use this in bootstrap.
9173 Roo.namespace('Roo.form');
9175 * @class Roo.form.Action
9176 * Internal Class used to handle form actions
9178 * @param {Roo.form.BasicForm} el The form element or its id
9179 * @param {Object} config Configuration options
9184 // define the action interface
9185 Roo.form.Action = function(form, options){
9187 this.options = options || {};
9190 * Client Validation Failed
9193 Roo.form.Action.CLIENT_INVALID = 'client';
9195 * Server Validation Failed
9198 Roo.form.Action.SERVER_INVALID = 'server';
9200 * Connect to Server Failed
9203 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 * Reading Data from Server Failed
9208 Roo.form.Action.LOAD_FAILURE = 'load';
9210 Roo.form.Action.prototype = {
9212 failureType : undefined,
9213 response : undefined,
9217 run : function(options){
9222 success : function(response){
9227 handleResponse : function(response){
9231 // default connection failure
9232 failure : function(response){
9234 this.response = response;
9235 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9236 this.form.afterAction(this, false);
9239 processResponse : function(response){
9240 this.response = response;
9241 if(!response.responseText){
9244 this.result = this.handleResponse(response);
9248 // utility functions used internally
9249 getUrl : function(appendParams){
9250 var url = this.options.url || this.form.url || this.form.el.dom.action;
9252 var p = this.getParams();
9254 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9260 getMethod : function(){
9261 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9264 getParams : function(){
9265 var bp = this.form.baseParams;
9266 var p = this.options.params;
9268 if(typeof p == "object"){
9269 p = Roo.urlEncode(Roo.applyIf(p, bp));
9270 }else if(typeof p == 'string' && bp){
9271 p += '&' + Roo.urlEncode(bp);
9274 p = Roo.urlEncode(bp);
9279 createCallback : function(){
9281 success: this.success,
9282 failure: this.failure,
9284 timeout: (this.form.timeout*1000),
9285 upload: this.form.fileUpload ? this.success : undefined
9290 Roo.form.Action.Submit = function(form, options){
9291 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9294 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9297 haveProgress : false,
9298 uploadComplete : false,
9300 // uploadProgress indicator.
9301 uploadProgress : function()
9303 if (!this.form.progressUrl) {
9307 if (!this.haveProgress) {
9308 Roo.MessageBox.progress("Uploading", "Uploading");
9310 if (this.uploadComplete) {
9311 Roo.MessageBox.hide();
9315 this.haveProgress = true;
9317 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319 var c = new Roo.data.Connection();
9321 url : this.form.progressUrl,
9326 success : function(req){
9327 //console.log(data);
9331 rdata = Roo.decode(req.responseText)
9333 Roo.log("Invalid data from server..");
9337 if (!rdata || !rdata.success) {
9339 Roo.MessageBox.alert(Roo.encode(rdata));
9342 var data = rdata.data;
9344 if (this.uploadComplete) {
9345 Roo.MessageBox.hide();
9350 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9351 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9354 this.uploadProgress.defer(2000,this);
9357 failure: function(data) {
9358 Roo.log('progress url failed ');
9369 // run get Values on the form, so it syncs any secondary forms.
9370 this.form.getValues();
9372 var o = this.options;
9373 var method = this.getMethod();
9374 var isPost = method == 'POST';
9375 if(o.clientValidation === false || this.form.isValid()){
9377 if (this.form.progressUrl) {
9378 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9379 (new Date() * 1) + '' + Math.random());
9384 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9385 form:this.form.el.dom,
9386 url:this.getUrl(!isPost),
9388 params:isPost ? this.getParams() : null,
9389 isUpload: this.form.fileUpload,
9390 formData : this.form.formData
9393 this.uploadProgress();
9395 }else if (o.clientValidation !== false){ // client validation failed
9396 this.failureType = Roo.form.Action.CLIENT_INVALID;
9397 this.form.afterAction(this, false);
9401 success : function(response)
9403 this.uploadComplete= true;
9404 if (this.haveProgress) {
9405 Roo.MessageBox.hide();
9409 var result = this.processResponse(response);
9410 if(result === true || result.success){
9411 this.form.afterAction(this, true);
9415 this.form.markInvalid(result.errors);
9416 this.failureType = Roo.form.Action.SERVER_INVALID;
9418 this.form.afterAction(this, false);
9420 failure : function(response)
9422 this.uploadComplete= true;
9423 if (this.haveProgress) {
9424 Roo.MessageBox.hide();
9427 this.response = response;
9428 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9429 this.form.afterAction(this, false);
9432 handleResponse : function(response){
9433 if(this.form.errorReader){
9434 var rs = this.form.errorReader.read(response);
9437 for(var i = 0, len = rs.records.length; i < len; i++) {
9438 var r = rs.records[i];
9442 if(errors.length < 1){
9446 success : rs.success,
9452 ret = Roo.decode(response.responseText);
9456 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9466 Roo.form.Action.Load = function(form, options){
9467 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9468 this.reader = this.form.reader;
9471 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476 Roo.Ajax.request(Roo.apply(
9477 this.createCallback(), {
9478 method:this.getMethod(),
9479 url:this.getUrl(false),
9480 params:this.getParams()
9484 success : function(response){
9486 var result = this.processResponse(response);
9487 if(result === true || !result.success || !result.data){
9488 this.failureType = Roo.form.Action.LOAD_FAILURE;
9489 this.form.afterAction(this, false);
9492 this.form.clearInvalid();
9493 this.form.setValues(result.data);
9494 this.form.afterAction(this, true);
9497 handleResponse : function(response){
9498 if(this.form.reader){
9499 var rs = this.form.reader.read(response);
9500 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502 success : rs.success,
9506 return Roo.decode(response.responseText);
9510 Roo.form.Action.ACTION_TYPES = {
9511 'load' : Roo.form.Action.Load,
9512 'submit' : Roo.form.Action.Submit
9521 * @class Roo.bootstrap.Form
9522 * @extends Roo.bootstrap.Component
9523 * Bootstrap Form class
9524 * @cfg {String} method GET | POST (default POST)
9525 * @cfg {String} labelAlign top | left (default top)
9526 * @cfg {String} align left | right - for navbars
9527 * @cfg {Boolean} loadMask load mask when submit (default true)
9532 * @param {Object} config The config object
9536 Roo.bootstrap.Form = function(config){
9538 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540 Roo.bootstrap.Form.popover.apply();
9544 * @event clientvalidation
9545 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9546 * @param {Form} this
9547 * @param {Boolean} valid true if the form has passed client-side validation
9549 clientvalidation: true,
9551 * @event beforeaction
9552 * Fires before any action is performed. Return false to cancel the action.
9553 * @param {Form} this
9554 * @param {Action} action The action to be performed
9558 * @event actionfailed
9559 * Fires when an action fails.
9560 * @param {Form} this
9561 * @param {Action} action The action that failed
9563 actionfailed : true,
9565 * @event actioncomplete
9566 * Fires when an action is completed.
9567 * @param {Form} this
9568 * @param {Action} action The action that completed
9570 actioncomplete : true
9574 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9577 * @cfg {String} method
9578 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583 * The URL to use for form actions if one isn't supplied in the action options.
9586 * @cfg {Boolean} fileUpload
9587 * Set to true if this form is a file upload.
9591 * @cfg {Object} baseParams
9592 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9596 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9600 * @cfg {Sting} align (left|right) for navbar forms
9605 activeAction : null,
9608 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9609 * element by passing it or its id or mask the form itself by passing in true.
9612 waitMsgTarget : false,
9617 * @cfg {Boolean} errorMask (true|false) default false
9622 * @cfg {Number} maskOffset Default 100
9627 * @cfg {Boolean} maskBody
9631 getAutoCreate : function(){
9635 method : this.method || 'POST',
9636 id : this.id || Roo.id(),
9639 if (this.parent().xtype.match(/^Nav/)) {
9640 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9644 if (this.labelAlign == 'left' ) {
9645 cfg.cls += ' form-horizontal';
9651 initEvents : function()
9653 this.el.on('submit', this.onSubmit, this);
9654 // this was added as random key presses on the form where triggering form submit.
9655 this.el.on('keypress', function(e) {
9656 if (e.getCharCode() != 13) {
9659 // we might need to allow it for textareas.. and some other items.
9660 // check e.getTarget().
9662 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9666 Roo.log("keypress blocked");
9674 onSubmit : function(e){
9679 * Returns true if client-side validation on the form is successful.
9682 isValid : function(){
9683 var items = this.getItems();
9687 items.each(function(f){
9693 Roo.log('invalid field: ' + f.name);
9697 if(!target && f.el.isVisible(true)){
9703 if(this.errorMask && !valid){
9704 Roo.bootstrap.Form.popover.mask(this, target);
9711 * Returns true if any fields in this form have changed since their original load.
9714 isDirty : function(){
9716 var items = this.getItems();
9717 items.each(function(f){
9727 * Performs a predefined action (submit or load) or custom actions you define on this form.
9728 * @param {String} actionName The name of the action type
9729 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9730 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9731 * accept other config options):
9733 Property Type Description
9734 ---------------- --------------- ----------------------------------------------------------------------------------
9735 url String The url for the action (defaults to the form's url)
9736 method String The form method to use (defaults to the form's method, or POST if not defined)
9737 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9738 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9739 validate the form on the client (defaults to false)
9741 * @return {BasicForm} this
9743 doAction : function(action, options){
9744 if(typeof action == 'string'){
9745 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747 if(this.fireEvent('beforeaction', this, action) !== false){
9748 this.beforeAction(action);
9749 action.run.defer(100, action);
9755 beforeAction : function(action){
9756 var o = action.options;
9761 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766 // not really supported yet.. ??
9768 //if(this.waitMsgTarget === true){
9769 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 //}else if(this.waitMsgTarget){
9771 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9772 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9780 afterAction : function(action, success){
9781 this.activeAction = null;
9782 var o = action.options;
9787 Roo.get(document.body).unmask();
9793 //if(this.waitMsgTarget === true){
9794 // this.el.unmask();
9795 //}else if(this.waitMsgTarget){
9796 // this.waitMsgTarget.unmask();
9798 // Roo.MessageBox.updateProgress(1);
9799 // Roo.MessageBox.hide();
9806 Roo.callback(o.success, o.scope, [this, action]);
9807 this.fireEvent('actioncomplete', this, action);
9811 // failure condition..
9812 // we have a scenario where updates need confirming.
9813 // eg. if a locking scenario exists..
9814 // we look for { errors : { needs_confirm : true }} in the response.
9816 (typeof(action.result) != 'undefined') &&
9817 (typeof(action.result.errors) != 'undefined') &&
9818 (typeof(action.result.errors.needs_confirm) != 'undefined')
9821 Roo.log("not supported yet");
9824 Roo.MessageBox.confirm(
9825 "Change requires confirmation",
9826 action.result.errorMsg,
9831 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9841 Roo.callback(o.failure, o.scope, [this, action]);
9842 // show an error message if no failed handler is set..
9843 if (!this.hasListener('actionfailed')) {
9844 Roo.log("need to add dialog support");
9846 Roo.MessageBox.alert("Error",
9847 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9848 action.result.errorMsg :
9849 "Saving Failed, please check your entries or try again"
9854 this.fireEvent('actionfailed', this, action);
9859 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9860 * @param {String} id The value to search for
9863 findField : function(id){
9864 var items = this.getItems();
9865 var field = items.get(id);
9867 items.each(function(f){
9868 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9875 return field || null;
9878 * Mark fields in this form invalid in bulk.
9879 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9880 * @return {BasicForm} this
9882 markInvalid : function(errors){
9883 if(errors instanceof Array){
9884 for(var i = 0, len = errors.length; i < len; i++){
9885 var fieldError = errors[i];
9886 var f = this.findField(fieldError.id);
9888 f.markInvalid(fieldError.msg);
9894 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9895 field.markInvalid(errors[id]);
9899 //Roo.each(this.childForms || [], function (f) {
9900 // f.markInvalid(errors);
9907 * Set values for fields in this form in bulk.
9908 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9909 * @return {BasicForm} this
9911 setValues : function(values){
9912 if(values instanceof Array){ // array of objects
9913 for(var i = 0, len = values.length; i < len; i++){
9915 var f = this.findField(v.id);
9917 f.setValue(v.value);
9918 if(this.trackResetOnLoad){
9919 f.originalValue = f.getValue();
9923 }else{ // object hash
9926 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928 if (field.setFromData &&
9930 field.displayField &&
9931 // combos' with local stores can
9932 // be queried via setValue()
9933 // to set their value..
9934 (field.store && !field.store.isLocal)
9938 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9939 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9940 field.setFromData(sd);
9942 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944 field.setFromData(values);
9947 field.setValue(values[id]);
9951 if(this.trackResetOnLoad){
9952 field.originalValue = field.getValue();
9958 //Roo.each(this.childForms || [], function (f) {
9959 // f.setValues(values);
9966 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9967 * they are returned as an array.
9968 * @param {Boolean} asString
9971 getValues : function(asString){
9972 //if (this.childForms) {
9973 // copy values from the child forms
9974 // Roo.each(this.childForms, function (f) {
9975 // this.setValues(f.getValues());
9981 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9982 if(asString === true){
9985 return Roo.urlDecode(fs);
9989 * Returns the fields in this form as an object with key/value pairs.
9990 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9993 getFieldValues : function(with_hidden)
9995 var items = this.getItems();
9997 items.each(function(f){
10003 var v = f.getValue();
10005 if (f.inputType =='radio') {
10006 if (typeof(ret[f.getName()]) == 'undefined') {
10007 ret[f.getName()] = ''; // empty..
10010 if (!f.el.dom.checked) {
10014 v = f.el.dom.value;
10018 if(f.xtype == 'MoneyField'){
10019 ret[f.currencyName] = f.getCurrency();
10022 // not sure if this supported any more..
10023 if ((typeof(v) == 'object') && f.getRawValue) {
10024 v = f.getRawValue() ; // dates..
10026 // combo boxes where name != hiddenName...
10027 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10028 ret[f.name] = f.getRawValue();
10030 ret[f.getName()] = v;
10037 * Clears all invalid messages in this form.
10038 * @return {BasicForm} this
10040 clearInvalid : function(){
10041 var items = this.getItems();
10043 items.each(function(f){
10051 * Resets this form.
10052 * @return {BasicForm} this
10054 reset : function(){
10055 var items = this.getItems();
10056 items.each(function(f){
10060 Roo.each(this.childForms || [], function (f) {
10068 getItems : function()
10070 var r=new Roo.util.MixedCollection(false, function(o){
10071 return o.id || (o.id = Roo.id());
10073 var iter = function(el) {
10080 Roo.each(el.items,function(e) {
10089 hideFields : function(items)
10091 Roo.each(items, function(i){
10093 var f = this.findField(i);
10104 showFields : function(items)
10106 Roo.each(items, function(i){
10108 var f = this.findField(i);
10121 Roo.apply(Roo.bootstrap.Form, {
10137 intervalID : false,
10143 if(this.isApplied){
10148 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10149 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10150 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10151 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10154 this.maskEl.top.enableDisplayMode("block");
10155 this.maskEl.left.enableDisplayMode("block");
10156 this.maskEl.bottom.enableDisplayMode("block");
10157 this.maskEl.right.enableDisplayMode("block");
10159 this.toolTip = new Roo.bootstrap.Tooltip({
10160 cls : 'roo-form-error-popover',
10162 'left' : ['r-l', [-2,0], 'right'],
10163 'right' : ['l-r', [2,0], 'left'],
10164 'bottom' : ['tl-bl', [0,2], 'top'],
10165 'top' : [ 'bl-tl', [0,-2], 'bottom']
10169 this.toolTip.render(Roo.get(document.body));
10171 this.toolTip.el.enableDisplayMode("block");
10173 Roo.get(document.body).on('click', function(){
10177 Roo.get(document.body).on('touchstart', function(){
10181 this.isApplied = true
10184 mask : function(form, target)
10188 this.target = target;
10190 if(!this.form.errorMask || !target.el){
10194 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196 Roo.log(scrollable);
10198 var ot = this.target.el.calcOffsetsTo(scrollable);
10200 var scrollTo = ot[1] - this.form.maskOffset;
10202 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204 scrollable.scrollTo('top', scrollTo);
10206 var box = this.target.el.getBox();
10208 var zIndex = Roo.bootstrap.Modal.zIndex++;
10211 this.maskEl.top.setStyle('position', 'absolute');
10212 this.maskEl.top.setStyle('z-index', zIndex);
10213 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10214 this.maskEl.top.setLeft(0);
10215 this.maskEl.top.setTop(0);
10216 this.maskEl.top.show();
10218 this.maskEl.left.setStyle('position', 'absolute');
10219 this.maskEl.left.setStyle('z-index', zIndex);
10220 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10221 this.maskEl.left.setLeft(0);
10222 this.maskEl.left.setTop(box.y - this.padding);
10223 this.maskEl.left.show();
10225 this.maskEl.bottom.setStyle('position', 'absolute');
10226 this.maskEl.bottom.setStyle('z-index', zIndex);
10227 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10228 this.maskEl.bottom.setLeft(0);
10229 this.maskEl.bottom.setTop(box.bottom + this.padding);
10230 this.maskEl.bottom.show();
10232 this.maskEl.right.setStyle('position', 'absolute');
10233 this.maskEl.right.setStyle('z-index', zIndex);
10234 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10235 this.maskEl.right.setLeft(box.right + this.padding);
10236 this.maskEl.right.setTop(box.y - this.padding);
10237 this.maskEl.right.show();
10239 this.toolTip.bindEl = this.target.el;
10241 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243 var tip = this.target.blankText;
10245 if(this.target.getValue() !== '' ) {
10247 if (this.target.invalidText.length) {
10248 tip = this.target.invalidText;
10249 } else if (this.target.regexText.length){
10250 tip = this.target.regexText;
10254 this.toolTip.show(tip);
10256 this.intervalID = window.setInterval(function() {
10257 Roo.bootstrap.Form.popover.unmask();
10260 window.onwheel = function(){ return false;};
10262 (function(){ this.isMasked = true; }).defer(500, this);
10266 unmask : function()
10268 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10272 this.maskEl.top.setStyle('position', 'absolute');
10273 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10274 this.maskEl.top.hide();
10276 this.maskEl.left.setStyle('position', 'absolute');
10277 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.left.hide();
10280 this.maskEl.bottom.setStyle('position', 'absolute');
10281 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.bottom.hide();
10284 this.maskEl.right.setStyle('position', 'absolute');
10285 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10286 this.maskEl.right.hide();
10288 this.toolTip.hide();
10290 this.toolTip.el.hide();
10292 window.onwheel = function(){ return true;};
10294 if(this.intervalID){
10295 window.clearInterval(this.intervalID);
10296 this.intervalID = false;
10299 this.isMasked = false;
10309 * Ext JS Library 1.1.1
10310 * Copyright(c) 2006-2007, Ext JS, LLC.
10312 * Originally Released Under LGPL - original licence link has changed is not relivant.
10315 * <script type="text/javascript">
10318 * @class Roo.form.VTypes
10319 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10322 Roo.form.VTypes = function(){
10323 // closure these in so they are only created once.
10324 var alpha = /^[a-zA-Z_]+$/;
10325 var alphanum = /^[a-zA-Z0-9_]+$/;
10326 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10327 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329 // All these messages and functions are configurable
10332 * The function used to validate email addresses
10333 * @param {String} value The email address
10335 'email' : function(v){
10336 return email.test(v);
10339 * The error text to display when the email validation function returns false
10342 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344 * The keystroke filter mask to be applied on email input
10347 'emailMask' : /[a-z0-9_\.\-@]/i,
10350 * The function used to validate URLs
10351 * @param {String} value The URL
10353 'url' : function(v){
10354 return url.test(v);
10357 * The error text to display when the url validation function returns false
10360 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10363 * The function used to validate alpha values
10364 * @param {String} value The value
10366 'alpha' : function(v){
10367 return alpha.test(v);
10370 * The error text to display when the alpha validation function returns false
10373 'alphaText' : 'This field should only contain letters and _',
10375 * The keystroke filter mask to be applied on alpha input
10378 'alphaMask' : /[a-z_]/i,
10381 * The function used to validate alphanumeric values
10382 * @param {String} value The value
10384 'alphanum' : function(v){
10385 return alphanum.test(v);
10388 * The error text to display when the alphanumeric validation function returns false
10391 'alphanumText' : 'This field should only contain letters, numbers and _',
10393 * The keystroke filter mask to be applied on alphanumeric input
10396 'alphanumMask' : /[a-z0-9_]/i
10406 * @class Roo.bootstrap.Input
10407 * @extends Roo.bootstrap.Component
10408 * Bootstrap Input class
10409 * @cfg {Boolean} disabled is it disabled
10410 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10411 * @cfg {String} name name of the input
10412 * @cfg {string} fieldLabel - the label associated
10413 * @cfg {string} placeholder - placeholder to put in text.
10414 * @cfg {string} before - input group add on before
10415 * @cfg {string} after - input group add on after
10416 * @cfg {string} size - (lg|sm) or leave empty..
10417 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10418 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10419 * @cfg {Number} md colspan out of 12 for computer-sized screens
10420 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10421 * @cfg {string} value default value of the input
10422 * @cfg {Number} labelWidth set the width of label
10423 * @cfg {Number} labellg set the width of label (1-12)
10424 * @cfg {Number} labelmd set the width of label (1-12)
10425 * @cfg {Number} labelsm set the width of label (1-12)
10426 * @cfg {Number} labelxs set the width of label (1-12)
10427 * @cfg {String} labelAlign (top|left)
10428 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10429 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10430 * @cfg {String} indicatorpos (left|right) default left
10431 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10432 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10433 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435 * @cfg {String} align (left|center|right) Default left
10436 * @cfg {Boolean} forceFeedback (true|false) Default false
10439 * Create a new Input
10440 * @param {Object} config The config object
10443 Roo.bootstrap.Input = function(config){
10445 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450 * Fires when this field receives input focus.
10451 * @param {Roo.form.Field} this
10456 * Fires when this field loses input focus.
10457 * @param {Roo.form.Field} this
10461 * @event specialkey
10462 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10463 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10464 * @param {Roo.form.Field} this
10465 * @param {Roo.EventObject} e The event object
10470 * Fires just before the field blurs if the field value has changed.
10471 * @param {Roo.form.Field} this
10472 * @param {Mixed} newValue The new value
10473 * @param {Mixed} oldValue The original value
10478 * Fires after the field has been marked as invalid.
10479 * @param {Roo.form.Field} this
10480 * @param {String} msg The validation message
10485 * Fires after the field has been validated with no errors.
10486 * @param {Roo.form.Field} this
10491 * Fires after the key up
10492 * @param {Roo.form.Field} this
10493 * @param {Roo.EventObject} e The event Object
10499 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10501 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10502 automatic validation (defaults to "keyup").
10504 validationEvent : "keyup",
10506 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508 validateOnBlur : true,
10510 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512 validationDelay : 250,
10514 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516 focusClass : "x-form-focus", // not needed???
10520 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522 invalidClass : "has-warning",
10525 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527 validClass : "has-success",
10530 * @cfg {Boolean} hasFeedback (true|false) default true
10532 hasFeedback : true,
10535 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537 invalidFeedbackClass : "glyphicon-warning-sign",
10540 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542 validFeedbackClass : "glyphicon-ok",
10545 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547 selectOnFocus : false,
10550 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10554 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10559 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561 disableKeyFilter : false,
10564 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10568 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10572 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574 blankText : "Please complete this mandatory field",
10577 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10581 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583 maxLength : Number.MAX_VALUE,
10585 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587 minLengthText : "The minimum length for this field is {0}",
10589 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591 maxLengthText : "The maximum length for this field is {0}",
10595 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10596 * If available, this function will be called only after the basic validators all return true, and will be passed the
10597 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10601 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10602 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10603 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10607 * @cfg {String} regexText -- Depricated - use Invalid Text
10612 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10618 autocomplete: false,
10622 inputType : 'text',
10625 placeholder: false,
10630 preventMark: false,
10631 isFormField : true,
10634 labelAlign : false,
10637 formatedValue : false,
10638 forceFeedback : false,
10640 indicatorpos : 'left',
10650 parentLabelAlign : function()
10653 while (parent.parent()) {
10654 parent = parent.parent();
10655 if (typeof(parent.labelAlign) !='undefined') {
10656 return parent.labelAlign;
10663 getAutoCreate : function()
10665 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10671 if(this.inputType != 'hidden'){
10672 cfg.cls = 'form-group' //input-group
10678 type : this.inputType,
10679 value : this.value,
10680 cls : 'form-control',
10681 placeholder : this.placeholder || '',
10682 autocomplete : this.autocomplete || 'new-password'
10684 if (this.inputType == 'file') {
10685 input.style = 'overflow:hidden'; // why not in CSS?
10688 if(this.capture.length){
10689 input.capture = this.capture;
10692 if(this.accept.length){
10693 input.accept = this.accept + "/*";
10697 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10700 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10701 input.maxLength = this.maxLength;
10704 if (this.disabled) {
10705 input.disabled=true;
10708 if (this.readOnly) {
10709 input.readonly=true;
10713 input.name = this.name;
10717 input.cls += ' input-' + this.size;
10721 ['xs','sm','md','lg'].map(function(size){
10722 if (settings[size]) {
10723 cfg.cls += ' col-' + size + '-' + settings[size];
10727 var inputblock = input;
10731 cls: 'glyphicon form-control-feedback'
10734 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10737 cls : 'has-feedback',
10745 if (this.before || this.after) {
10748 cls : 'input-group',
10752 if (this.before && typeof(this.before) == 'string') {
10754 inputblock.cn.push({
10756 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10760 if (this.before && typeof(this.before) == 'object') {
10761 this.before = Roo.factory(this.before);
10763 inputblock.cn.push({
10765 cls : 'roo-input-before input-group-prepend input-group-' +
10766 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10770 inputblock.cn.push(input);
10772 if (this.after && typeof(this.after) == 'string') {
10773 inputblock.cn.push({
10775 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10779 if (this.after && typeof(this.after) == 'object') {
10780 this.after = Roo.factory(this.after);
10782 inputblock.cn.push({
10784 cls : 'roo-input-after input-group-append input-group-' +
10785 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10789 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10790 inputblock.cls += ' has-feedback';
10791 inputblock.cn.push(feedback);
10796 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10797 tooltip : 'This field is required'
10799 if (this.allowBlank ) {
10800 indicator.style = this.allowBlank ? ' display:none' : '';
10802 if (align ==='left' && this.fieldLabel.length) {
10804 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10811 cls : 'control-label col-form-label',
10812 html : this.fieldLabel
10823 var labelCfg = cfg.cn[1];
10824 var contentCfg = cfg.cn[2];
10826 if(this.indicatorpos == 'right'){
10831 cls : 'control-label col-form-label',
10835 html : this.fieldLabel
10849 labelCfg = cfg.cn[0];
10850 contentCfg = cfg.cn[1];
10854 if(this.labelWidth > 12){
10855 labelCfg.style = "width: " + this.labelWidth + 'px';
10858 if(this.labelWidth < 13 && this.labelmd == 0){
10859 this.labelmd = this.labelWidth;
10862 if(this.labellg > 0){
10863 labelCfg.cls += ' col-lg-' + this.labellg;
10864 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10867 if(this.labelmd > 0){
10868 labelCfg.cls += ' col-md-' + this.labelmd;
10869 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10872 if(this.labelsm > 0){
10873 labelCfg.cls += ' col-sm-' + this.labelsm;
10874 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10877 if(this.labelxs > 0){
10878 labelCfg.cls += ' col-xs-' + this.labelxs;
10879 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10883 } else if ( this.fieldLabel.length) {
10890 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10891 tooltip : 'This field is required',
10892 style : this.allowBlank ? ' display:none' : ''
10896 //cls : 'input-group-addon',
10897 html : this.fieldLabel
10905 if(this.indicatorpos == 'right'){
10910 //cls : 'input-group-addon',
10911 html : this.fieldLabel
10916 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10917 tooltip : 'This field is required',
10918 style : this.allowBlank ? ' display:none' : ''
10938 if (this.parentType === 'Navbar' && this.parent().bar) {
10939 cfg.cls += ' navbar-form';
10942 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10943 // on BS4 we do this only if not form
10944 cfg.cls += ' navbar-form';
10952 * return the real input element.
10954 inputEl: function ()
10956 return this.el.select('input.form-control',true).first();
10959 tooltipEl : function()
10961 return this.inputEl();
10964 indicatorEl : function()
10966 if (Roo.bootstrap.version == 4) {
10967 return false; // not enabled in v4 yet.
10970 var indicator = this.el.select('i.roo-required-indicator',true).first();
10980 setDisabled : function(v)
10982 var i = this.inputEl().dom;
10984 i.removeAttribute('disabled');
10988 i.setAttribute('disabled','true');
10990 initEvents : function()
10993 this.inputEl().on("keydown" , this.fireKey, this);
10994 this.inputEl().on("focus", this.onFocus, this);
10995 this.inputEl().on("blur", this.onBlur, this);
10997 this.inputEl().relayEvent('keyup', this);
10999 this.indicator = this.indicatorEl();
11001 if(this.indicator){
11002 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11005 // reference to original value for reset
11006 this.originalValue = this.getValue();
11007 //Roo.form.TextField.superclass.initEvents.call(this);
11008 if(this.validationEvent == 'keyup'){
11009 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11010 this.inputEl().on('keyup', this.filterValidation, this);
11012 else if(this.validationEvent !== false){
11013 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11016 if(this.selectOnFocus){
11017 this.on("focus", this.preFocus, this);
11020 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11021 this.inputEl().on("keypress", this.filterKeys, this);
11023 this.inputEl().relayEvent('keypress', this);
11026 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11027 this.el.on("click", this.autoSize, this);
11030 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11031 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11034 if (typeof(this.before) == 'object') {
11035 this.before.render(this.el.select('.roo-input-before',true).first());
11037 if (typeof(this.after) == 'object') {
11038 this.after.render(this.el.select('.roo-input-after',true).first());
11041 this.inputEl().on('change', this.onChange, this);
11044 filterValidation : function(e){
11045 if(!e.isNavKeyPress()){
11046 this.validationTask.delay(this.validationDelay);
11050 * Validates the field value
11051 * @return {Boolean} True if the value is valid, else false
11053 validate : function(){
11054 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11055 if(this.disabled || this.validateValue(this.getRawValue())){
11060 this.markInvalid();
11066 * Validates a value according to the field's validation rules and marks the field as invalid
11067 * if the validation fails
11068 * @param {Mixed} value The value to validate
11069 * @return {Boolean} True if the value is valid, else false
11071 validateValue : function(value)
11073 if(this.getVisibilityEl().hasClass('hidden')){
11077 if(value.length < 1) { // if it's blank
11078 if(this.allowBlank){
11084 if(value.length < this.minLength){
11087 if(value.length > this.maxLength){
11091 var vt = Roo.form.VTypes;
11092 if(!vt[this.vtype](value, this)){
11096 if(typeof this.validator == "function"){
11097 var msg = this.validator(value);
11101 if (typeof(msg) == 'string') {
11102 this.invalidText = msg;
11106 if(this.regex && !this.regex.test(value)){
11114 fireKey : function(e){
11115 //Roo.log('field ' + e.getKey());
11116 if(e.isNavKeyPress()){
11117 this.fireEvent("specialkey", this, e);
11120 focus : function (selectText){
11122 this.inputEl().focus();
11123 if(selectText === true){
11124 this.inputEl().dom.select();
11130 onFocus : function(){
11131 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11132 // this.el.addClass(this.focusClass);
11134 if(!this.hasFocus){
11135 this.hasFocus = true;
11136 this.startValue = this.getValue();
11137 this.fireEvent("focus", this);
11141 beforeBlur : Roo.emptyFn,
11145 onBlur : function(){
11147 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11148 //this.el.removeClass(this.focusClass);
11150 this.hasFocus = false;
11151 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11154 var v = this.getValue();
11155 if(String(v) !== String(this.startValue)){
11156 this.fireEvent('change', this, v, this.startValue);
11158 this.fireEvent("blur", this);
11161 onChange : function(e)
11163 var v = this.getValue();
11164 if(String(v) !== String(this.startValue)){
11165 this.fireEvent('change', this, v, this.startValue);
11171 * Resets the current field value to the originally loaded value and clears any validation messages
11173 reset : function(){
11174 this.setValue(this.originalValue);
11178 * Returns the name of the field
11179 * @return {Mixed} name The name field
11181 getName: function(){
11185 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11186 * @return {Mixed} value The field value
11188 getValue : function(){
11190 var v = this.inputEl().getValue();
11195 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11196 * @return {Mixed} value The field value
11198 getRawValue : function(){
11199 var v = this.inputEl().getValue();
11205 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11206 * @param {Mixed} value The value to set
11208 setRawValue : function(v){
11209 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11212 selectText : function(start, end){
11213 var v = this.getRawValue();
11215 start = start === undefined ? 0 : start;
11216 end = end === undefined ? v.length : end;
11217 var d = this.inputEl().dom;
11218 if(d.setSelectionRange){
11219 d.setSelectionRange(start, end);
11220 }else if(d.createTextRange){
11221 var range = d.createTextRange();
11222 range.moveStart("character", start);
11223 range.moveEnd("character", v.length-end);
11230 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11231 * @param {Mixed} value The value to set
11233 setValue : function(v){
11236 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11242 processValue : function(value){
11243 if(this.stripCharsRe){
11244 var newValue = value.replace(this.stripCharsRe, '');
11245 if(newValue !== value){
11246 this.setRawValue(newValue);
11253 preFocus : function(){
11255 if(this.selectOnFocus){
11256 this.inputEl().dom.select();
11259 filterKeys : function(e){
11260 var k = e.getKey();
11261 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11264 var c = e.getCharCode(), cc = String.fromCharCode(c);
11265 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11268 if(!this.maskRe.test(cc)){
11273 * Clear any invalid styles/messages for this field
11275 clearInvalid : function(){
11277 if(!this.el || this.preventMark){ // not rendered
11282 this.el.removeClass([this.invalidClass, 'is-invalid']);
11284 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286 var feedback = this.el.select('.form-control-feedback', true).first();
11289 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11294 if(this.indicator){
11295 this.indicator.removeClass('visible');
11296 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11299 this.fireEvent('valid', this);
11303 * Mark this field as valid
11305 markValid : function()
11307 if(!this.el || this.preventMark){ // not rendered...
11311 this.el.removeClass([this.invalidClass, this.validClass]);
11312 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314 var feedback = this.el.select('.form-control-feedback', true).first();
11317 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11320 if(this.indicator){
11321 this.indicator.removeClass('visible');
11322 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11330 if(this.allowBlank && !this.getRawValue().length){
11333 if (Roo.bootstrap.version == 3) {
11334 this.el.addClass(this.validClass);
11336 this.inputEl().addClass('is-valid');
11339 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341 var feedback = this.el.select('.form-control-feedback', true).first();
11344 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11345 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11350 this.fireEvent('valid', this);
11354 * Mark this field as invalid
11355 * @param {String} msg The validation message
11357 markInvalid : function(msg)
11359 if(!this.el || this.preventMark){ // not rendered
11363 this.el.removeClass([this.invalidClass, this.validClass]);
11364 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366 var feedback = this.el.select('.form-control-feedback', true).first();
11369 this.el.select('.form-control-feedback', true).first().removeClass(
11370 [this.invalidFeedbackClass, this.validFeedbackClass]);
11377 if(this.allowBlank && !this.getRawValue().length){
11381 if(this.indicator){
11382 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11383 this.indicator.addClass('visible');
11385 if (Roo.bootstrap.version == 3) {
11386 this.el.addClass(this.invalidClass);
11388 this.inputEl().addClass('is-invalid');
11393 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395 var feedback = this.el.select('.form-control-feedback', true).first();
11398 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400 if(this.getValue().length || this.forceFeedback){
11401 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11408 this.fireEvent('invalid', this, msg);
11411 SafariOnKeyDown : function(event)
11413 // this is a workaround for a password hang bug on chrome/ webkit.
11414 if (this.inputEl().dom.type != 'password') {
11418 var isSelectAll = false;
11420 if(this.inputEl().dom.selectionEnd > 0){
11421 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11424 event.preventDefault();
11429 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431 event.preventDefault();
11432 // this is very hacky as keydown always get's upper case.
11434 var cc = String.fromCharCode(event.getCharCode());
11435 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11439 adjustWidth : function(tag, w){
11440 tag = tag.toLowerCase();
11441 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11442 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11443 if(tag == 'input'){
11446 if(tag == 'textarea'){
11449 }else if(Roo.isOpera){
11450 if(tag == 'input'){
11453 if(tag == 'textarea'){
11461 setFieldLabel : function(v)
11463 if(!this.rendered){
11467 if(this.indicatorEl()){
11468 var ar = this.el.select('label > span',true);
11470 if (ar.elements.length) {
11471 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11472 this.fieldLabel = v;
11476 var br = this.el.select('label',true);
11478 if(br.elements.length) {
11479 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11480 this.fieldLabel = v;
11484 Roo.log('Cannot Found any of label > span || label in input');
11488 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11489 this.fieldLabel = v;
11504 * @class Roo.bootstrap.TextArea
11505 * @extends Roo.bootstrap.Input
11506 * Bootstrap TextArea class
11507 * @cfg {Number} cols Specifies the visible width of a text area
11508 * @cfg {Number} rows Specifies the visible number of lines in a text area
11509 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11510 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11511 * @cfg {string} html text
11514 * Create a new TextArea
11515 * @param {Object} config The config object
11518 Roo.bootstrap.TextArea = function(config){
11519 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11523 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11533 getAutoCreate : function(){
11535 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11541 if(this.inputType != 'hidden'){
11542 cfg.cls = 'form-group' //input-group
11550 value : this.value || '',
11551 html: this.html || '',
11552 cls : 'form-control',
11553 placeholder : this.placeholder || ''
11557 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11558 input.maxLength = this.maxLength;
11562 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11566 input.cols = this.cols;
11569 if (this.readOnly) {
11570 input.readonly = true;
11574 input.name = this.name;
11578 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11582 ['xs','sm','md','lg'].map(function(size){
11583 if (settings[size]) {
11584 cfg.cls += ' col-' + size + '-' + settings[size];
11588 var inputblock = input;
11590 if(this.hasFeedback && !this.allowBlank){
11594 cls: 'glyphicon form-control-feedback'
11598 cls : 'has-feedback',
11607 if (this.before || this.after) {
11610 cls : 'input-group',
11614 inputblock.cn.push({
11616 cls : 'input-group-addon',
11621 inputblock.cn.push(input);
11623 if(this.hasFeedback && !this.allowBlank){
11624 inputblock.cls += ' has-feedback';
11625 inputblock.cn.push(feedback);
11629 inputblock.cn.push({
11631 cls : 'input-group-addon',
11638 if (align ==='left' && this.fieldLabel.length) {
11643 cls : 'control-label',
11644 html : this.fieldLabel
11655 if(this.labelWidth > 12){
11656 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11659 if(this.labelWidth < 13 && this.labelmd == 0){
11660 this.labelmd = this.labelWidth;
11663 if(this.labellg > 0){
11664 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11665 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11668 if(this.labelmd > 0){
11669 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11670 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11673 if(this.labelsm > 0){
11674 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11675 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11678 if(this.labelxs > 0){
11679 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11680 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11683 } else if ( this.fieldLabel.length) {
11688 //cls : 'input-group-addon',
11689 html : this.fieldLabel
11707 if (this.disabled) {
11708 input.disabled=true;
11715 * return the real textarea element.
11717 inputEl: function ()
11719 return this.el.select('textarea.form-control',true).first();
11723 * Clear any invalid styles/messages for this field
11725 clearInvalid : function()
11728 if(!this.el || this.preventMark){ // not rendered
11732 var label = this.el.select('label', true).first();
11733 var icon = this.el.select('i.fa-star', true).first();
11738 this.el.removeClass( this.validClass);
11739 this.inputEl().removeClass('is-invalid');
11741 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743 var feedback = this.el.select('.form-control-feedback', true).first();
11746 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11751 this.fireEvent('valid', this);
11755 * Mark this field as valid
11757 markValid : function()
11759 if(!this.el || this.preventMark){ // not rendered
11763 this.el.removeClass([this.invalidClass, this.validClass]);
11764 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766 var feedback = this.el.select('.form-control-feedback', true).first();
11769 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11772 if(this.disabled || this.allowBlank){
11776 var label = this.el.select('label', true).first();
11777 var icon = this.el.select('i.fa-star', true).first();
11782 if (Roo.bootstrap.version == 3) {
11783 this.el.addClass(this.validClass);
11785 this.inputEl().addClass('is-valid');
11789 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791 var feedback = this.el.select('.form-control-feedback', true).first();
11794 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11795 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11800 this.fireEvent('valid', this);
11804 * Mark this field as invalid
11805 * @param {String} msg The validation message
11807 markInvalid : function(msg)
11809 if(!this.el || this.preventMark){ // not rendered
11813 this.el.removeClass([this.invalidClass, this.validClass]);
11814 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816 var feedback = this.el.select('.form-control-feedback', true).first();
11819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11822 if(this.disabled || this.allowBlank){
11826 var label = this.el.select('label', true).first();
11827 var icon = this.el.select('i.fa-star', true).first();
11829 if(!this.getValue().length && label && !icon){
11830 this.el.createChild({
11832 cls : 'text-danger fa fa-lg fa-star',
11833 tooltip : 'This field is required',
11834 style : 'margin-right:5px;'
11838 if (Roo.bootstrap.version == 3) {
11839 this.el.addClass(this.invalidClass);
11841 this.inputEl().addClass('is-invalid');
11844 // fixme ... this may be depricated need to test..
11845 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847 var feedback = this.el.select('.form-control-feedback', true).first();
11850 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852 if(this.getValue().length || this.forceFeedback){
11853 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11860 this.fireEvent('invalid', this, msg);
11868 * trigger field - base class for combo..
11873 * @class Roo.bootstrap.TriggerField
11874 * @extends Roo.bootstrap.Input
11875 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11876 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11877 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11878 * for which you can provide a custom implementation. For example:
11880 var trigger = new Roo.bootstrap.TriggerField();
11881 trigger.onTriggerClick = myTriggerFn;
11882 trigger.applyTo('my-field');
11885 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11886 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11887 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11888 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11889 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11892 * Create a new TriggerField.
11893 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11894 * to the base TextField)
11896 Roo.bootstrap.TriggerField = function(config){
11897 this.mimicing = false;
11898 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11901 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11903 * @cfg {String} triggerClass A CSS class to apply to the trigger
11906 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11911 * @cfg {Boolean} removable (true|false) special filter default false
11915 /** @cfg {Boolean} grow @hide */
11916 /** @cfg {Number} growMin @hide */
11917 /** @cfg {Number} growMax @hide */
11923 autoSize: Roo.emptyFn,
11927 deferHeight : true,
11930 actionMode : 'wrap',
11935 getAutoCreate : function(){
11937 var align = this.labelAlign || this.parentLabelAlign();
11942 cls: 'form-group' //input-group
11949 type : this.inputType,
11950 cls : 'form-control',
11951 autocomplete: 'new-password',
11952 placeholder : this.placeholder || ''
11956 input.name = this.name;
11959 input.cls += ' input-' + this.size;
11962 if (this.disabled) {
11963 input.disabled=true;
11966 var inputblock = input;
11968 if(this.hasFeedback && !this.allowBlank){
11972 cls: 'glyphicon form-control-feedback'
11975 if(this.removable && !this.editable ){
11977 cls : 'has-feedback',
11983 cls : 'roo-combo-removable-btn close'
11990 cls : 'has-feedback',
11999 if(this.removable && !this.editable ){
12001 cls : 'roo-removable',
12007 cls : 'roo-combo-removable-btn close'
12014 if (this.before || this.after) {
12017 cls : 'input-group',
12021 inputblock.cn.push({
12023 cls : 'input-group-addon input-group-prepend input-group-text',
12028 inputblock.cn.push(input);
12030 if(this.hasFeedback && !this.allowBlank){
12031 inputblock.cls += ' has-feedback';
12032 inputblock.cn.push(feedback);
12036 inputblock.cn.push({
12038 cls : 'input-group-addon input-group-append input-group-text',
12047 var ibwrap = inputblock;
12052 cls: 'roo-select2-choices',
12056 cls: 'roo-select2-search-field',
12068 cls: 'roo-select2-container input-group',
12073 cls: 'form-hidden-field'
12079 if(!this.multiple && this.showToggleBtn){
12085 if (this.caret != false) {
12088 cls: 'fa fa-' + this.caret
12095 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097 Roo.bootstrap.version == 3 ? caret : '',
12100 cls: 'combobox-clear',
12114 combobox.cls += ' roo-select2-container-multi';
12118 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12119 tooltip : 'This field is required'
12121 if (Roo.bootstrap.version == 4) {
12124 style : 'display:none'
12129 if (align ==='left' && this.fieldLabel.length) {
12131 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12138 cls : 'control-label',
12139 html : this.fieldLabel
12151 var labelCfg = cfg.cn[1];
12152 var contentCfg = cfg.cn[2];
12154 if(this.indicatorpos == 'right'){
12159 cls : 'control-label',
12163 html : this.fieldLabel
12177 labelCfg = cfg.cn[0];
12178 contentCfg = cfg.cn[1];
12181 if(this.labelWidth > 12){
12182 labelCfg.style = "width: " + this.labelWidth + 'px';
12185 if(this.labelWidth < 13 && this.labelmd == 0){
12186 this.labelmd = this.labelWidth;
12189 if(this.labellg > 0){
12190 labelCfg.cls += ' col-lg-' + this.labellg;
12191 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12194 if(this.labelmd > 0){
12195 labelCfg.cls += ' col-md-' + this.labelmd;
12196 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12199 if(this.labelsm > 0){
12200 labelCfg.cls += ' col-sm-' + this.labelsm;
12201 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12204 if(this.labelxs > 0){
12205 labelCfg.cls += ' col-xs-' + this.labelxs;
12206 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12209 } else if ( this.fieldLabel.length) {
12210 // Roo.log(" label");
12215 //cls : 'input-group-addon',
12216 html : this.fieldLabel
12224 if(this.indicatorpos == 'right'){
12232 html : this.fieldLabel
12246 // Roo.log(" no label && no align");
12253 ['xs','sm','md','lg'].map(function(size){
12254 if (settings[size]) {
12255 cfg.cls += ' col-' + size + '-' + settings[size];
12266 onResize : function(w, h){
12267 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12268 // if(typeof w == 'number'){
12269 // var x = w - this.trigger.getWidth();
12270 // this.inputEl().setWidth(this.adjustWidth('input', x));
12271 // this.trigger.setStyle('left', x+'px');
12276 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12279 getResizeEl : function(){
12280 return this.inputEl();
12284 getPositionEl : function(){
12285 return this.inputEl();
12289 alignErrorIcon : function(){
12290 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12294 initEvents : function(){
12298 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12299 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12300 if(!this.multiple && this.showToggleBtn){
12301 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12302 if(this.hideTrigger){
12303 this.trigger.setDisplayed(false);
12305 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12309 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12312 if(this.removable && !this.editable && !this.tickable){
12313 var close = this.closeTriggerEl();
12316 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12317 close.on('click', this.removeBtnClick, this, close);
12321 //this.trigger.addClassOnOver('x-form-trigger-over');
12322 //this.trigger.addClassOnClick('x-form-trigger-click');
12325 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12329 closeTriggerEl : function()
12331 var close = this.el.select('.roo-combo-removable-btn', true).first();
12332 return close ? close : false;
12335 removeBtnClick : function(e, h, el)
12337 e.preventDefault();
12339 if(this.fireEvent("remove", this) !== false){
12341 this.fireEvent("afterremove", this)
12345 createList : function()
12347 this.list = Roo.get(document.body).createChild({
12348 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12349 cls: 'typeahead typeahead-long dropdown-menu shadow',
12350 style: 'display:none'
12353 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12358 initTrigger : function(){
12363 onDestroy : function(){
12365 this.trigger.removeAllListeners();
12366 // this.trigger.remove();
12369 // this.wrap.remove();
12371 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12375 onFocus : function(){
12376 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378 if(!this.mimicing){
12379 this.wrap.addClass('x-trigger-wrap-focus');
12380 this.mimicing = true;
12381 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12382 if(this.monitorTab){
12383 this.el.on("keydown", this.checkTab, this);
12390 checkTab : function(e){
12391 if(e.getKey() == e.TAB){
12392 this.triggerBlur();
12397 onBlur : function(){
12402 mimicBlur : function(e, t){
12404 if(!this.wrap.contains(t) && this.validateBlur()){
12405 this.triggerBlur();
12411 triggerBlur : function(){
12412 this.mimicing = false;
12413 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12414 if(this.monitorTab){
12415 this.el.un("keydown", this.checkTab, this);
12417 //this.wrap.removeClass('x-trigger-wrap-focus');
12418 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12422 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12423 validateBlur : function(e, t){
12428 onDisable : function(){
12429 this.inputEl().dom.disabled = true;
12430 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432 // this.wrap.addClass('x-item-disabled');
12437 onEnable : function(){
12438 this.inputEl().dom.disabled = false;
12439 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441 // this.el.removeClass('x-item-disabled');
12446 onShow : function(){
12447 var ae = this.getActionEl();
12450 ae.dom.style.display = '';
12451 ae.dom.style.visibility = 'visible';
12457 onHide : function(){
12458 var ae = this.getActionEl();
12459 ae.dom.style.display = 'none';
12463 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12464 * by an implementing function.
12466 * @param {EventObject} e
12468 onTriggerClick : Roo.emptyFn
12476 * @class Roo.bootstrap.CardUploader
12477 * @extends Roo.bootstrap.Button
12478 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12479 * @cfg {Number} errorTimeout default 3000
12480 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12481 * @cfg {Array} html The button text.
12485 * Create a new CardUploader
12486 * @param {Object} config The config object
12489 Roo.bootstrap.CardUploader = function(config){
12493 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12496 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12503 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12506 errorTimeout : 3000,
12510 fileCollection : false,
12513 getAutoCreate : function()
12517 cls :'form-group' ,
12522 //cls : 'input-group-addon',
12523 html : this.fieldLabel
12530 value : this.value,
12531 cls : 'd-none form-control'
12536 multiple : 'multiple',
12538 cls : 'd-none roo-card-upload-selector'
12542 cls : 'roo-card-uploader-button-container w-100 mb-2'
12545 cls : 'card-columns roo-card-uploader-container'
12555 getChildContainer : function() /// what children are added to.
12557 return this.containerEl;
12560 getButtonContainer : function() /// what children are added to.
12562 return this.el.select(".roo-card-uploader-button-container").first();
12565 initEvents : function()
12568 Roo.bootstrap.Input.prototype.initEvents.call(this);
12572 xns: Roo.bootstrap,
12575 container_method : 'getButtonContainer' ,
12576 html : this.html, // fix changable?
12579 'click' : function(btn, e) {
12588 this.urlAPI = (window.createObjectURL && window) ||
12589 (window.URL && URL.revokeObjectURL && URL) ||
12590 (window.webkitURL && webkitURL);
12595 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597 this.selectorEl.on('change', this.onFileSelected, this);
12600 this.images.forEach(function(img) {
12603 this.images = false;
12605 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12611 onClick : function(e)
12613 e.preventDefault();
12615 this.selectorEl.dom.click();
12619 onFileSelected : function(e)
12621 e.preventDefault();
12623 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12627 Roo.each(this.selectorEl.dom.files, function(file){
12628 this.addFile(file);
12637 addFile : function(file)
12640 if(typeof(file) === 'string'){
12641 throw "Add file by name?"; // should not happen
12645 if(!file || !this.urlAPI){
12655 var url = _this.urlAPI.createObjectURL( file);
12658 id : Roo.bootstrap.CardUploader.ID--,
12659 is_uploaded : false,
12662 mimetype : file.type,
12669 addCard : function (data)
12671 // hidden input element?
12672 // if the file is not an image...
12673 //then we need to use something other that and header_image
12678 xns : Roo.bootstrap,
12679 xtype : 'CardFooter',
12682 xns : Roo.bootstrap,
12688 xns : Roo.bootstrap,
12690 html : String.format("<small>{0}</small>", data.title),
12691 cls : 'col-11 text-left',
12696 click : function() {
12697 this.downloadCard(data.id)
12703 xns : Roo.bootstrap,
12711 click : function() {
12712 t.removeCard(data.id)
12724 var cn = this.addxtype(
12727 xns : Roo.bootstrap,
12730 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12731 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12732 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12737 initEvents : function() {
12738 Roo.bootstrap.Card.prototype.initEvents.call(this);
12739 this.imgEl = this.el.select('.card-img-top').first();
12741 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12742 this.imgEl.set({ 'pointer' : 'cursor' });
12751 // dont' really need ot update items.
12752 // this.items.push(cn);
12753 this.fileCollection.add(cn);
12754 this.updateInput();
12757 removeCard : function(id)
12760 var card = this.fileCollection.get(id);
12761 card.data.is_deleted = 1;
12762 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12763 this.fileCollection.remove(card);
12764 //this.items = this.items.filter(function(e) { return e != card });
12765 // dont' really need ot update items.
12766 card.el.dom.parentNode.removeChild(card.el.dom);
12771 this.fileCollection.each(function(card) {
12772 card.el.dom.parentNode.removeChild(card.el.dom);
12774 this.fileCollection.clear();
12775 this.updateInput();
12778 updateInput : function()
12781 this.fileCollection.each(function(e) {
12785 this.inputEl().dom.value = JSON.stringify(data);
12792 Roo.bootstrap.CardUploader.ID = -1;/*
12794 * Ext JS Library 1.1.1
12795 * Copyright(c) 2006-2007, Ext JS, LLC.
12797 * Originally Released Under LGPL - original licence link has changed is not relivant.
12800 * <script type="text/javascript">
12805 * @class Roo.data.SortTypes
12807 * Defines the default sorting (casting?) comparison functions used when sorting data.
12809 Roo.data.SortTypes = {
12811 * Default sort that does nothing
12812 * @param {Mixed} s The value being converted
12813 * @return {Mixed} The comparison value
12815 none : function(s){
12820 * The regular expression used to strip tags
12824 stripTagsRE : /<\/?[^>]+>/gi,
12827 * Strips all HTML tags to sort on text only
12828 * @param {Mixed} s The value being converted
12829 * @return {String} The comparison value
12831 asText : function(s){
12832 return String(s).replace(this.stripTagsRE, "");
12836 * Strips all HTML tags to sort on text only - Case insensitive
12837 * @param {Mixed} s The value being converted
12838 * @return {String} The comparison value
12840 asUCText : function(s){
12841 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12845 * Case insensitive string
12846 * @param {Mixed} s The value being converted
12847 * @return {String} The comparison value
12849 asUCString : function(s) {
12850 return String(s).toUpperCase();
12855 * @param {Mixed} s The value being converted
12856 * @return {Number} The comparison value
12858 asDate : function(s) {
12862 if(s instanceof Date){
12863 return s.getTime();
12865 return Date.parse(String(s));
12870 * @param {Mixed} s The value being converted
12871 * @return {Float} The comparison value
12873 asFloat : function(s) {
12874 var val = parseFloat(String(s).replace(/,/g, ""));
12883 * @param {Mixed} s The value being converted
12884 * @return {Number} The comparison value
12886 asInt : function(s) {
12887 var val = parseInt(String(s).replace(/,/g, ""));
12895 * Ext JS Library 1.1.1
12896 * Copyright(c) 2006-2007, Ext JS, LLC.
12898 * Originally Released Under LGPL - original licence link has changed is not relivant.
12901 * <script type="text/javascript">
12905 * @class Roo.data.Record
12906 * Instances of this class encapsulate both record <em>definition</em> information, and record
12907 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12908 * to access Records cached in an {@link Roo.data.Store} object.<br>
12910 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12911 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12914 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12917 * {@link #create}. The parameters are the same.
12918 * @param {Array} data An associative Array of data values keyed by the field name.
12919 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12920 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12921 * not specified an integer id is generated.
12923 Roo.data.Record = function(data, id){
12924 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12929 * Generate a constructor for a specific record layout.
12930 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12931 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12932 * Each field definition object may contain the following properties: <ul>
12933 * <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,
12934 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12935 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12936 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12937 * is being used, then this is a string containing the javascript expression to reference the data relative to
12938 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12939 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12940 * this may be omitted.</p></li>
12941 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12942 * <ul><li>auto (Default, implies no conversion)</li>
12947 * <li>date</li></ul></p></li>
12948 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12949 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12950 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12951 * by the Reader into an object that will be stored in the Record. It is passed the
12952 * following parameters:<ul>
12953 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957 * <br>usage:<br><pre><code>
12958 var TopicRecord = Roo.data.Record.create(
12959 {name: 'title', mapping: 'topic_title'},
12960 {name: 'author', mapping: 'username'},
12961 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12962 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12963 {name: 'lastPoster', mapping: 'user2'},
12964 {name: 'excerpt', mapping: 'post_text'}
12967 var myNewRecord = new TopicRecord({
12968 title: 'Do my job please',
12971 lastPost: new Date(),
12972 lastPoster: 'Animal',
12973 excerpt: 'No way dude!'
12975 myStore.add(myNewRecord);
12980 Roo.data.Record.create = function(o){
12981 var f = function(){
12982 f.superclass.constructor.apply(this, arguments);
12984 Roo.extend(f, Roo.data.Record);
12985 var p = f.prototype;
12986 p.fields = new Roo.util.MixedCollection(false, function(field){
12989 for(var i = 0, len = o.length; i < len; i++){
12990 p.fields.add(new Roo.data.Field(o[i]));
12992 f.getField = function(name){
12993 return p.fields.get(name);
12998 Roo.data.Record.AUTO_ID = 1000;
12999 Roo.data.Record.EDIT = 'edit';
13000 Roo.data.Record.REJECT = 'reject';
13001 Roo.data.Record.COMMIT = 'commit';
13003 Roo.data.Record.prototype = {
13005 * Readonly flag - true if this record has been modified.
13014 join : function(store){
13015 this.store = store;
13019 * Set the named field to the specified value.
13020 * @param {String} name The name of the field to set.
13021 * @param {Object} value The value to set the field to.
13023 set : function(name, value){
13024 if(this.data[name] == value){
13028 if(!this.modified){
13029 this.modified = {};
13031 if(typeof this.modified[name] == 'undefined'){
13032 this.modified[name] = this.data[name];
13034 this.data[name] = value;
13035 if(!this.editing && this.store){
13036 this.store.afterEdit(this);
13041 * Get the value of the named field.
13042 * @param {String} name The name of the field to get the value of.
13043 * @return {Object} The value of the field.
13045 get : function(name){
13046 return this.data[name];
13050 beginEdit : function(){
13051 this.editing = true;
13052 this.modified = {};
13056 cancelEdit : function(){
13057 this.editing = false;
13058 delete this.modified;
13062 endEdit : function(){
13063 this.editing = false;
13064 if(this.dirty && this.store){
13065 this.store.afterEdit(this);
13070 * Usually called by the {@link Roo.data.Store} which owns the Record.
13071 * Rejects all changes made to the Record since either creation, or the last commit operation.
13072 * Modified fields are reverted to their original values.
13074 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13075 * of reject operations.
13077 reject : function(){
13078 var m = this.modified;
13080 if(typeof m[n] != "function"){
13081 this.data[n] = m[n];
13084 this.dirty = false;
13085 delete this.modified;
13086 this.editing = false;
13088 this.store.afterReject(this);
13093 * Usually called by the {@link Roo.data.Store} which owns the Record.
13094 * Commits all changes made to the Record since either creation, or the last commit operation.
13096 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13097 * of commit operations.
13099 commit : function(){
13100 this.dirty = false;
13101 delete this.modified;
13102 this.editing = false;
13104 this.store.afterCommit(this);
13109 hasError : function(){
13110 return this.error != null;
13114 clearError : function(){
13119 * Creates a copy of this record.
13120 * @param {String} id (optional) A new record id if you don't want to use this record's id
13123 copy : function(newId) {
13124 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13128 * Ext JS Library 1.1.1
13129 * Copyright(c) 2006-2007, Ext JS, LLC.
13131 * Originally Released Under LGPL - original licence link has changed is not relivant.
13134 * <script type="text/javascript">
13140 * @class Roo.data.Store
13141 * @extends Roo.util.Observable
13142 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13143 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145 * 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
13146 * has no knowledge of the format of the data returned by the Proxy.<br>
13148 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13149 * instances from the data object. These records are cached and made available through accessor functions.
13151 * Creates a new Store.
13152 * @param {Object} config A config object containing the objects needed for the Store to access data,
13153 * and read the data into Records.
13155 Roo.data.Store = function(config){
13156 this.data = new Roo.util.MixedCollection(false);
13157 this.data.getKey = function(o){
13160 this.baseParams = {};
13162 this.paramNames = {
13167 "multisort" : "_multisort"
13170 if(config && config.data){
13171 this.inlineData = config.data;
13172 delete config.data;
13175 Roo.apply(this, config);
13177 if(this.reader){ // reader passed
13178 this.reader = Roo.factory(this.reader, Roo.data);
13179 this.reader.xmodule = this.xmodule || false;
13180 if(!this.recordType){
13181 this.recordType = this.reader.recordType;
13183 if(this.reader.onMetaChange){
13184 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13188 if(this.recordType){
13189 this.fields = this.recordType.prototype.fields;
13191 this.modified = [];
13195 * @event datachanged
13196 * Fires when the data cache has changed, and a widget which is using this Store
13197 * as a Record cache should refresh its view.
13198 * @param {Store} this
13200 datachanged : true,
13202 * @event metachange
13203 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13204 * @param {Store} this
13205 * @param {Object} meta The JSON metadata
13210 * Fires when Records have been added to the Store
13211 * @param {Store} this
13212 * @param {Roo.data.Record[]} records The array of Records added
13213 * @param {Number} index The index at which the record(s) were added
13218 * Fires when a Record has been removed from the Store
13219 * @param {Store} this
13220 * @param {Roo.data.Record} record The Record that was removed
13221 * @param {Number} index The index at which the record was removed
13226 * Fires when a Record has been updated
13227 * @param {Store} this
13228 * @param {Roo.data.Record} record The Record that was updated
13229 * @param {String} operation The update operation being performed. Value may be one of:
13231 Roo.data.Record.EDIT
13232 Roo.data.Record.REJECT
13233 Roo.data.Record.COMMIT
13239 * Fires when the data cache has been cleared.
13240 * @param {Store} this
13244 * @event beforeload
13245 * Fires before a request is made for a new data object. If the beforeload handler returns false
13246 * the load action will be canceled.
13247 * @param {Store} this
13248 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13252 * @event beforeloadadd
13253 * Fires after a new set of Records has been loaded.
13254 * @param {Store} this
13255 * @param {Roo.data.Record[]} records The Records that were loaded
13256 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258 beforeloadadd : true,
13261 * Fires after a new set of Records has been loaded, before they are added to the store.
13262 * @param {Store} this
13263 * @param {Roo.data.Record[]} records The Records that were loaded
13264 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13265 * @params {Object} return from reader
13269 * @event loadexception
13270 * Fires if an exception occurs in the Proxy during loading.
13271 * Called with the signature of the Proxy's "loadexception" event.
13272 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13275 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13276 * @param {Object} load options
13277 * @param {Object} jsonData from your request (normally this contains the Exception)
13279 loadexception : true
13283 this.proxy = Roo.factory(this.proxy, Roo.data);
13284 this.proxy.xmodule = this.xmodule || false;
13285 this.relayEvents(this.proxy, ["loadexception"]);
13287 this.sortToggle = {};
13288 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290 Roo.data.Store.superclass.constructor.call(this);
13292 if(this.inlineData){
13293 this.loadData(this.inlineData);
13294 delete this.inlineData;
13298 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13301 * without a remote query - used by combo/forms at present.
13305 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13308 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13311 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13312 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13315 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13316 * on any HTTP request
13319 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13322 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13326 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13327 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329 remoteSort : false,
13332 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13333 * loaded or when a record is removed. (defaults to false).
13335 pruneModifiedRecords : false,
13338 lastOptions : null,
13341 * Add Records to the Store and fires the add event.
13342 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344 add : function(records){
13345 records = [].concat(records);
13346 for(var i = 0, len = records.length; i < len; i++){
13347 records[i].join(this);
13349 var index = this.data.length;
13350 this.data.addAll(records);
13351 this.fireEvent("add", this, records, index);
13355 * Remove a Record from the Store and fires the remove event.
13356 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358 remove : function(record){
13359 var index = this.data.indexOf(record);
13360 this.data.removeAt(index);
13362 if(this.pruneModifiedRecords){
13363 this.modified.remove(record);
13365 this.fireEvent("remove", this, record, index);
13369 * Remove all Records from the Store and fires the clear event.
13371 removeAll : function(){
13373 if(this.pruneModifiedRecords){
13374 this.modified = [];
13376 this.fireEvent("clear", this);
13380 * Inserts Records to the Store at the given index and fires the add event.
13381 * @param {Number} index The start index at which to insert the passed Records.
13382 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384 insert : function(index, records){
13385 records = [].concat(records);
13386 for(var i = 0, len = records.length; i < len; i++){
13387 this.data.insert(index, records[i]);
13388 records[i].join(this);
13390 this.fireEvent("add", this, records, index);
13394 * Get the index within the cache of the passed Record.
13395 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13396 * @return {Number} The index of the passed Record. Returns -1 if not found.
13398 indexOf : function(record){
13399 return this.data.indexOf(record);
13403 * Get the index within the cache of the Record with the passed id.
13404 * @param {String} id The id of the Record to find.
13405 * @return {Number} The index of the Record. Returns -1 if not found.
13407 indexOfId : function(id){
13408 return this.data.indexOfKey(id);
13412 * Get the Record with the specified id.
13413 * @param {String} id The id of the Record to find.
13414 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416 getById : function(id){
13417 return this.data.key(id);
13421 * Get the Record at the specified index.
13422 * @param {Number} index The index of the Record to find.
13423 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425 getAt : function(index){
13426 return this.data.itemAt(index);
13430 * Returns a range of Records between specified indices.
13431 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13432 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13433 * @return {Roo.data.Record[]} An array of Records
13435 getRange : function(start, end){
13436 return this.data.getRange(start, end);
13440 storeOptions : function(o){
13441 o = Roo.apply({}, o);
13444 this.lastOptions = o;
13448 * Loads the Record cache from the configured Proxy using the configured Reader.
13450 * If using remote paging, then the first load call must specify the <em>start</em>
13451 * and <em>limit</em> properties in the options.params property to establish the initial
13452 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13455 * and this call will return before the new data has been loaded. Perform any post-processing
13456 * in a callback function, or in a "load" event handler.</strong>
13458 * @param {Object} options An object containing properties which control loading options:<ul>
13459 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13460 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13461 * passed the following arguments:<ul>
13462 * <li>r : Roo.data.Record[]</li>
13463 * <li>options: Options object from the load call</li>
13464 * <li>success: Boolean success indicator</li></ul></li>
13465 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13466 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13469 load : function(options){
13470 options = options || {};
13471 if(this.fireEvent("beforeload", this, options) !== false){
13472 this.storeOptions(options);
13473 var p = Roo.apply(options.params || {}, this.baseParams);
13474 // if meta was not loaded from remote source.. try requesting it.
13475 if (!this.reader.metaFromRemote) {
13476 p._requestMeta = 1;
13478 if(this.sortInfo && this.remoteSort){
13479 var pn = this.paramNames;
13480 p[pn["sort"]] = this.sortInfo.field;
13481 p[pn["dir"]] = this.sortInfo.direction;
13483 if (this.multiSort) {
13484 var pn = this.paramNames;
13485 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13488 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13493 * Reloads the Record cache from the configured Proxy using the configured Reader and
13494 * the options from the last load operation performed.
13495 * @param {Object} options (optional) An object containing properties which may override the options
13496 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13497 * the most recently used options are reused).
13499 reload : function(options){
13500 this.load(Roo.applyIf(options||{}, this.lastOptions));
13504 // Called as a callback by the Reader during a load operation.
13505 loadRecords : function(o, options, success){
13506 if(!o || success === false){
13507 if(success !== false){
13508 this.fireEvent("load", this, [], options, o);
13510 if(options.callback){
13511 options.callback.call(options.scope || this, [], options, false);
13515 // if data returned failure - throw an exception.
13516 if (o.success === false) {
13517 // show a message if no listener is registered.
13518 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13519 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521 // loadmask wil be hooked into this..
13522 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13525 var r = o.records, t = o.totalRecords || r.length;
13527 this.fireEvent("beforeloadadd", this, r, options, o);
13529 if(!options || options.add !== true){
13530 if(this.pruneModifiedRecords){
13531 this.modified = [];
13533 for(var i = 0, len = r.length; i < len; i++){
13537 this.data = this.snapshot;
13538 delete this.snapshot;
13541 this.data.addAll(r);
13542 this.totalLength = t;
13544 this.fireEvent("datachanged", this);
13546 this.totalLength = Math.max(t, this.data.length+r.length);
13550 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552 var e = new Roo.data.Record({});
13554 e.set(this.parent.displayField, this.parent.emptyTitle);
13555 e.set(this.parent.valueField, '');
13560 this.fireEvent("load", this, r, options, o);
13561 if(options.callback){
13562 options.callback.call(options.scope || this, r, options, true);
13568 * Loads data from a passed data block. A Reader which understands the format of the data
13569 * must have been configured in the constructor.
13570 * @param {Object} data The data block from which to read the Records. The format of the data expected
13571 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13572 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574 loadData : function(o, append){
13575 var r = this.reader.readRecords(o);
13576 this.loadRecords(r, {add: append}, true);
13580 * using 'cn' the nested child reader read the child array into it's child stores.
13581 * @param {Object} rec The record with a 'children array
13583 loadDataFromChildren : function(rec)
13585 this.loadData(this.reader.toLoadData(rec));
13590 * Gets the number of cached records.
13592 * <em>If using paging, this may not be the total size of the dataset. If the data object
13593 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13594 * the data set size</em>
13596 getCount : function(){
13597 return this.data.length || 0;
13601 * Gets the total number of records in the dataset as returned by the server.
13603 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13604 * the dataset size</em>
13606 getTotalCount : function(){
13607 return this.totalLength || 0;
13611 * Returns the sort state of the Store as an object with two properties:
13613 field {String} The name of the field by which the Records are sorted
13614 direction {String} The sort order, "ASC" or "DESC"
13617 getSortState : function(){
13618 return this.sortInfo;
13622 applySort : function(){
13623 if(this.sortInfo && !this.remoteSort){
13624 var s = this.sortInfo, f = s.field;
13625 var st = this.fields.get(f).sortType;
13626 var fn = function(r1, r2){
13627 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13628 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630 this.data.sort(s.direction, fn);
13631 if(this.snapshot && this.snapshot != this.data){
13632 this.snapshot.sort(s.direction, fn);
13638 * Sets the default sort column and order to be used by the next load operation.
13639 * @param {String} fieldName The name of the field to sort by.
13640 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642 setDefaultSort : function(field, dir){
13643 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13647 * Sort the Records.
13648 * If remote sorting is used, the sort is performed on the server, and the cache is
13649 * reloaded. If local sorting is used, the cache is sorted internally.
13650 * @param {String} fieldName The name of the field to sort by.
13651 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653 sort : function(fieldName, dir){
13654 var f = this.fields.get(fieldName);
13656 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13659 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13664 this.sortToggle[f.name] = dir;
13665 this.sortInfo = {field: f.name, direction: dir};
13666 if(!this.remoteSort){
13668 this.fireEvent("datachanged", this);
13670 this.load(this.lastOptions);
13675 * Calls the specified function for each of the Records in the cache.
13676 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13677 * Returning <em>false</em> aborts and exits the iteration.
13678 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680 each : function(fn, scope){
13681 this.data.each(fn, scope);
13685 * Gets all records modified since the last commit. Modified records are persisted across load operations
13686 * (e.g., during paging).
13687 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689 getModifiedRecords : function(){
13690 return this.modified;
13694 createFilterFn : function(property, value, anyMatch){
13695 if(!value.exec){ // not a regex
13696 value = String(value);
13697 if(value.length == 0){
13700 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702 return function(r){
13703 return value.test(r.data[property]);
13708 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13709 * @param {String} property A field on your records
13710 * @param {Number} start The record index to start at (defaults to 0)
13711 * @param {Number} end The last record index to include (defaults to length - 1)
13712 * @return {Number} The sum
13714 sum : function(property, start, end){
13715 var rs = this.data.items, v = 0;
13716 start = start || 0;
13717 end = (end || end === 0) ? end : rs.length-1;
13719 for(var i = start; i <= end; i++){
13720 v += (rs[i].data[property] || 0);
13726 * Filter the records by a specified property.
13727 * @param {String} field A field on your records
13728 * @param {String/RegExp} value Either a string that the field
13729 * should start with or a RegExp to test against the field
13730 * @param {Boolean} anyMatch True to match any part not just the beginning
13732 filter : function(property, value, anyMatch){
13733 var fn = this.createFilterFn(property, value, anyMatch);
13734 return fn ? this.filterBy(fn) : this.clearFilter();
13738 * Filter by a function. The specified function will be called with each
13739 * record in this data source. If the function returns true the record is included,
13740 * otherwise it is filtered.
13741 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13742 * @param {Object} scope (optional) The scope of the function (defaults to this)
13744 filterBy : function(fn, scope){
13745 this.snapshot = this.snapshot || this.data;
13746 this.data = this.queryBy(fn, scope||this);
13747 this.fireEvent("datachanged", this);
13751 * Query the records by a specified property.
13752 * @param {String} field A field on your records
13753 * @param {String/RegExp} value Either a string that the field
13754 * should start with or a RegExp to test against the field
13755 * @param {Boolean} anyMatch True to match any part not just the beginning
13756 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758 query : function(property, value, anyMatch){
13759 var fn = this.createFilterFn(property, value, anyMatch);
13760 return fn ? this.queryBy(fn) : this.data.clone();
13764 * Query by a function. The specified function will be called with each
13765 * record in this data source. If the function returns true the record is included
13767 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13768 * @param {Object} scope (optional) The scope of the function (defaults to this)
13769 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771 queryBy : function(fn, scope){
13772 var data = this.snapshot || this.data;
13773 return data.filterBy(fn, scope||this);
13777 * Collects unique values for a particular dataIndex from this store.
13778 * @param {String} dataIndex The property to collect
13779 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13780 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13781 * @return {Array} An array of the unique values
13783 collect : function(dataIndex, allowNull, bypassFilter){
13784 var d = (bypassFilter === true && this.snapshot) ?
13785 this.snapshot.items : this.data.items;
13786 var v, sv, r = [], l = {};
13787 for(var i = 0, len = d.length; i < len; i++){
13788 v = d[i].data[dataIndex];
13790 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13799 * Revert to a view of the Record cache with no filtering applied.
13800 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802 clearFilter : function(suppressEvent){
13803 if(this.snapshot && this.snapshot != this.data){
13804 this.data = this.snapshot;
13805 delete this.snapshot;
13806 if(suppressEvent !== true){
13807 this.fireEvent("datachanged", this);
13813 afterEdit : function(record){
13814 if(this.modified.indexOf(record) == -1){
13815 this.modified.push(record);
13817 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13821 afterReject : function(record){
13822 this.modified.remove(record);
13823 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13827 afterCommit : function(record){
13828 this.modified.remove(record);
13829 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13833 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13834 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836 commitChanges : function(){
13837 var m = this.modified.slice(0);
13838 this.modified = [];
13839 for(var i = 0, len = m.length; i < len; i++){
13845 * Cancel outstanding changes on all changed records.
13847 rejectChanges : function(){
13848 var m = this.modified.slice(0);
13849 this.modified = [];
13850 for(var i = 0, len = m.length; i < len; i++){
13855 onMetaChange : function(meta, rtype, o){
13856 this.recordType = rtype;
13857 this.fields = rtype.prototype.fields;
13858 delete this.snapshot;
13859 this.sortInfo = meta.sortInfo || this.sortInfo;
13860 this.modified = [];
13861 this.fireEvent('metachange', this, this.reader.meta);
13864 moveIndex : function(data, type)
13866 var index = this.indexOf(data);
13868 var newIndex = index + type;
13872 this.insert(newIndex, data);
13877 * Ext JS Library 1.1.1
13878 * Copyright(c) 2006-2007, Ext JS, LLC.
13880 * Originally Released Under LGPL - original licence link has changed is not relivant.
13883 * <script type="text/javascript">
13887 * @class Roo.data.SimpleStore
13888 * @extends Roo.data.Store
13889 * Small helper class to make creating Stores from Array data easier.
13890 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13891 * @cfg {Array} fields An array of field definition objects, or field name strings.
13892 * @cfg {Object} an existing reader (eg. copied from another store)
13893 * @cfg {Array} data The multi-dimensional array of data
13895 * @param {Object} config
13897 Roo.data.SimpleStore = function(config)
13899 Roo.data.SimpleStore.superclass.constructor.call(this, {
13901 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13904 Roo.data.Record.create(config.fields)
13906 proxy : new Roo.data.MemoryProxy(config.data)
13910 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912 * Ext JS Library 1.1.1
13913 * Copyright(c) 2006-2007, Ext JS, LLC.
13915 * Originally Released Under LGPL - original licence link has changed is not relivant.
13918 * <script type="text/javascript">
13923 * @extends Roo.data.Store
13924 * @class Roo.data.JsonStore
13925 * Small helper class to make creating Stores for JSON data easier. <br/>
13927 var store = new Roo.data.JsonStore({
13928 url: 'get-images.php',
13930 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13933 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13934 * JsonReader and HttpProxy (unless inline data is provided).</b>
13935 * @cfg {Array} fields An array of field definition objects, or field name strings.
13937 * @param {Object} config
13939 Roo.data.JsonStore = function(c){
13940 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13941 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13942 reader: new Roo.data.JsonReader(c, c.fields)
13945 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947 * Ext JS Library 1.1.1
13948 * Copyright(c) 2006-2007, Ext JS, LLC.
13950 * Originally Released Under LGPL - original licence link has changed is not relivant.
13953 * <script type="text/javascript">
13957 Roo.data.Field = function(config){
13958 if(typeof config == "string"){
13959 config = {name: config};
13961 Roo.apply(this, config);
13964 this.type = "auto";
13967 var st = Roo.data.SortTypes;
13968 // named sortTypes are supported, here we look them up
13969 if(typeof this.sortType == "string"){
13970 this.sortType = st[this.sortType];
13973 // set default sortType for strings and dates
13974 if(!this.sortType){
13977 this.sortType = st.asUCString;
13980 this.sortType = st.asDate;
13983 this.sortType = st.none;
13988 var stripRe = /[\$,%]/g;
13990 // prebuilt conversion function for this field, instead of
13991 // switching every time we're reading a value
13993 var cv, dateFormat = this.dateFormat;
13998 cv = function(v){ return v; };
14001 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14005 return v !== undefined && v !== null && v !== '' ?
14006 parseInt(String(v).replace(stripRe, ""), 10) : '';
14011 return v !== undefined && v !== null && v !== '' ?
14012 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14017 cv = function(v){ return v === true || v === "true" || v == 1; };
14024 if(v instanceof Date){
14028 if(dateFormat == "timestamp"){
14029 return new Date(v*1000);
14031 return Date.parseDate(v, dateFormat);
14033 var parsed = Date.parse(v);
14034 return parsed ? new Date(parsed) : null;
14043 Roo.data.Field.prototype = {
14051 * Ext JS Library 1.1.1
14052 * Copyright(c) 2006-2007, Ext JS, LLC.
14054 * Originally Released Under LGPL - original licence link has changed is not relivant.
14057 * <script type="text/javascript">
14060 // Base class for reading structured data from a data source. This class is intended to be
14061 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14064 * @class Roo.data.DataReader
14065 * Base class for reading structured data from a data source. This class is intended to be
14066 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14069 Roo.data.DataReader = function(meta, recordType){
14073 this.recordType = recordType instanceof Array ?
14074 Roo.data.Record.create(recordType) : recordType;
14077 Roo.data.DataReader.prototype = {
14080 readerType : 'Data',
14082 * Create an empty record
14083 * @param {Object} data (optional) - overlay some values
14084 * @return {Roo.data.Record} record created.
14086 newRow : function(d) {
14088 this.recordType.prototype.fields.each(function(c) {
14090 case 'int' : da[c.name] = 0; break;
14091 case 'date' : da[c.name] = new Date(); break;
14092 case 'float' : da[c.name] = 0.0; break;
14093 case 'boolean' : da[c.name] = false; break;
14094 default : da[c.name] = ""; break;
14098 return new this.recordType(Roo.apply(da, d));
14104 * Ext JS Library 1.1.1
14105 * Copyright(c) 2006-2007, Ext JS, LLC.
14107 * Originally Released Under LGPL - original licence link has changed is not relivant.
14110 * <script type="text/javascript">
14114 * @class Roo.data.DataProxy
14115 * @extends Roo.data.Observable
14116 * This class is an abstract base class for implementations which provide retrieval of
14117 * unformatted data objects.<br>
14119 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14120 * (of the appropriate type which knows how to parse the data object) to provide a block of
14121 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123 * Custom implementations must implement the load method as described in
14124 * {@link Roo.data.HttpProxy#load}.
14126 Roo.data.DataProxy = function(){
14129 * @event beforeload
14130 * Fires before a network request is made to retrieve a data object.
14131 * @param {Object} This DataProxy object.
14132 * @param {Object} params The params parameter to the load function.
14137 * Fires before the load method's callback is called.
14138 * @param {Object} This DataProxy object.
14139 * @param {Object} o The data object.
14140 * @param {Object} arg The callback argument object passed to the load function.
14144 * @event loadexception
14145 * Fires if an Exception occurs during data retrieval.
14146 * @param {Object} This DataProxy object.
14147 * @param {Object} o The data object.
14148 * @param {Object} arg The callback argument object passed to the load function.
14149 * @param {Object} e The Exception.
14151 loadexception : true
14153 Roo.data.DataProxy.superclass.constructor.call(this);
14156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14159 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14163 * Ext JS Library 1.1.1
14164 * Copyright(c) 2006-2007, Ext JS, LLC.
14166 * Originally Released Under LGPL - original licence link has changed is not relivant.
14169 * <script type="text/javascript">
14172 * @class Roo.data.MemoryProxy
14173 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14174 * to the Reader when its load method is called.
14176 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178 Roo.data.MemoryProxy = function(data){
14182 Roo.data.MemoryProxy.superclass.constructor.call(this);
14186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14189 * Load data from the requested source (in this case an in-memory
14190 * data object passed to the constructor), read the data object into
14191 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14192 * process that block using the passed callback.
14193 * @param {Object} params This parameter is not used by the MemoryProxy class.
14194 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14195 * object into a block of Roo.data.Records.
14196 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14197 * The function must be passed <ul>
14198 * <li>The Record block object</li>
14199 * <li>The "arg" argument from the load function</li>
14200 * <li>A boolean success indicator</li>
14202 * @param {Object} scope The scope in which to call the callback
14203 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205 load : function(params, reader, callback, scope, arg){
14206 params = params || {};
14209 result = reader.readRecords(params.data ? params.data :this.data);
14211 this.fireEvent("loadexception", this, arg, null, e);
14212 callback.call(scope, null, arg, false);
14215 callback.call(scope, result, arg, true);
14219 update : function(params, records){
14224 * Ext JS Library 1.1.1
14225 * Copyright(c) 2006-2007, Ext JS, LLC.
14227 * Originally Released Under LGPL - original licence link has changed is not relivant.
14230 * <script type="text/javascript">
14233 * @class Roo.data.HttpProxy
14234 * @extends Roo.data.DataProxy
14235 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14236 * configured to reference a certain URL.<br><br>
14238 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14239 * from which the running page was served.<br><br>
14241 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243 * Be aware that to enable the browser to parse an XML document, the server must set
14244 * the Content-Type header in the HTTP response to "text/xml".
14246 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14247 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14248 * will be used to make the request.
14250 Roo.data.HttpProxy = function(conn){
14251 Roo.data.HttpProxy.superclass.constructor.call(this);
14252 // is conn a conn config or a real conn?
14254 this.useAjax = !conn || !conn.events;
14258 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14259 // thse are take from connection...
14262 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14265 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14266 * extra parameters to each request made by this object. (defaults to undefined)
14269 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14270 * to each request made by this object. (defaults to undefined)
14273 * @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)
14276 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14279 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14285 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14289 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14290 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14291 * a finer-grained basis than the DataProxy events.
14293 getConnection : function(){
14294 return this.useAjax ? Roo.Ajax : this.conn;
14298 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14299 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14300 * process that block using the passed callback.
14301 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14302 * for the request to the remote server.
14303 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14304 * object into a block of Roo.data.Records.
14305 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14306 * The function must be passed <ul>
14307 * <li>The Record block object</li>
14308 * <li>The "arg" argument from the load function</li>
14309 * <li>A boolean success indicator</li>
14311 * @param {Object} scope The scope in which to call the callback
14312 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314 load : function(params, reader, callback, scope, arg){
14315 if(this.fireEvent("beforeload", this, params) !== false){
14317 params : params || {},
14319 callback : callback,
14324 callback : this.loadResponse,
14328 Roo.applyIf(o, this.conn);
14329 if(this.activeRequest){
14330 Roo.Ajax.abort(this.activeRequest);
14332 this.activeRequest = Roo.Ajax.request(o);
14334 this.conn.request(o);
14337 callback.call(scope||this, null, arg, false);
14342 loadResponse : function(o, success, response){
14343 delete this.activeRequest;
14345 this.fireEvent("loadexception", this, o, response);
14346 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351 result = o.reader.read(response);
14353 this.fireEvent("loadexception", this, o, response, e);
14354 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14358 this.fireEvent("load", this, o, o.request.arg);
14359 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14363 update : function(dataSet){
14368 updateResponse : function(dataSet){
14373 * Ext JS Library 1.1.1
14374 * Copyright(c) 2006-2007, Ext JS, LLC.
14376 * Originally Released Under LGPL - original licence link has changed is not relivant.
14379 * <script type="text/javascript">
14383 * @class Roo.data.ScriptTagProxy
14384 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14385 * other than the originating domain of the running page.<br><br>
14387 * <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
14388 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14391 * source code that is used as the source inside a <script> tag.<br><br>
14393 * In order for the browser to process the returned data, the server must wrap the data object
14394 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14395 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14396 * depending on whether the callback name was passed:
14399 boolean scriptTag = false;
14400 String cb = request.getParameter("callback");
14403 response.setContentType("text/javascript");
14405 response.setContentType("application/x-json");
14407 Writer out = response.getWriter();
14409 out.write(cb + "(");
14411 out.print(dataBlock.toJsonString());
14418 * @param {Object} config A configuration object.
14420 Roo.data.ScriptTagProxy = function(config){
14421 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14422 Roo.apply(this, config);
14423 this.head = document.getElementsByTagName("head")[0];
14426 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430 * @cfg {String} url The URL from which to request the data object.
14433 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14437 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14438 * the server the name of the callback function set up by the load call to process the returned data object.
14439 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14440 * javascript output which calls this named function passing the data object as its only parameter.
14442 callbackParam : "callback",
14444 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14445 * name to the request.
14450 * Load data from the configured URL, read the data object into
14451 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14452 * process that block using the passed callback.
14453 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14454 * for the request to the remote server.
14455 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14456 * object into a block of Roo.data.Records.
14457 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14458 * The function must be passed <ul>
14459 * <li>The Record block object</li>
14460 * <li>The "arg" argument from the load function</li>
14461 * <li>A boolean success indicator</li>
14463 * @param {Object} scope The scope in which to call the callback
14464 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466 load : function(params, reader, callback, scope, arg){
14467 if(this.fireEvent("beforeload", this, params) !== false){
14469 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471 var url = this.url;
14472 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474 url += "&_dc=" + (new Date().getTime());
14476 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14479 cb : "stcCallback"+transId,
14480 scriptId : "stcScript"+transId,
14484 callback : callback,
14490 window[trans.cb] = function(o){
14491 conn.handleResponse(o, trans);
14494 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496 if(this.autoAbort !== false){
14500 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502 var script = document.createElement("script");
14503 script.setAttribute("src", url);
14504 script.setAttribute("type", "text/javascript");
14505 script.setAttribute("id", trans.scriptId);
14506 this.head.appendChild(script);
14508 this.trans = trans;
14510 callback.call(scope||this, null, arg, false);
14515 isLoading : function(){
14516 return this.trans ? true : false;
14520 * Abort the current server request.
14522 abort : function(){
14523 if(this.isLoading()){
14524 this.destroyTrans(this.trans);
14529 destroyTrans : function(trans, isLoaded){
14530 this.head.removeChild(document.getElementById(trans.scriptId));
14531 clearTimeout(trans.timeoutId);
14533 window[trans.cb] = undefined;
14535 delete window[trans.cb];
14538 // if hasn't been loaded, wait for load to remove it to prevent script error
14539 window[trans.cb] = function(){
14540 window[trans.cb] = undefined;
14542 delete window[trans.cb];
14549 handleResponse : function(o, trans){
14550 this.trans = false;
14551 this.destroyTrans(trans, true);
14554 result = trans.reader.readRecords(o);
14556 this.fireEvent("loadexception", this, o, trans.arg, e);
14557 trans.callback.call(trans.scope||window, null, trans.arg, false);
14560 this.fireEvent("load", this, o, trans.arg);
14561 trans.callback.call(trans.scope||window, result, trans.arg, true);
14565 handleFailure : function(trans){
14566 this.trans = false;
14567 this.destroyTrans(trans, false);
14568 this.fireEvent("loadexception", this, null, trans.arg);
14569 trans.callback.call(trans.scope||window, null, trans.arg, false);
14573 * Ext JS Library 1.1.1
14574 * Copyright(c) 2006-2007, Ext JS, LLC.
14576 * Originally Released Under LGPL - original licence link has changed is not relivant.
14579 * <script type="text/javascript">
14583 * @class Roo.data.JsonReader
14584 * @extends Roo.data.DataReader
14585 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14586 * based on mappings in a provided Roo.data.Record constructor.
14588 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14589 * in the reply previously.
14594 var RecordDef = Roo.data.Record.create([
14595 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14596 {name: 'occupation'} // This field will use "occupation" as the mapping.
14598 var myReader = new Roo.data.JsonReader({
14599 totalProperty: "results", // The property which contains the total dataset size (optional)
14600 root: "rows", // The property which contains an Array of row objects
14601 id: "id" // The property within each row object that provides an ID for the record (optional)
14605 * This would consume a JSON file like this:
14607 { 'results': 2, 'rows': [
14608 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14609 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14612 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14613 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14614 * paged from the remote server.
14615 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14616 * @cfg {String} root name of the property which contains the Array of row objects.
14617 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14618 * @cfg {Array} fields Array of field definition objects
14620 * Create a new JsonReader
14621 * @param {Object} meta Metadata configuration options
14622 * @param {Object} recordType Either an Array of field definition objects,
14623 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625 Roo.data.JsonReader = function(meta, recordType){
14628 // set some defaults:
14629 Roo.applyIf(meta, {
14630 totalProperty: 'total',
14631 successProperty : 'success',
14636 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640 readerType : 'Json',
14643 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14644 * Used by Store query builder to append _requestMeta to params.
14647 metaFromRemote : false,
14649 * This method is only used by a DataProxy which has retrieved data from a remote server.
14650 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14651 * @return {Object} data A data block which is used by an Roo.data.Store object as
14652 * a cache of Roo.data.Records.
14654 read : function(response){
14655 var json = response.responseText;
14657 var o = /* eval:var:o */ eval("("+json+")");
14659 throw {message: "JsonReader.read: Json object not found"};
14665 this.metaFromRemote = true;
14666 this.meta = o.metaData;
14667 this.recordType = Roo.data.Record.create(o.metaData.fields);
14668 this.onMetaChange(this.meta, this.recordType, o);
14670 return this.readRecords(o);
14673 // private function a store will implement
14674 onMetaChange : function(meta, recordType, o){
14681 simpleAccess: function(obj, subsc) {
14688 getJsonAccessor: function(){
14690 return function(expr) {
14692 return(re.test(expr))
14693 ? new Function("obj", "return obj." + expr)
14698 return Roo.emptyFn;
14703 * Create a data block containing Roo.data.Records from an XML document.
14704 * @param {Object} o An object which contains an Array of row objects in the property specified
14705 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14706 * which contains the total size of the dataset.
14707 * @return {Object} data A data block which is used by an Roo.data.Store object as
14708 * a cache of Roo.data.Records.
14710 readRecords : function(o){
14712 * After any data loads, the raw JSON data is available for further custom processing.
14716 var s = this.meta, Record = this.recordType,
14717 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14721 if(s.totalProperty) {
14722 this.getTotal = this.getJsonAccessor(s.totalProperty);
14724 if(s.successProperty) {
14725 this.getSuccess = this.getJsonAccessor(s.successProperty);
14727 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729 var g = this.getJsonAccessor(s.id);
14730 this.getId = function(rec) {
14732 return (r === undefined || r === "") ? null : r;
14735 this.getId = function(){return null;};
14738 for(var jj = 0; jj < fl; jj++){
14740 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14741 this.ef[jj] = this.getJsonAccessor(map);
14745 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14746 if(s.totalProperty){
14747 var vt = parseInt(this.getTotal(o), 10);
14752 if(s.successProperty){
14753 var vs = this.getSuccess(o);
14754 if(vs === false || vs === 'false'){
14759 for(var i = 0; i < c; i++){
14762 var id = this.getId(n);
14763 for(var j = 0; j < fl; j++){
14765 var v = this.ef[j](n);
14767 Roo.log('missing convert for ' + f.name);
14771 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773 var record = new Record(values, id);
14775 records[i] = record;
14781 totalRecords : totalRecords
14784 // used when loading children.. @see loadDataFromChildren
14785 toLoadData: function(rec)
14787 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14788 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14789 return { data : data, total : data.length };
14794 * Ext JS Library 1.1.1
14795 * Copyright(c) 2006-2007, Ext JS, LLC.
14797 * Originally Released Under LGPL - original licence link has changed is not relivant.
14800 * <script type="text/javascript">
14804 * @class Roo.data.ArrayReader
14805 * @extends Roo.data.DataReader
14806 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14807 * Each element of that Array represents a row of data fields. The
14808 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14809 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14813 var RecordDef = Roo.data.Record.create([
14814 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14815 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14817 var myReader = new Roo.data.ArrayReader({
14818 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14822 * This would consume an Array like this:
14824 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14828 * Create a new JsonReader
14829 * @param {Object} meta Metadata configuration options.
14830 * @param {Object|Array} recordType Either an Array of field definition objects
14832 * @cfg {Array} fields Array of field definition objects
14833 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14834 * as specified to {@link Roo.data.Record#create},
14835 * or an {@link Roo.data.Record} object
14838 * created using {@link Roo.data.Record#create}.
14840 Roo.data.ArrayReader = function(meta, recordType)
14842 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14845 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14848 * Create a data block containing Roo.data.Records from an XML document.
14849 * @param {Object} o An Array of row objects which represents the dataset.
14850 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14851 * a cache of Roo.data.Records.
14853 readRecords : function(o)
14855 var sid = this.meta ? this.meta.id : null;
14856 var recordType = this.recordType, fields = recordType.prototype.fields;
14859 for(var i = 0; i < root.length; i++){
14862 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14863 for(var j = 0, jlen = fields.length; j < jlen; j++){
14864 var f = fields.items[j];
14865 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14866 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868 values[f.name] = v;
14870 var record = new recordType(values, id);
14872 records[records.length] = record;
14876 totalRecords : records.length
14879 // used when loading children.. @see loadDataFromChildren
14880 toLoadData: function(rec)
14882 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14883 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14894 * @class Roo.bootstrap.ComboBox
14895 * @extends Roo.bootstrap.TriggerField
14896 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14897 * @cfg {Boolean} append (true|false) default false
14898 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14899 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14900 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14901 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14902 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14903 * @cfg {Boolean} animate default true
14904 * @cfg {Boolean} emptyResultText only for touch device
14905 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14906 * @cfg {String} emptyTitle default ''
14907 * @cfg {Number} width fixed with? experimental
14909 * Create a new ComboBox.
14910 * @param {Object} config Configuration options
14912 Roo.bootstrap.ComboBox = function(config){
14913 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14917 * Fires when the dropdown list is expanded
14918 * @param {Roo.bootstrap.ComboBox} combo This combo box
14923 * Fires when the dropdown list is collapsed
14924 * @param {Roo.bootstrap.ComboBox} combo This combo box
14928 * @event beforeselect
14929 * Fires before a list item is selected. Return false to cancel the selection.
14930 * @param {Roo.bootstrap.ComboBox} combo This combo box
14931 * @param {Roo.data.Record} record The data record returned from the underlying store
14932 * @param {Number} index The index of the selected item in the dropdown list
14934 'beforeselect' : true,
14937 * Fires when a list item is selected
14938 * @param {Roo.bootstrap.ComboBox} combo This combo box
14939 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14940 * @param {Number} index The index of the selected item in the dropdown list
14944 * @event beforequery
14945 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14946 * The event object passed has these properties:
14947 * @param {Roo.bootstrap.ComboBox} combo This combo box
14948 * @param {String} query The query
14949 * @param {Boolean} forceAll true to force "all" query
14950 * @param {Boolean} cancel true to cancel the query
14951 * @param {Object} e The query event object
14953 'beforequery': true,
14956 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14957 * @param {Roo.bootstrap.ComboBox} combo This combo box
14962 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14963 * @param {Roo.bootstrap.ComboBox} combo This combo box
14964 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14969 * Fires when the remove value from the combobox array
14970 * @param {Roo.bootstrap.ComboBox} combo This combo box
14974 * @event afterremove
14975 * Fires when the remove value from the combobox array
14976 * @param {Roo.bootstrap.ComboBox} combo This combo box
14978 'afterremove' : true,
14980 * @event specialfilter
14981 * Fires when specialfilter
14982 * @param {Roo.bootstrap.ComboBox} combo This combo box
14984 'specialfilter' : true,
14987 * Fires when tick the element
14988 * @param {Roo.bootstrap.ComboBox} combo This combo box
14992 * @event touchviewdisplay
14993 * Fires when touch view require special display (default is using displayField)
14994 * @param {Roo.bootstrap.ComboBox} combo This combo box
14995 * @param {Object} cfg set html .
14997 'touchviewdisplay' : true
15002 this.tickItems = [];
15004 this.selectedIndex = -1;
15005 if(this.mode == 'local'){
15006 if(config.queryDelay === undefined){
15007 this.queryDelay = 10;
15009 if(config.minChars === undefined){
15015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15018 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15019 * rendering into an Roo.Editor, defaults to false)
15022 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15023 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15026 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15029 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15030 * the dropdown list (defaults to undefined, with no header element)
15034 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15038 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040 listWidth: undefined,
15042 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15043 * mode = 'remote' or 'text' if mode = 'local')
15045 displayField: undefined,
15048 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15049 * mode = 'remote' or 'value' if mode = 'local').
15050 * Note: use of a valueField requires the user make a selection
15051 * in order for a value to be mapped.
15053 valueField: undefined,
15055 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15060 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15061 * field's data value (defaults to the underlying DOM element's name)
15063 hiddenName: undefined,
15065 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15069 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071 selectedClass: 'active',
15074 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15078 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15079 * anchor positions (defaults to 'tl-bl')
15081 listAlign: 'tl-bl?',
15083 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15087 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15088 * query specified by the allQuery config option (defaults to 'query')
15090 triggerAction: 'query',
15092 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15093 * (defaults to 4, does not apply if editable = false)
15097 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15098 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15102 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15103 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15107 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15108 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15112 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15113 * when editable = true (defaults to false)
15115 selectOnFocus:false,
15117 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119 queryParam: 'query',
15121 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15122 * when mode = 'remote' (defaults to 'Loading...')
15124 loadingText: 'Loading...',
15126 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15130 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15134 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15135 * traditional select (defaults to true)
15139 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15143 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15147 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15148 * listWidth has a higher value)
15152 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15153 * allow the user to set arbitrary text into the field (defaults to false)
15155 forceSelection:false,
15157 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15158 * if typeAhead = true (defaults to 250)
15160 typeAheadDelay : 250,
15162 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15163 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165 valueNotFoundText : undefined,
15167 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169 blockFocus : false,
15172 * @cfg {Boolean} disableClear Disable showing of clear button.
15174 disableClear : false,
15176 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15178 alwaysQuery : false,
15181 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15186 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188 invalidClass : "has-warning",
15191 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193 validClass : "has-success",
15196 * @cfg {Boolean} specialFilter (true|false) special filter default false
15198 specialFilter : false,
15201 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203 mobileTouchView : true,
15206 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208 useNativeIOS : false,
15211 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213 mobile_restrict_height : false,
15215 ios_options : false,
15227 btnPosition : 'right',
15228 triggerList : true,
15229 showToggleBtn : true,
15231 emptyResultText: 'Empty',
15232 triggerText : 'Select',
15236 // element that contains real text value.. (when hidden is used..)
15238 getAutoCreate : function()
15243 * Render classic select for iso
15246 if(Roo.isIOS && this.useNativeIOS){
15247 cfg = this.getAutoCreateNativeIOS();
15255 if(Roo.isTouch && this.mobileTouchView){
15256 cfg = this.getAutoCreateTouchView();
15263 if(!this.tickable){
15264 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15269 * ComboBox with tickable selections
15272 var align = this.labelAlign || this.parentLabelAlign();
15275 cls : 'form-group roo-combobox-tickable' //input-group
15278 var btn_text_select = '';
15279 var btn_text_done = '';
15280 var btn_text_cancel = '';
15282 if (this.btn_text_show) {
15283 btn_text_select = 'Select';
15284 btn_text_done = 'Done';
15285 btn_text_cancel = 'Cancel';
15290 cls : 'tickable-buttons',
15295 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15296 //html : this.triggerText
15297 html: btn_text_select
15303 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305 html: btn_text_done
15311 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313 html: btn_text_cancel
15319 buttons.cn.unshift({
15321 cls: 'roo-select2-search-field-input'
15327 Roo.each(buttons.cn, function(c){
15329 c.cls += ' btn-' + _this.size;
15332 if (_this.disabled) {
15339 style : 'display: contents',
15344 cls: 'form-hidden-field'
15348 cls: 'roo-select2-choices',
15352 cls: 'roo-select2-search-field',
15363 cls: 'roo-select2-container input-group roo-select2-container-multi',
15369 // cls: 'typeahead typeahead-long dropdown-menu',
15370 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15375 if(this.hasFeedback && !this.allowBlank){
15379 cls: 'glyphicon form-control-feedback'
15382 combobox.cn.push(feedback);
15389 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15390 tooltip : 'This field is required'
15392 if (Roo.bootstrap.version == 4) {
15395 style : 'display:none'
15398 if (align ==='left' && this.fieldLabel.length) {
15400 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15407 cls : 'control-label col-form-label',
15408 html : this.fieldLabel
15420 var labelCfg = cfg.cn[1];
15421 var contentCfg = cfg.cn[2];
15424 if(this.indicatorpos == 'right'){
15430 cls : 'control-label col-form-label',
15434 html : this.fieldLabel
15450 labelCfg = cfg.cn[0];
15451 contentCfg = cfg.cn[1];
15455 if(this.labelWidth > 12){
15456 labelCfg.style = "width: " + this.labelWidth + 'px';
15458 if(this.width * 1 > 0){
15459 contentCfg.style = "width: " + this.width + 'px';
15461 if(this.labelWidth < 13 && this.labelmd == 0){
15462 this.labelmd = this.labelWidth;
15465 if(this.labellg > 0){
15466 labelCfg.cls += ' col-lg-' + this.labellg;
15467 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15470 if(this.labelmd > 0){
15471 labelCfg.cls += ' col-md-' + this.labelmd;
15472 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15475 if(this.labelsm > 0){
15476 labelCfg.cls += ' col-sm-' + this.labelsm;
15477 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15480 if(this.labelxs > 0){
15481 labelCfg.cls += ' col-xs-' + this.labelxs;
15482 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15486 } else if ( this.fieldLabel.length) {
15487 // Roo.log(" label");
15492 //cls : 'input-group-addon',
15493 html : this.fieldLabel
15498 if(this.indicatorpos == 'right'){
15502 //cls : 'input-group-addon',
15503 html : this.fieldLabel
15513 // Roo.log(" no label && no align");
15520 ['xs','sm','md','lg'].map(function(size){
15521 if (settings[size]) {
15522 cfg.cls += ' col-' + size + '-' + settings[size];
15530 _initEventsCalled : false,
15533 initEvents: function()
15535 if (this._initEventsCalled) { // as we call render... prevent looping...
15538 this._initEventsCalled = true;
15541 throw "can not find store for combo";
15544 this.indicator = this.indicatorEl();
15546 this.store = Roo.factory(this.store, Roo.data);
15547 this.store.parent = this;
15549 // if we are building from html. then this element is so complex, that we can not really
15550 // use the rendered HTML.
15551 // so we have to trash and replace the previous code.
15552 if (Roo.XComponent.build_from_html) {
15553 // remove this element....
15554 var e = this.el.dom, k=0;
15555 while (e ) { e = e.previousSibling; ++k;}
15560 this.rendered = false;
15562 this.render(this.parent().getChildContainer(true), k);
15565 if(Roo.isIOS && this.useNativeIOS){
15566 this.initIOSView();
15574 if(Roo.isTouch && this.mobileTouchView){
15575 this.initTouchView();
15580 this.initTickableEvents();
15584 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586 if(this.hiddenName){
15588 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590 this.hiddenField.dom.value =
15591 this.hiddenValue !== undefined ? this.hiddenValue :
15592 this.value !== undefined ? this.value : '';
15594 // prevent input submission
15595 this.el.dom.removeAttribute('name');
15596 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15601 // this.el.dom.setAttribute('autocomplete', 'off');
15604 var cls = 'x-combo-list';
15606 //this.list = new Roo.Layer({
15607 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15613 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15614 _this.list.setWidth(lw);
15617 this.list.on('mouseover', this.onViewOver, this);
15618 this.list.on('mousemove', this.onViewMove, this);
15619 this.list.on('scroll', this.onViewScroll, this);
15622 this.list.swallowEvent('mousewheel');
15623 this.assetHeight = 0;
15626 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15627 this.assetHeight += this.header.getHeight();
15630 this.innerList = this.list.createChild({cls:cls+'-inner'});
15631 this.innerList.on('mouseover', this.onViewOver, this);
15632 this.innerList.on('mousemove', this.onViewMove, this);
15633 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635 if(this.allowBlank && !this.pageSize && !this.disableClear){
15636 this.footer = this.list.createChild({cls:cls+'-ft'});
15637 this.pageTb = new Roo.Toolbar(this.footer);
15641 this.footer = this.list.createChild({cls:cls+'-ft'});
15642 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15643 {pageSize: this.pageSize});
15647 if (this.pageTb && this.allowBlank && !this.disableClear) {
15649 this.pageTb.add(new Roo.Toolbar.Fill(), {
15650 cls: 'x-btn-icon x-btn-clear',
15652 handler: function()
15655 _this.clearValue();
15656 _this.onSelect(false, -1);
15661 this.assetHeight += this.footer.getHeight();
15666 this.tpl = Roo.bootstrap.version == 4 ?
15667 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15668 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15671 this.view = new Roo.View(this.list, this.tpl, {
15672 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674 //this.view.wrapEl.setDisplayed(false);
15675 this.view.on('click', this.onViewClick, this);
15678 this.store.on('beforeload', this.onBeforeLoad, this);
15679 this.store.on('load', this.onLoad, this);
15680 this.store.on('loadexception', this.onLoadException, this);
15682 if(this.resizable){
15683 this.resizer = new Roo.Resizable(this.list, {
15684 pinned:true, handles:'se'
15686 this.resizer.on('resize', function(r, w, h){
15687 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15688 this.listWidth = w;
15689 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15690 this.restrictHeight();
15692 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15695 if(!this.editable){
15696 this.editable = true;
15697 this.setEditable(false);
15702 if (typeof(this.events.add.listeners) != 'undefined') {
15704 this.addicon = this.wrap.createChild(
15705 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15707 this.addicon.on('click', function(e) {
15708 this.fireEvent('add', this);
15711 if (typeof(this.events.edit.listeners) != 'undefined') {
15713 this.editicon = this.wrap.createChild(
15714 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15715 if (this.addicon) {
15716 this.editicon.setStyle('margin-left', '40px');
15718 this.editicon.on('click', function(e) {
15720 // we fire even if inothing is selected..
15721 this.fireEvent('edit', this, this.lastData );
15727 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15728 "up" : function(e){
15729 this.inKeyMode = true;
15733 "down" : function(e){
15734 if(!this.isExpanded()){
15735 this.onTriggerClick();
15737 this.inKeyMode = true;
15742 "enter" : function(e){
15743 // this.onViewClick();
15747 if(this.fireEvent("specialkey", this, e)){
15748 this.onViewClick(false);
15754 "esc" : function(e){
15758 "tab" : function(e){
15761 if(this.fireEvent("specialkey", this, e)){
15762 this.onViewClick(false);
15770 doRelay : function(foo, bar, hname){
15771 if(hname == 'down' || this.scope.isExpanded()){
15772 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15781 this.queryDelay = Math.max(this.queryDelay || 10,
15782 this.mode == 'local' ? 10 : 250);
15785 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787 if(this.typeAhead){
15788 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790 if(this.editable !== false){
15791 this.inputEl().on("keyup", this.onKeyUp, this);
15793 if(this.forceSelection){
15794 this.inputEl().on('blur', this.doForce, this);
15798 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15799 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15803 initTickableEvents: function()
15807 if(this.hiddenName){
15809 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811 this.hiddenField.dom.value =
15812 this.hiddenValue !== undefined ? this.hiddenValue :
15813 this.value !== undefined ? this.value : '';
15815 // prevent input submission
15816 this.el.dom.removeAttribute('name');
15817 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15822 // this.list = this.el.select('ul.dropdown-menu',true).first();
15824 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15825 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15826 if(this.triggerList){
15827 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15830 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15831 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15834 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15837 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15840 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15841 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15844 this.cancelBtn.hide();
15849 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15850 _this.list.setWidth(lw);
15853 this.list.on('mouseover', this.onViewOver, this);
15854 this.list.on('mousemove', this.onViewMove, this);
15856 this.list.on('scroll', this.onViewScroll, this);
15859 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15860 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15863 this.view = new Roo.View(this.list, this.tpl, {
15868 selectedClass: this.selectedClass
15871 //this.view.wrapEl.setDisplayed(false);
15872 this.view.on('click', this.onViewClick, this);
15876 this.store.on('beforeload', this.onBeforeLoad, this);
15877 this.store.on('load', this.onLoad, this);
15878 this.store.on('loadexception', this.onLoadException, this);
15881 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15882 "up" : function(e){
15883 this.inKeyMode = true;
15887 "down" : function(e){
15888 this.inKeyMode = true;
15892 "enter" : function(e){
15893 if(this.fireEvent("specialkey", this, e)){
15894 this.onViewClick(false);
15900 "esc" : function(e){
15901 this.onTickableFooterButtonClick(e, false, false);
15904 "tab" : function(e){
15905 this.fireEvent("specialkey", this, e);
15907 this.onTickableFooterButtonClick(e, false, false);
15914 doRelay : function(e, fn, key){
15915 if(this.scope.isExpanded()){
15916 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15925 this.queryDelay = Math.max(this.queryDelay || 10,
15926 this.mode == 'local' ? 10 : 250);
15929 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931 if(this.typeAhead){
15932 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15935 if(this.editable !== false){
15936 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15939 this.indicator = this.indicatorEl();
15941 if(this.indicator){
15942 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15943 this.indicator.hide();
15948 onDestroy : function(){
15950 this.view.setStore(null);
15951 this.view.el.removeAllListeners();
15952 this.view.el.remove();
15953 this.view.purgeListeners();
15956 this.list.dom.innerHTML = '';
15960 this.store.un('beforeload', this.onBeforeLoad, this);
15961 this.store.un('load', this.onLoad, this);
15962 this.store.un('loadexception', this.onLoadException, this);
15964 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15968 fireKey : function(e){
15969 if(e.isNavKeyPress() && !this.list.isVisible()){
15970 this.fireEvent("specialkey", this, e);
15975 onResize: function(w, h)
15979 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 // if(typeof w != 'number'){
15982 // // we do not handle it!?!?
15985 // var tw = this.trigger.getWidth();
15986 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15987 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 // this.inputEl().setWidth( this.adjustWidth('input', x));
15991 // //this.trigger.setStyle('left', x+'px');
15993 // if(this.list && this.listWidth === undefined){
15994 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15995 // this.list.setWidth(lw);
15996 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16004 * Allow or prevent the user from directly editing the field text. If false is passed,
16005 * the user will only be able to select from the items defined in the dropdown list. This method
16006 * is the runtime equivalent of setting the 'editable' config option at config time.
16007 * @param {Boolean} value True to allow the user to directly edit the field text
16009 setEditable : function(value){
16010 if(value == this.editable){
16013 this.editable = value;
16015 this.inputEl().dom.setAttribute('readOnly', true);
16016 this.inputEl().on('mousedown', this.onTriggerClick, this);
16017 this.inputEl().addClass('x-combo-noedit');
16019 this.inputEl().dom.setAttribute('readOnly', false);
16020 this.inputEl().un('mousedown', this.onTriggerClick, this);
16021 this.inputEl().removeClass('x-combo-noedit');
16027 onBeforeLoad : function(combo,opts){
16028 if(!this.hasFocus){
16032 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034 this.restrictHeight();
16035 this.selectedIndex = -1;
16039 onLoad : function(){
16041 this.hasQuery = false;
16043 if(!this.hasFocus){
16047 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16048 this.loading.hide();
16051 if(this.store.getCount() > 0){
16054 this.restrictHeight();
16055 if(this.lastQuery == this.allQuery){
16056 if(this.editable && !this.tickable){
16057 this.inputEl().dom.select();
16061 !this.selectByValue(this.value, true) &&
16064 !this.store.lastOptions ||
16065 typeof(this.store.lastOptions.add) == 'undefined' ||
16066 this.store.lastOptions.add != true
16069 this.select(0, true);
16072 if(this.autoFocus){
16075 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16076 this.taTask.delay(this.typeAheadDelay);
16080 this.onEmptyResults();
16086 onLoadException : function()
16088 this.hasQuery = false;
16090 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16091 this.loading.hide();
16094 if(this.tickable && this.editable){
16099 // only causes errors at present
16100 //Roo.log(this.store.reader.jsonData);
16101 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16109 onTypeAhead : function(){
16110 if(this.store.getCount() > 0){
16111 var r = this.store.getAt(0);
16112 var newValue = r.data[this.displayField];
16113 var len = newValue.length;
16114 var selStart = this.getRawValue().length;
16116 if(selStart != len){
16117 this.setRawValue(newValue);
16118 this.selectText(selStart, newValue.length);
16124 onSelect : function(record, index){
16126 if(this.fireEvent('beforeselect', this, record, index) !== false){
16128 this.setFromData(index > -1 ? record.data : false);
16131 this.fireEvent('select', this, record, index);
16136 * Returns the currently selected field value or empty string if no value is set.
16137 * @return {String} value The selected value
16139 getValue : function()
16141 if(Roo.isIOS && this.useNativeIOS){
16142 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16146 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16149 if(this.valueField){
16150 return typeof this.value != 'undefined' ? this.value : '';
16152 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16156 getRawValue : function()
16158 if(Roo.isIOS && this.useNativeIOS){
16159 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16162 var v = this.inputEl().getValue();
16168 * Clears any text/value currently set in the field
16170 clearValue : function(){
16172 if(this.hiddenField){
16173 this.hiddenField.dom.value = '';
16176 this.setRawValue('');
16177 this.lastSelectionText = '';
16178 this.lastData = false;
16180 var close = this.closeTriggerEl();
16191 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16192 * will be displayed in the field. If the value does not match the data value of an existing item,
16193 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16194 * Otherwise the field will be blank (although the value will still be set).
16195 * @param {String} value The value to match
16197 setValue : function(v)
16199 if(Roo.isIOS && this.useNativeIOS){
16200 this.setIOSValue(v);
16210 if(this.valueField){
16211 var r = this.findRecord(this.valueField, v);
16213 text = r.data[this.displayField];
16214 }else if(this.valueNotFoundText !== undefined){
16215 text = this.valueNotFoundText;
16218 this.lastSelectionText = text;
16219 if(this.hiddenField){
16220 this.hiddenField.dom.value = v;
16222 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16225 var close = this.closeTriggerEl();
16228 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16234 * @property {Object} the last set data for the element
16239 * Sets the value of the field based on a object which is related to the record format for the store.
16240 * @param {Object} value the value to set as. or false on reset?
16242 setFromData : function(o){
16249 var dv = ''; // display value
16250 var vv = ''; // value value..
16252 if (this.displayField) {
16253 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255 // this is an error condition!!!
16256 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16259 if(this.valueField){
16260 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16263 var close = this.closeTriggerEl();
16266 if(dv.length || vv * 1 > 0){
16268 this.blockFocus=true;
16274 if(this.hiddenField){
16275 this.hiddenField.dom.value = vv;
16277 this.lastSelectionText = dv;
16278 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282 // no hidden field.. - we store the value in 'value', but still display
16283 // display field!!!!
16284 this.lastSelectionText = dv;
16285 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16292 reset : function(){
16293 // overridden so that last data is reset..
16300 this.setValue(this.originalValue);
16301 //this.clearInvalid();
16302 this.lastData = false;
16304 this.view.clearSelections();
16310 findRecord : function(prop, value){
16312 if(this.store.getCount() > 0){
16313 this.store.each(function(r){
16314 if(r.data[prop] == value){
16324 getName: function()
16326 // returns hidden if it's set..
16327 if (!this.rendered) {return ''};
16328 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16332 onViewMove : function(e, t){
16333 this.inKeyMode = false;
16337 onViewOver : function(e, t){
16338 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16341 var item = this.view.findItemFromChild(t);
16344 var index = this.view.indexOf(item);
16345 this.select(index, false);
16350 onViewClick : function(view, doFocus, el, e)
16352 var index = this.view.getSelectedIndexes()[0];
16354 var r = this.store.getAt(index);
16358 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16365 Roo.each(this.tickItems, function(v,k){
16367 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369 _this.tickItems.splice(k, 1);
16371 if(typeof(e) == 'undefined' && view == false){
16372 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16384 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16385 this.tickItems.push(r.data);
16388 if(typeof(e) == 'undefined' && view == false){
16389 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16396 this.onSelect(r, index);
16398 if(doFocus !== false && !this.blockFocus){
16399 this.inputEl().focus();
16404 restrictHeight : function(){
16405 //this.innerList.dom.style.height = '';
16406 //var inner = this.innerList.dom;
16407 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16408 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16409 //this.list.beginUpdate();
16410 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16411 this.list.alignTo(this.inputEl(), this.listAlign);
16412 this.list.alignTo(this.inputEl(), this.listAlign);
16413 //this.list.endUpdate();
16417 onEmptyResults : function(){
16419 if(this.tickable && this.editable){
16420 this.hasFocus = false;
16421 this.restrictHeight();
16429 * Returns true if the dropdown list is expanded, else false.
16431 isExpanded : function(){
16432 return this.list.isVisible();
16436 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16437 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16438 * @param {String} value The data value of the item to select
16439 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16440 * selected item if it is not currently in view (defaults to true)
16441 * @return {Boolean} True if the value matched an item in the list, else false
16443 selectByValue : function(v, scrollIntoView){
16444 if(v !== undefined && v !== null){
16445 var r = this.findRecord(this.valueField || this.displayField, v);
16447 this.select(this.store.indexOf(r), scrollIntoView);
16455 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16456 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16457 * @param {Number} index The zero-based index of the list item to select
16458 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16459 * selected item if it is not currently in view (defaults to true)
16461 select : function(index, scrollIntoView){
16462 this.selectedIndex = index;
16463 this.view.select(index);
16464 if(scrollIntoView !== false){
16465 var el = this.view.getNode(index);
16467 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16470 this.list.scrollChildIntoView(el, false);
16476 selectNext : function(){
16477 var ct = this.store.getCount();
16479 if(this.selectedIndex == -1){
16481 }else if(this.selectedIndex < ct-1){
16482 this.select(this.selectedIndex+1);
16488 selectPrev : function(){
16489 var ct = this.store.getCount();
16491 if(this.selectedIndex == -1){
16493 }else if(this.selectedIndex != 0){
16494 this.select(this.selectedIndex-1);
16500 onKeyUp : function(e){
16501 if(this.editable !== false && !e.isSpecialKey()){
16502 this.lastKey = e.getKey();
16503 this.dqTask.delay(this.queryDelay);
16508 validateBlur : function(){
16509 return !this.list || !this.list.isVisible();
16513 initQuery : function(){
16515 var v = this.getRawValue();
16517 if(this.tickable && this.editable){
16518 v = this.tickableInputEl().getValue();
16525 doForce : function(){
16526 if(this.inputEl().dom.value.length > 0){
16527 this.inputEl().dom.value =
16528 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16534 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16535 * query allowing the query action to be canceled if needed.
16536 * @param {String} query The SQL query to execute
16537 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16538 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16539 * saved in the current store (defaults to false)
16541 doQuery : function(q, forceAll){
16543 if(q === undefined || q === null){
16548 forceAll: forceAll,
16552 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16557 forceAll = qe.forceAll;
16558 if(forceAll === true || (q.length >= this.minChars)){
16560 this.hasQuery = true;
16562 if(this.lastQuery != q || this.alwaysQuery){
16563 this.lastQuery = q;
16564 if(this.mode == 'local'){
16565 this.selectedIndex = -1;
16567 this.store.clearFilter();
16570 if(this.specialFilter){
16571 this.fireEvent('specialfilter', this);
16576 this.store.filter(this.displayField, q);
16579 this.store.fireEvent("datachanged", this.store);
16586 this.store.baseParams[this.queryParam] = q;
16588 var options = {params : this.getParams(q)};
16591 options.add = true;
16592 options.params.start = this.page * this.pageSize;
16595 this.store.load(options);
16598 * this code will make the page width larger, at the beginning, the list not align correctly,
16599 * we should expand the list on onLoad
16600 * so command out it
16605 this.selectedIndex = -1;
16610 this.loadNext = false;
16614 getParams : function(q){
16616 //p[this.queryParam] = q;
16620 p.limit = this.pageSize;
16626 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628 collapse : function(){
16629 if(!this.isExpanded()){
16635 this.hasFocus = false;
16639 this.cancelBtn.hide();
16640 this.trigger.show();
16643 this.tickableInputEl().dom.value = '';
16644 this.tickableInputEl().blur();
16649 Roo.get(document).un('mousedown', this.collapseIf, this);
16650 Roo.get(document).un('mousewheel', this.collapseIf, this);
16651 if (!this.editable) {
16652 Roo.get(document).un('keydown', this.listKeyPress, this);
16654 this.fireEvent('collapse', this);
16660 collapseIf : function(e){
16661 var in_combo = e.within(this.el);
16662 var in_list = e.within(this.list);
16663 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665 if (in_combo || in_list || is_list) {
16666 //e.stopPropagation();
16671 this.onTickableFooterButtonClick(e, false, false);
16679 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681 expand : function(){
16683 if(this.isExpanded() || !this.hasFocus){
16687 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16688 this.list.setWidth(lw);
16694 this.restrictHeight();
16698 this.tickItems = Roo.apply([], this.item);
16701 this.cancelBtn.show();
16702 this.trigger.hide();
16705 this.tickableInputEl().focus();
16710 Roo.get(document).on('mousedown', this.collapseIf, this);
16711 Roo.get(document).on('mousewheel', this.collapseIf, this);
16712 if (!this.editable) {
16713 Roo.get(document).on('keydown', this.listKeyPress, this);
16716 this.fireEvent('expand', this);
16720 // Implements the default empty TriggerField.onTriggerClick function
16721 onTriggerClick : function(e)
16723 Roo.log('trigger click');
16725 if(this.disabled || !this.triggerList){
16730 this.loadNext = false;
16732 if(this.isExpanded()){
16734 if (!this.blockFocus) {
16735 this.inputEl().focus();
16739 this.hasFocus = true;
16740 if(this.triggerAction == 'all') {
16741 this.doQuery(this.allQuery, true);
16743 this.doQuery(this.getRawValue());
16745 if (!this.blockFocus) {
16746 this.inputEl().focus();
16751 onTickableTriggerClick : function(e)
16758 this.loadNext = false;
16759 this.hasFocus = true;
16761 if(this.triggerAction == 'all') {
16762 this.doQuery(this.allQuery, true);
16764 this.doQuery(this.getRawValue());
16768 onSearchFieldClick : function(e)
16770 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16771 this.onTickableFooterButtonClick(e, false, false);
16775 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16780 this.loadNext = false;
16781 this.hasFocus = true;
16783 if(this.triggerAction == 'all') {
16784 this.doQuery(this.allQuery, true);
16786 this.doQuery(this.getRawValue());
16790 listKeyPress : function(e)
16792 //Roo.log('listkeypress');
16793 // scroll to first matching element based on key pres..
16794 if (e.isSpecialKey()) {
16797 var k = String.fromCharCode(e.getKey()).toUpperCase();
16800 var csel = this.view.getSelectedNodes();
16801 var cselitem = false;
16803 var ix = this.view.indexOf(csel[0]);
16804 cselitem = this.store.getAt(ix);
16805 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16811 this.store.each(function(v) {
16813 // start at existing selection.
16814 if (cselitem.id == v.id) {
16820 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16821 match = this.store.indexOf(v);
16827 if (match === false) {
16828 return true; // no more action?
16831 this.view.select(match);
16832 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16833 sn.scrollIntoView(sn.dom.parentNode, false);
16836 onViewScroll : function(e, t){
16838 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){
16842 this.hasQuery = true;
16844 this.loading = this.list.select('.loading', true).first();
16846 if(this.loading === null){
16847 this.list.createChild({
16849 cls: 'loading roo-select2-more-results roo-select2-active',
16850 html: 'Loading more results...'
16853 this.loading = this.list.select('.loading', true).first();
16855 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857 this.loading.hide();
16860 this.loading.show();
16865 this.loadNext = true;
16867 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16872 addItem : function(o)
16874 var dv = ''; // display value
16876 if (this.displayField) {
16877 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879 // this is an error condition!!!
16880 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16887 var choice = this.choices.createChild({
16889 cls: 'roo-select2-search-choice',
16898 cls: 'roo-select2-search-choice-close fa fa-times',
16903 }, this.searchField);
16905 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16915 this.inputEl().dom.value = '';
16920 onRemoveItem : function(e, _self, o)
16922 e.preventDefault();
16924 this.lastItem = Roo.apply([], this.item);
16926 var index = this.item.indexOf(o.data) * 1;
16929 Roo.log('not this item?!');
16933 this.item.splice(index, 1);
16938 this.fireEvent('remove', this, e);
16944 syncValue : function()
16946 if(!this.item.length){
16953 Roo.each(this.item, function(i){
16954 if(_this.valueField){
16955 value.push(i[_this.valueField]);
16962 this.value = value.join(',');
16964 if(this.hiddenField){
16965 this.hiddenField.dom.value = this.value;
16968 this.store.fireEvent("datachanged", this.store);
16973 clearItem : function()
16975 if(!this.multiple){
16981 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16989 if(this.tickable && !Roo.isTouch){
16990 this.view.refresh();
16994 inputEl: function ()
16996 if(Roo.isIOS && this.useNativeIOS){
16997 return this.el.select('select.roo-ios-select', true).first();
17000 if(Roo.isTouch && this.mobileTouchView){
17001 return this.el.select('input.form-control',true).first();
17005 return this.searchField;
17008 return this.el.select('input.form-control',true).first();
17011 onTickableFooterButtonClick : function(e, btn, el)
17013 e.preventDefault();
17015 this.lastItem = Roo.apply([], this.item);
17017 if(btn && btn.name == 'cancel'){
17018 this.tickItems = Roo.apply([], this.item);
17027 Roo.each(this.tickItems, function(o){
17035 validate : function()
17037 if(this.getVisibilityEl().hasClass('hidden')){
17041 var v = this.getRawValue();
17044 v = this.getValue();
17047 if(this.disabled || this.allowBlank || v.length){
17052 this.markInvalid();
17056 tickableInputEl : function()
17058 if(!this.tickable || !this.editable){
17059 return this.inputEl();
17062 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17066 getAutoCreateTouchView : function()
17071 cls: 'form-group' //input-group
17077 type : this.inputType,
17078 cls : 'form-control x-combo-noedit',
17079 autocomplete: 'new-password',
17080 placeholder : this.placeholder || '',
17085 input.name = this.name;
17089 input.cls += ' input-' + this.size;
17092 if (this.disabled) {
17093 input.disabled = true;
17097 cls : 'roo-combobox-wrap',
17104 inputblock.cls += ' input-group';
17106 inputblock.cn.unshift({
17108 cls : 'input-group-addon input-group-prepend input-group-text',
17113 if(this.removable && !this.multiple){
17114 inputblock.cls += ' roo-removable';
17116 inputblock.cn.push({
17119 cls : 'roo-combo-removable-btn close'
17123 if(this.hasFeedback && !this.allowBlank){
17125 inputblock.cls += ' has-feedback';
17127 inputblock.cn.push({
17129 cls: 'glyphicon form-control-feedback'
17136 inputblock.cls += (this.before) ? '' : ' input-group';
17138 inputblock.cn.push({
17140 cls : 'input-group-addon input-group-append input-group-text',
17146 var ibwrap = inputblock;
17151 cls: 'roo-select2-choices',
17155 cls: 'roo-select2-search-field',
17168 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17173 cls: 'form-hidden-field'
17179 if(!this.multiple && this.showToggleBtn){
17185 if (this.caret != false) {
17188 cls: 'fa fa-' + this.caret
17195 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197 Roo.bootstrap.version == 3 ? caret : '',
17200 cls: 'combobox-clear',
17214 combobox.cls += ' roo-select2-container-multi';
17217 var align = this.labelAlign || this.parentLabelAlign();
17219 if (align ==='left' && this.fieldLabel.length) {
17224 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17225 tooltip : 'This field is required'
17229 cls : 'control-label col-form-label',
17230 html : this.fieldLabel
17234 cls : 'roo-combobox-wrap ',
17241 var labelCfg = cfg.cn[1];
17242 var contentCfg = cfg.cn[2];
17245 if(this.indicatorpos == 'right'){
17250 cls : 'control-label col-form-label',
17254 html : this.fieldLabel
17258 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17259 tooltip : 'This field is required'
17264 cls : "roo-combobox-wrap ",
17272 labelCfg = cfg.cn[0];
17273 contentCfg = cfg.cn[1];
17278 if(this.labelWidth > 12){
17279 labelCfg.style = "width: " + this.labelWidth + 'px';
17282 if(this.labelWidth < 13 && this.labelmd == 0){
17283 this.labelmd = this.labelWidth;
17286 if(this.labellg > 0){
17287 labelCfg.cls += ' col-lg-' + this.labellg;
17288 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17291 if(this.labelmd > 0){
17292 labelCfg.cls += ' col-md-' + this.labelmd;
17293 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17296 if(this.labelsm > 0){
17297 labelCfg.cls += ' col-sm-' + this.labelsm;
17298 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17301 if(this.labelxs > 0){
17302 labelCfg.cls += ' col-xs-' + this.labelxs;
17303 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17307 } else if ( this.fieldLabel.length) {
17311 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17312 tooltip : 'This field is required'
17316 cls : 'control-label',
17317 html : this.fieldLabel
17328 if(this.indicatorpos == 'right'){
17332 cls : 'control-label',
17333 html : this.fieldLabel,
17337 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17338 tooltip : 'This field is required'
17355 var settings = this;
17357 ['xs','sm','md','lg'].map(function(size){
17358 if (settings[size]) {
17359 cfg.cls += ' col-' + size + '-' + settings[size];
17366 initTouchView : function()
17368 this.renderTouchView();
17370 this.touchViewEl.on('scroll', function(){
17371 this.el.dom.scrollTop = 0;
17374 this.originalValue = this.getValue();
17376 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378 this.inputEl().on("click", this.showTouchView, this);
17379 if (this.triggerEl) {
17380 this.triggerEl.on("click", this.showTouchView, this);
17384 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17385 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17390 this.store.on('load', this.onTouchViewLoad, this);
17391 this.store.on('loadexception', this.onTouchViewLoadException, this);
17393 if(this.hiddenName){
17395 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397 this.hiddenField.dom.value =
17398 this.hiddenValue !== undefined ? this.hiddenValue :
17399 this.value !== undefined ? this.value : '';
17401 this.el.dom.removeAttribute('name');
17402 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17406 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17407 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17410 if(this.removable && !this.multiple){
17411 var close = this.closeTriggerEl();
17413 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17414 close.on('click', this.removeBtnClick, this, close);
17418 * fix the bug in Safari iOS8
17420 this.inputEl().on("focus", function(e){
17421 document.activeElement.blur();
17424 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17431 renderTouchView : function()
17433 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17434 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17437 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17440 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441 this.touchViewBodyEl.setStyle('overflow', 'auto');
17443 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17444 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17447 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17451 showTouchView : function()
17457 this.touchViewHeaderEl.hide();
17459 if(this.modalTitle.length){
17460 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17461 this.touchViewHeaderEl.show();
17464 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17465 this.touchViewEl.show();
17467 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17470 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474 if(this.modalTitle.length){
17475 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17478 this.touchViewBodyEl.setHeight(bodyHeight);
17482 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484 this.touchViewEl.addClass(['in','show']);
17487 if(this._touchViewMask){
17488 Roo.get(document.body).addClass("x-body-masked");
17489 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17490 this._touchViewMask.setStyle('z-index', 10000);
17491 this._touchViewMask.addClass('show');
17494 this.doTouchViewQuery();
17498 hideTouchView : function()
17500 this.touchViewEl.removeClass(['in','show']);
17504 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506 this.touchViewEl.setStyle('display', 'none');
17509 if(this._touchViewMask){
17510 this._touchViewMask.removeClass('show');
17511 Roo.get(document.body).removeClass("x-body-masked");
17515 setTouchViewValue : function()
17522 Roo.each(this.tickItems, function(o){
17527 this.hideTouchView();
17530 doTouchViewQuery : function()
17539 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17543 if(!this.alwaysQuery || this.mode == 'local'){
17544 this.onTouchViewLoad();
17551 onTouchViewBeforeLoad : function(combo,opts)
17557 onTouchViewLoad : function()
17559 if(this.store.getCount() < 1){
17560 this.onTouchViewEmptyResults();
17564 this.clearTouchView();
17566 var rawValue = this.getRawValue();
17568 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570 this.tickItems = [];
17572 this.store.data.each(function(d, rowIndex){
17573 var row = this.touchViewListGroup.createChild(template);
17575 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17576 row.addClass(d.data.cls);
17579 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17582 html : d.data[this.displayField]
17585 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17586 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17589 row.removeClass('selected');
17590 if(!this.multiple && this.valueField &&
17591 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17594 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17595 row.addClass('selected');
17598 if(this.multiple && this.valueField &&
17599 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17603 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17604 this.tickItems.push(d.data);
17607 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17611 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615 if(this.modalTitle.length){
17616 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17619 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621 if(this.mobile_restrict_height && listHeight < bodyHeight){
17622 this.touchViewBodyEl.setHeight(listHeight);
17627 if(firstChecked && listHeight > bodyHeight){
17628 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17633 onTouchViewLoadException : function()
17635 this.hideTouchView();
17638 onTouchViewEmptyResults : function()
17640 this.clearTouchView();
17642 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17648 clearTouchView : function()
17650 this.touchViewListGroup.dom.innerHTML = '';
17653 onTouchViewClick : function(e, el, o)
17655 e.preventDefault();
17658 var rowIndex = o.rowIndex;
17660 var r = this.store.getAt(rowIndex);
17662 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664 if(!this.multiple){
17665 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17666 c.dom.removeAttribute('checked');
17669 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671 this.setFromData(r.data);
17673 var close = this.closeTriggerEl();
17679 this.hideTouchView();
17681 this.fireEvent('select', this, r, rowIndex);
17686 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17687 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17688 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17692 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17693 this.addItem(r.data);
17694 this.tickItems.push(r.data);
17698 getAutoCreateNativeIOS : function()
17701 cls: 'form-group' //input-group,
17706 cls : 'roo-ios-select'
17710 combobox.name = this.name;
17713 if (this.disabled) {
17714 combobox.disabled = true;
17717 var settings = this;
17719 ['xs','sm','md','lg'].map(function(size){
17720 if (settings[size]) {
17721 cfg.cls += ' col-' + size + '-' + settings[size];
17731 initIOSView : function()
17733 this.store.on('load', this.onIOSViewLoad, this);
17738 onIOSViewLoad : function()
17740 if(this.store.getCount() < 1){
17744 this.clearIOSView();
17746 if(this.allowBlank) {
17748 var default_text = '-- SELECT --';
17750 if(this.placeholder.length){
17751 default_text = this.placeholder;
17754 if(this.emptyTitle.length){
17755 default_text += ' - ' + this.emptyTitle + ' -';
17758 var opt = this.inputEl().createChild({
17761 html : default_text
17765 o[this.valueField] = 0;
17766 o[this.displayField] = default_text;
17768 this.ios_options.push({
17775 this.store.data.each(function(d, rowIndex){
17779 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17780 html = d.data[this.displayField];
17785 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17786 value = d.data[this.valueField];
17795 if(this.value == d.data[this.valueField]){
17796 option['selected'] = true;
17799 var opt = this.inputEl().createChild(option);
17801 this.ios_options.push({
17808 this.inputEl().on('change', function(){
17809 this.fireEvent('select', this);
17814 clearIOSView: function()
17816 this.inputEl().dom.innerHTML = '';
17818 this.ios_options = [];
17821 setIOSValue: function(v)
17825 if(!this.ios_options){
17829 Roo.each(this.ios_options, function(opts){
17831 opts.el.dom.removeAttribute('selected');
17833 if(opts.data[this.valueField] != v){
17837 opts.el.dom.setAttribute('selected', true);
17843 * @cfg {Boolean} grow
17847 * @cfg {Number} growMin
17851 * @cfg {Number} growMax
17860 Roo.apply(Roo.bootstrap.ComboBox, {
17864 cls: 'modal-header',
17886 cls: 'list-group-item',
17890 cls: 'roo-combobox-list-group-item-value'
17894 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17908 listItemCheckbox : {
17910 cls: 'list-group-item',
17914 cls: 'roo-combobox-list-group-item-value'
17918 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17934 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17939 cls: 'modal-footer',
17947 cls: 'col-xs-6 text-left',
17950 cls: 'btn btn-danger roo-touch-view-cancel',
17956 cls: 'col-xs-6 text-right',
17959 cls: 'btn btn-success roo-touch-view-ok',
17970 Roo.apply(Roo.bootstrap.ComboBox, {
17972 touchViewTemplate : {
17974 cls: 'modal fade roo-combobox-touch-view',
17978 cls: 'modal-dialog',
17979 style : 'position:fixed', // we have to fix position....
17983 cls: 'modal-content',
17985 Roo.bootstrap.ComboBox.header,
17986 Roo.bootstrap.ComboBox.body,
17987 Roo.bootstrap.ComboBox.footer
17996 * Ext JS Library 1.1.1
17997 * Copyright(c) 2006-2007, Ext JS, LLC.
17999 * Originally Released Under LGPL - original licence link has changed is not relivant.
18002 * <script type="text/javascript">
18007 * @extends Roo.util.Observable
18008 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18009 * This class also supports single and multi selection modes. <br>
18010 * Create a data model bound view:
18012 var store = new Roo.data.Store(...);
18014 var view = new Roo.View({
18016 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18018 singleSelect: true,
18019 selectedClass: "ydataview-selected",
18023 // listen for node click?
18024 view.on("click", function(vw, index, node, e){
18025 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18029 dataModel.load("foobar.xml");
18031 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18034 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036 * Note: old style constructor is still suported (container, template, config)
18039 * Create a new View
18040 * @param {Object} config The config object
18043 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045 this.parent = false;
18047 if (typeof(depreciated_tpl) == 'undefined') {
18048 // new way.. - universal constructor.
18049 Roo.apply(this, config);
18050 this.el = Roo.get(this.el);
18053 this.el = Roo.get(config);
18054 this.tpl = depreciated_tpl;
18055 Roo.apply(this, depreciated_config);
18057 this.wrapEl = this.el.wrap().wrap();
18058 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18061 if(typeof(this.tpl) == "string"){
18062 this.tpl = new Roo.Template(this.tpl);
18064 // support xtype ctors..
18065 this.tpl = new Roo.factory(this.tpl, Roo);
18069 this.tpl.compile();
18074 * @event beforeclick
18075 * Fires before a click is processed. Returns false to cancel the default action.
18076 * @param {Roo.View} this
18077 * @param {Number} index The index of the target node
18078 * @param {HTMLElement} node The target node
18079 * @param {Roo.EventObject} e The raw event object
18081 "beforeclick" : true,
18084 * Fires when a template node is clicked.
18085 * @param {Roo.View} this
18086 * @param {Number} index The index of the target node
18087 * @param {HTMLElement} node The target node
18088 * @param {Roo.EventObject} e The raw event object
18093 * Fires when a template node is double clicked.
18094 * @param {Roo.View} this
18095 * @param {Number} index The index of the target node
18096 * @param {HTMLElement} node The target node
18097 * @param {Roo.EventObject} e The raw event object
18101 * @event contextmenu
18102 * Fires when a template node is right clicked.
18103 * @param {Roo.View} this
18104 * @param {Number} index The index of the target node
18105 * @param {HTMLElement} node The target node
18106 * @param {Roo.EventObject} e The raw event object
18108 "contextmenu" : true,
18110 * @event selectionchange
18111 * Fires when the selected nodes change.
18112 * @param {Roo.View} this
18113 * @param {Array} selections Array of the selected nodes
18115 "selectionchange" : true,
18118 * @event beforeselect
18119 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18120 * @param {Roo.View} this
18121 * @param {HTMLElement} node The node to be selected
18122 * @param {Array} selections Array of currently selected nodes
18124 "beforeselect" : true,
18126 * @event preparedata
18127 * Fires on every row to render, to allow you to change the data.
18128 * @param {Roo.View} this
18129 * @param {Object} data to be rendered (change this)
18131 "preparedata" : true
18139 "click": this.onClick,
18140 "dblclick": this.onDblClick,
18141 "contextmenu": this.onContextMenu,
18145 this.selections = [];
18147 this.cmp = new Roo.CompositeElementLite([]);
18149 this.store = Roo.factory(this.store, Roo.data);
18150 this.setStore(this.store, true);
18153 if ( this.footer && this.footer.xtype) {
18155 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157 this.footer.dataSource = this.store;
18158 this.footer.container = fctr;
18159 this.footer = Roo.factory(this.footer, Roo);
18160 fctr.insertFirst(this.el);
18162 // this is a bit insane - as the paging toolbar seems to detach the el..
18163 // dom.parentNode.parentNode.parentNode
18164 // they get detached?
18168 Roo.View.superclass.constructor.call(this);
18173 Roo.extend(Roo.View, Roo.util.Observable, {
18176 * @cfg {Roo.data.Store} store Data store to load data from.
18181 * @cfg {String|Roo.Element} el The container element.
18186 * @cfg {String|Roo.Template} tpl The template used by this View
18190 * @cfg {String} dataName the named area of the template to use as the data area
18191 * Works with domtemplates roo-name="name"
18195 * @cfg {String} selectedClass The css class to add to selected nodes
18197 selectedClass : "x-view-selected",
18199 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18204 * @cfg {String} text to display on mask (default Loading)
18208 * @cfg {Boolean} multiSelect Allow multiple selection
18210 multiSelect : false,
18212 * @cfg {Boolean} singleSelect Allow single selection
18214 singleSelect: false,
18217 * @cfg {Boolean} toggleSelect - selecting
18219 toggleSelect : false,
18222 * @cfg {Boolean} tickable - selecting
18227 * Returns the element this view is bound to.
18228 * @return {Roo.Element}
18230 getEl : function(){
18231 return this.wrapEl;
18237 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239 refresh : function(){
18240 //Roo.log('refresh');
18243 // if we are using something like 'domtemplate', then
18244 // the what gets used is:
18245 // t.applySubtemplate(NAME, data, wrapping data..)
18246 // the outer template then get' applied with
18247 // the store 'extra data'
18248 // and the body get's added to the
18249 // roo-name="data" node?
18250 // <span class='roo-tpl-{name}'></span> ?????
18254 this.clearSelections();
18255 this.el.update("");
18257 var records = this.store.getRange();
18258 if(records.length < 1) {
18260 // is this valid?? = should it render a template??
18262 this.el.update(this.emptyText);
18266 if (this.dataName) {
18267 this.el.update(t.apply(this.store.meta)); //????
18268 el = this.el.child('.roo-tpl-' + this.dataName);
18271 for(var i = 0, len = records.length; i < len; i++){
18272 var data = this.prepareData(records[i].data, i, records[i]);
18273 this.fireEvent("preparedata", this, data, i, records[i]);
18275 var d = Roo.apply({}, data);
18278 Roo.apply(d, {'roo-id' : Roo.id()});
18282 Roo.each(this.parent.item, function(item){
18283 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18286 Roo.apply(d, {'roo-data-checked' : 'checked'});
18290 html[html.length] = Roo.util.Format.trim(
18292 t.applySubtemplate(this.dataName, d, this.store.meta) :
18299 el.update(html.join(""));
18300 this.nodes = el.dom.childNodes;
18301 this.updateIndexes(0);
18306 * Function to override to reformat the data that is sent to
18307 * the template for each node.
18308 * DEPRICATED - use the preparedata event handler.
18309 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18310 * a JSON object for an UpdateManager bound view).
18312 prepareData : function(data, index, record)
18314 this.fireEvent("preparedata", this, data, index, record);
18318 onUpdate : function(ds, record){
18319 // Roo.log('on update');
18320 this.clearSelections();
18321 var index = this.store.indexOf(record);
18322 var n = this.nodes[index];
18323 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18324 n.parentNode.removeChild(n);
18325 this.updateIndexes(index, index);
18331 onAdd : function(ds, records, index)
18333 //Roo.log(['on Add', ds, records, index] );
18334 this.clearSelections();
18335 if(this.nodes.length == 0){
18339 var n = this.nodes[index];
18340 for(var i = 0, len = records.length; i < len; i++){
18341 var d = this.prepareData(records[i].data, i, records[i]);
18343 this.tpl.insertBefore(n, d);
18346 this.tpl.append(this.el, d);
18349 this.updateIndexes(index);
18352 onRemove : function(ds, record, index){
18353 // Roo.log('onRemove');
18354 this.clearSelections();
18355 var el = this.dataName ?
18356 this.el.child('.roo-tpl-' + this.dataName) :
18359 el.dom.removeChild(this.nodes[index]);
18360 this.updateIndexes(index);
18364 * Refresh an individual node.
18365 * @param {Number} index
18367 refreshNode : function(index){
18368 this.onUpdate(this.store, this.store.getAt(index));
18371 updateIndexes : function(startIndex, endIndex){
18372 var ns = this.nodes;
18373 startIndex = startIndex || 0;
18374 endIndex = endIndex || ns.length - 1;
18375 for(var i = startIndex; i <= endIndex; i++){
18376 ns[i].nodeIndex = i;
18381 * Changes the data store this view uses and refresh the view.
18382 * @param {Store} store
18384 setStore : function(store, initial){
18385 if(!initial && this.store){
18386 this.store.un("datachanged", this.refresh);
18387 this.store.un("add", this.onAdd);
18388 this.store.un("remove", this.onRemove);
18389 this.store.un("update", this.onUpdate);
18390 this.store.un("clear", this.refresh);
18391 this.store.un("beforeload", this.onBeforeLoad);
18392 this.store.un("load", this.onLoad);
18393 this.store.un("loadexception", this.onLoad);
18397 store.on("datachanged", this.refresh, this);
18398 store.on("add", this.onAdd, this);
18399 store.on("remove", this.onRemove, this);
18400 store.on("update", this.onUpdate, this);
18401 store.on("clear", this.refresh, this);
18402 store.on("beforeload", this.onBeforeLoad, this);
18403 store.on("load", this.onLoad, this);
18404 store.on("loadexception", this.onLoad, this);
18412 * onbeforeLoad - masks the loading area.
18415 onBeforeLoad : function(store,opts)
18417 //Roo.log('onBeforeLoad');
18419 this.el.update("");
18421 this.el.mask(this.mask ? this.mask : "Loading" );
18423 onLoad : function ()
18430 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18431 * @param {HTMLElement} node
18432 * @return {HTMLElement} The template node
18434 findItemFromChild : function(node){
18435 var el = this.dataName ?
18436 this.el.child('.roo-tpl-' + this.dataName,true) :
18439 if(!node || node.parentNode == el){
18442 var p = node.parentNode;
18443 while(p && p != el){
18444 if(p.parentNode == el){
18453 onClick : function(e){
18454 var item = this.findItemFromChild(e.getTarget());
18456 var index = this.indexOf(item);
18457 if(this.onItemClick(item, index, e) !== false){
18458 this.fireEvent("click", this, index, item, e);
18461 this.clearSelections();
18466 onContextMenu : function(e){
18467 var item = this.findItemFromChild(e.getTarget());
18469 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18474 onDblClick : function(e){
18475 var item = this.findItemFromChild(e.getTarget());
18477 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18481 onItemClick : function(item, index, e)
18483 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18486 if (this.toggleSelect) {
18487 var m = this.isSelected(item) ? 'unselect' : 'select';
18490 _t[m](item, true, false);
18493 if(this.multiSelect || this.singleSelect){
18494 if(this.multiSelect && e.shiftKey && this.lastSelection){
18495 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497 this.select(item, this.multiSelect && e.ctrlKey);
18498 this.lastSelection = item;
18501 if(!this.tickable){
18502 e.preventDefault();
18510 * Get the number of selected nodes.
18513 getSelectionCount : function(){
18514 return this.selections.length;
18518 * Get the currently selected nodes.
18519 * @return {Array} An array of HTMLElements
18521 getSelectedNodes : function(){
18522 return this.selections;
18526 * Get the indexes of the selected nodes.
18529 getSelectedIndexes : function(){
18530 var indexes = [], s = this.selections;
18531 for(var i = 0, len = s.length; i < len; i++){
18532 indexes.push(s[i].nodeIndex);
18538 * Clear all selections
18539 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541 clearSelections : function(suppressEvent){
18542 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18543 this.cmp.elements = this.selections;
18544 this.cmp.removeClass(this.selectedClass);
18545 this.selections = [];
18546 if(!suppressEvent){
18547 this.fireEvent("selectionchange", this, this.selections);
18553 * Returns true if the passed node is selected
18554 * @param {HTMLElement/Number} node The node or node index
18555 * @return {Boolean}
18557 isSelected : function(node){
18558 var s = this.selections;
18562 node = this.getNode(node);
18563 return s.indexOf(node) !== -1;
18568 * @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
18569 * @param {Boolean} keepExisting (optional) true to keep existing selections
18570 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572 select : function(nodeInfo, keepExisting, suppressEvent){
18573 if(nodeInfo instanceof Array){
18575 this.clearSelections(true);
18577 for(var i = 0, len = nodeInfo.length; i < len; i++){
18578 this.select(nodeInfo[i], true, true);
18582 var node = this.getNode(nodeInfo);
18583 if(!node || this.isSelected(node)){
18584 return; // already selected.
18587 this.clearSelections(true);
18590 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18591 Roo.fly(node).addClass(this.selectedClass);
18592 this.selections.push(node);
18593 if(!suppressEvent){
18594 this.fireEvent("selectionchange", this, this.selections);
18602 * @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
18603 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18604 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606 unselect : function(nodeInfo, keepExisting, suppressEvent)
18608 if(nodeInfo instanceof Array){
18609 Roo.each(this.selections, function(s) {
18610 this.unselect(s, nodeInfo);
18614 var node = this.getNode(nodeInfo);
18615 if(!node || !this.isSelected(node)){
18616 //Roo.log("not selected");
18617 return; // not selected.
18621 Roo.each(this.selections, function(s) {
18623 Roo.fly(node).removeClass(this.selectedClass);
18630 this.selections= ns;
18631 this.fireEvent("selectionchange", this, this.selections);
18635 * Gets a template node.
18636 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18637 * @return {HTMLElement} The node or null if it wasn't found
18639 getNode : function(nodeInfo){
18640 if(typeof nodeInfo == "string"){
18641 return document.getElementById(nodeInfo);
18642 }else if(typeof nodeInfo == "number"){
18643 return this.nodes[nodeInfo];
18649 * Gets a range template nodes.
18650 * @param {Number} startIndex
18651 * @param {Number} endIndex
18652 * @return {Array} An array of nodes
18654 getNodes : function(start, end){
18655 var ns = this.nodes;
18656 start = start || 0;
18657 end = typeof end == "undefined" ? ns.length - 1 : end;
18660 for(var i = start; i <= end; i++){
18664 for(var i = start; i >= end; i--){
18672 * Finds the index of the passed node
18673 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18674 * @return {Number} The index of the node or -1
18676 indexOf : function(node){
18677 node = this.getNode(node);
18678 if(typeof node.nodeIndex == "number"){
18679 return node.nodeIndex;
18681 var ns = this.nodes;
18682 for(var i = 0, len = ns.length; i < len; i++){
18693 * based on jquery fullcalendar
18697 Roo.bootstrap = Roo.bootstrap || {};
18699 * @class Roo.bootstrap.Calendar
18700 * @extends Roo.bootstrap.Component
18701 * Bootstrap Calendar class
18702 * @cfg {Boolean} loadMask (true|false) default false
18703 * @cfg {Object} header generate the user specific header of the calendar, default false
18706 * Create a new Container
18707 * @param {Object} config The config object
18712 Roo.bootstrap.Calendar = function(config){
18713 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18717 * Fires when a date is selected
18718 * @param {DatePicker} this
18719 * @param {Date} date The selected date
18723 * @event monthchange
18724 * Fires when the displayed month changes
18725 * @param {DatePicker} this
18726 * @param {Date} date The selected month
18728 'monthchange': true,
18730 * @event evententer
18731 * Fires when mouse over an event
18732 * @param {Calendar} this
18733 * @param {event} Event
18735 'evententer': true,
18737 * @event eventleave
18738 * Fires when the mouse leaves an
18739 * @param {Calendar} this
18742 'eventleave': true,
18744 * @event eventclick
18745 * Fires when the mouse click an
18746 * @param {Calendar} this
18755 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18758 * @cfg {Number} startDay
18759 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18767 getAutoCreate : function(){
18770 var fc_button = function(name, corner, style, content ) {
18771 return Roo.apply({},{
18773 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18775 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18778 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18789 style : 'width:100%',
18796 cls : 'fc-header-left',
18798 fc_button('prev', 'left', 'arrow', '‹' ),
18799 fc_button('next', 'right', 'arrow', '›' ),
18800 { tag: 'span', cls: 'fc-header-space' },
18801 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18809 cls : 'fc-header-center',
18813 cls: 'fc-header-title',
18816 html : 'month / year'
18824 cls : 'fc-header-right',
18826 /* fc_button('month', 'left', '', 'month' ),
18827 fc_button('week', '', '', 'week' ),
18828 fc_button('day', 'right', '', 'day' )
18840 header = this.header;
18843 var cal_heads = function() {
18845 // fixme - handle this.
18847 for (var i =0; i < Date.dayNames.length; i++) {
18848 var d = Date.dayNames[i];
18851 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18852 html : d.substring(0,3)
18856 ret[0].cls += ' fc-first';
18857 ret[6].cls += ' fc-last';
18860 var cal_cell = function(n) {
18863 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18868 cls: 'fc-day-number',
18872 cls: 'fc-day-content',
18876 style: 'position: relative;' // height: 17px;
18888 var cal_rows = function() {
18891 for (var r = 0; r < 6; r++) {
18898 for (var i =0; i < Date.dayNames.length; i++) {
18899 var d = Date.dayNames[i];
18900 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18903 row.cn[0].cls+=' fc-first';
18904 row.cn[0].cn[0].style = 'min-height:90px';
18905 row.cn[6].cls+=' fc-last';
18909 ret[0].cls += ' fc-first';
18910 ret[4].cls += ' fc-prev-last';
18911 ret[5].cls += ' fc-last';
18918 cls: 'fc-border-separate',
18919 style : 'width:100%',
18927 cls : 'fc-first fc-last',
18945 cls : 'fc-content',
18946 style : "position: relative;",
18949 cls : 'fc-view fc-view-month fc-grid',
18950 style : 'position: relative',
18951 unselectable : 'on',
18954 cls : 'fc-event-container',
18955 style : 'position:absolute;z-index:8;top:0;left:0;'
18973 initEvents : function()
18976 throw "can not find store for calendar";
18982 style: "text-align:center",
18986 style: "background-color:white;width:50%;margin:250 auto",
18990 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19001 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003 var size = this.el.select('.fc-content', true).first().getSize();
19004 this.maskEl.setSize(size.width, size.height);
19005 this.maskEl.enableDisplayMode("block");
19006 if(!this.loadMask){
19007 this.maskEl.hide();
19010 this.store = Roo.factory(this.store, Roo.data);
19011 this.store.on('load', this.onLoad, this);
19012 this.store.on('beforeload', this.onBeforeLoad, this);
19016 this.cells = this.el.select('.fc-day',true);
19017 //Roo.log(this.cells);
19018 this.textNodes = this.el.query('.fc-day-number');
19019 this.cells.addClassOnOver('fc-state-hover');
19021 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19022 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19023 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19024 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026 this.on('monthchange', this.onMonthChange, this);
19028 this.update(new Date().clearTime());
19031 resize : function() {
19032 var sz = this.el.getSize();
19034 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19035 this.el.select('.fc-day-content div',true).setHeight(34);
19040 showPrevMonth : function(e){
19041 this.update(this.activeDate.add("mo", -1));
19043 showToday : function(e){
19044 this.update(new Date().clearTime());
19047 showNextMonth : function(e){
19048 this.update(this.activeDate.add("mo", 1));
19052 showPrevYear : function(){
19053 this.update(this.activeDate.add("y", -1));
19057 showNextYear : function(){
19058 this.update(this.activeDate.add("y", 1));
19063 update : function(date)
19065 var vd = this.activeDate;
19066 this.activeDate = date;
19067 // if(vd && this.el){
19068 // var t = date.getTime();
19069 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19070 // Roo.log('using add remove');
19072 // this.fireEvent('monthchange', this, date);
19074 // this.cells.removeClass("fc-state-highlight");
19075 // this.cells.each(function(c){
19076 // if(c.dateValue == t){
19077 // c.addClass("fc-state-highlight");
19078 // setTimeout(function(){
19079 // try{c.dom.firstChild.focus();}catch(e){}
19089 var days = date.getDaysInMonth();
19091 var firstOfMonth = date.getFirstDateOfMonth();
19092 var startingPos = firstOfMonth.getDay()-this.startDay;
19094 if(startingPos < this.startDay){
19098 var pm = date.add(Date.MONTH, -1);
19099 var prevStart = pm.getDaysInMonth()-startingPos;
19101 this.cells = this.el.select('.fc-day',true);
19102 this.textNodes = this.el.query('.fc-day-number');
19103 this.cells.addClassOnOver('fc-state-hover');
19105 var cells = this.cells.elements;
19106 var textEls = this.textNodes;
19108 Roo.each(cells, function(cell){
19109 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19112 days += startingPos;
19114 // convert everything to numbers so it's fast
19115 var day = 86400000;
19116 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19119 //Roo.log(prevStart);
19121 var today = new Date().clearTime().getTime();
19122 var sel = date.clearTime().getTime();
19123 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19124 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19125 var ddMatch = this.disabledDatesRE;
19126 var ddText = this.disabledDatesText;
19127 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19128 var ddaysText = this.disabledDaysText;
19129 var format = this.format;
19131 var setCellClass = function(cal, cell){
19135 //Roo.log('set Cell Class');
19137 var t = d.getTime();
19141 cell.dateValue = t;
19143 cell.className += " fc-today";
19144 cell.className += " fc-state-highlight";
19145 cell.title = cal.todayText;
19148 // disable highlight in other month..
19149 //cell.className += " fc-state-highlight";
19154 cell.className = " fc-state-disabled";
19155 cell.title = cal.minText;
19159 cell.className = " fc-state-disabled";
19160 cell.title = cal.maxText;
19164 if(ddays.indexOf(d.getDay()) != -1){
19165 cell.title = ddaysText;
19166 cell.className = " fc-state-disabled";
19169 if(ddMatch && format){
19170 var fvalue = d.dateFormat(format);
19171 if(ddMatch.test(fvalue)){
19172 cell.title = ddText.replace("%0", fvalue);
19173 cell.className = " fc-state-disabled";
19177 if (!cell.initialClassName) {
19178 cell.initialClassName = cell.dom.className;
19181 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19186 for(; i < startingPos; i++) {
19187 textEls[i].innerHTML = (++prevStart);
19188 d.setDate(d.getDate()+1);
19190 cells[i].className = "fc-past fc-other-month";
19191 setCellClass(this, cells[i]);
19196 for(; i < days; i++){
19197 intDay = i - startingPos + 1;
19198 textEls[i].innerHTML = (intDay);
19199 d.setDate(d.getDate()+1);
19201 cells[i].className = ''; // "x-date-active";
19202 setCellClass(this, cells[i]);
19206 for(; i < 42; i++) {
19207 textEls[i].innerHTML = (++extraDays);
19208 d.setDate(d.getDate()+1);
19210 cells[i].className = "fc-future fc-other-month";
19211 setCellClass(this, cells[i]);
19214 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19219 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221 if(totalRows != 6){
19222 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19223 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19226 this.fireEvent('monthchange', this, date);
19230 if(!this.internalRender){
19231 var main = this.el.dom.firstChild;
19232 var w = main.offsetWidth;
19233 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19234 Roo.fly(main).setWidth(w);
19235 this.internalRender = true;
19236 // opera does not respect the auto grow header center column
19237 // then, after it gets a width opera refuses to recalculate
19238 // without a second pass
19239 if(Roo.isOpera && !this.secondPass){
19240 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19241 this.secondPass = true;
19242 this.update.defer(10, this, [date]);
19249 findCell : function(dt) {
19250 dt = dt.clearTime().getTime();
19252 this.cells.each(function(c){
19253 //Roo.log("check " +c.dateValue + '?=' + dt);
19254 if(c.dateValue == dt){
19264 findCells : function(ev) {
19265 var s = ev.start.clone().clearTime().getTime();
19267 var e= ev.end.clone().clearTime().getTime();
19270 this.cells.each(function(c){
19271 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273 if(c.dateValue > e){
19276 if(c.dateValue < s){
19285 // findBestRow: function(cells)
19289 // for (var i =0 ; i < cells.length;i++) {
19290 // ret = Math.max(cells[i].rows || 0,ret);
19297 addItem : function(ev)
19299 // look for vertical location slot in
19300 var cells = this.findCells(ev);
19302 // ev.row = this.findBestRow(cells);
19304 // work out the location.
19308 for(var i =0; i < cells.length; i++) {
19310 cells[i].row = cells[0].row;
19313 cells[i].row = cells[i].row + 1;
19323 if (crow.start.getY() == cells[i].getY()) {
19325 crow.end = cells[i];
19342 cells[0].events.push(ev);
19344 this.calevents.push(ev);
19347 clearEvents: function() {
19349 if(!this.calevents){
19353 Roo.each(this.cells.elements, function(c){
19359 Roo.each(this.calevents, function(e) {
19360 Roo.each(e.els, function(el) {
19361 el.un('mouseenter' ,this.onEventEnter, this);
19362 el.un('mouseleave' ,this.onEventLeave, this);
19367 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19373 renderEvents: function()
19377 this.cells.each(function(c) {
19386 if(c.row != c.events.length){
19387 r = 4 - (4 - (c.row - c.events.length));
19390 c.events = ev.slice(0, r);
19391 c.more = ev.slice(r);
19393 if(c.more.length && c.more.length == 1){
19394 c.events.push(c.more.pop());
19397 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19401 this.cells.each(function(c) {
19403 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19406 for (var e = 0; e < c.events.length; e++){
19407 var ev = c.events[e];
19408 var rows = ev.rows;
19410 for(var i = 0; i < rows.length; i++) {
19412 // how many rows should it span..
19415 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19416 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418 unselectable : "on",
19421 cls: 'fc-event-inner',
19425 // cls: 'fc-event-time',
19426 // html : cells.length > 1 ? '' : ev.time
19430 cls: 'fc-event-title',
19431 html : String.format('{0}', ev.title)
19438 cls: 'ui-resizable-handle ui-resizable-e',
19439 html : '  '
19446 cfg.cls += ' fc-event-start';
19448 if ((i+1) == rows.length) {
19449 cfg.cls += ' fc-event-end';
19452 var ctr = _this.el.select('.fc-event-container',true).first();
19453 var cg = ctr.createChild(cfg);
19455 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19456 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458 var r = (c.more.length) ? 1 : 0;
19459 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19460 cg.setWidth(ebox.right - sbox.x -2);
19462 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19463 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19464 cg.on('click', _this.onEventClick, _this, ev);
19475 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19476 style : 'position: absolute',
19477 unselectable : "on",
19480 cls: 'fc-event-inner',
19484 cls: 'fc-event-title',
19492 cls: 'ui-resizable-handle ui-resizable-e',
19493 html : '  '
19499 var ctr = _this.el.select('.fc-event-container',true).first();
19500 var cg = ctr.createChild(cfg);
19502 var sbox = c.select('.fc-day-content',true).first().getBox();
19503 var ebox = c.select('.fc-day-content',true).first().getBox();
19505 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19506 cg.setWidth(ebox.right - sbox.x -2);
19508 cg.on('click', _this.onMoreEventClick, _this, c.more);
19518 onEventEnter: function (e, el,event,d) {
19519 this.fireEvent('evententer', this, el, event);
19522 onEventLeave: function (e, el,event,d) {
19523 this.fireEvent('eventleave', this, el, event);
19526 onEventClick: function (e, el,event,d) {
19527 this.fireEvent('eventclick', this, el, event);
19530 onMonthChange: function () {
19534 onMoreEventClick: function(e, el, more)
19538 this.calpopover.placement = 'right';
19539 this.calpopover.setTitle('More');
19541 this.calpopover.setContent('');
19543 var ctr = this.calpopover.el.select('.popover-content', true).first();
19545 Roo.each(more, function(m){
19547 cls : 'fc-event-hori fc-event-draggable',
19550 var cg = ctr.createChild(cfg);
19552 cg.on('click', _this.onEventClick, _this, m);
19555 this.calpopover.show(el);
19560 onLoad: function ()
19562 this.calevents = [];
19565 if(this.store.getCount() > 0){
19566 this.store.data.each(function(d){
19569 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19570 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19571 time : d.data.start_time,
19572 title : d.data.title,
19573 description : d.data.description,
19574 venue : d.data.venue
19579 this.renderEvents();
19581 if(this.calevents.length && this.loadMask){
19582 this.maskEl.hide();
19586 onBeforeLoad: function()
19588 this.clearEvents();
19590 this.maskEl.show();
19604 * @class Roo.bootstrap.Popover
19605 * @extends Roo.bootstrap.Component
19606 * Bootstrap Popover class
19607 * @cfg {String} html contents of the popover (or false to use children..)
19608 * @cfg {String} title of popover (or false to hide)
19609 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19610 * @cfg {String} trigger click || hover (or false to trigger manually)
19611 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19612 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19613 * - if false and it has a 'parent' then it will be automatically added to that element
19614 * - if string - Roo.get will be called
19615 * @cfg {Number} delay - delay before showing
19618 * Create a new Popover
19619 * @param {Object} config The config object
19622 Roo.bootstrap.Popover = function(config){
19623 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19629 * After the popover show
19631 * @param {Roo.bootstrap.Popover} this
19636 * After the popover hide
19638 * @param {Roo.bootstrap.Popover} this
19644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19649 placement : 'right',
19650 trigger : 'hover', // hover
19656 can_build_overlaid : false,
19658 maskEl : false, // the mask element
19663 getChildContainer : function()
19665 return this.contentEl;
19668 getPopoverHeader : function()
19670 this.title = true; // flag not to hide it..
19671 this.headerEl.addClass('p-0');
19672 return this.headerEl
19676 getAutoCreate : function(){
19679 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19680 style: 'display:block',
19686 cls : 'popover-inner ',
19690 cls: 'popover-title popover-header',
19691 html : this.title === false ? '' : this.title
19694 cls : 'popover-content popover-body ' + (this.cls || ''),
19695 html : this.html || ''
19706 * @param {string} the title
19708 setTitle: function(str)
19712 this.headerEl.dom.innerHTML = str;
19717 * @param {string} the body content
19719 setContent: function(str)
19722 if (this.contentEl) {
19723 this.contentEl.dom.innerHTML = str;
19727 // as it get's added to the bottom of the page.
19728 onRender : function(ct, position)
19730 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19735 var cfg = Roo.apply({}, this.getAutoCreate());
19739 cfg.cls += ' ' + this.cls;
19742 cfg.style = this.style;
19744 //Roo.log("adding to ");
19745 this.el = Roo.get(document.body).createChild(cfg, position);
19746 // Roo.log(this.el);
19749 this.contentEl = this.el.select('.popover-content',true).first();
19750 this.headerEl = this.el.select('.popover-title',true).first();
19753 if(typeof(this.items) != 'undefined'){
19754 var items = this.items;
19757 for(var i =0;i < items.length;i++) {
19758 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19762 this.items = nitems;
19764 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19765 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19772 resizeMask : function()
19774 this.maskEl.setSize(
19775 Roo.lib.Dom.getViewWidth(true),
19776 Roo.lib.Dom.getViewHeight(true)
19780 initEvents : function()
19784 Roo.bootstrap.Popover.register(this);
19788 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19789 this.el.enableDisplayMode('block');
19791 if (this.over === false && !this.parent()) {
19794 if (this.triggers === false) {
19799 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19800 var triggers = this.trigger ? this.trigger.split(' ') : [];
19801 Roo.each(triggers, function(trigger) {
19803 if (trigger == 'click') {
19804 on_el.on('click', this.toggle, this);
19805 } else if (trigger != 'manual') {
19806 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19807 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19809 on_el.on(eventIn ,this.enter, this);
19810 on_el.on(eventOut, this.leave, this);
19821 toggle : function () {
19822 this.hoverState == 'in' ? this.leave() : this.enter();
19825 enter : function () {
19827 clearTimeout(this.timeout);
19829 this.hoverState = 'in';
19831 if (!this.delay || !this.delay.show) {
19836 this.timeout = setTimeout(function () {
19837 if (_t.hoverState == 'in') {
19840 }, this.delay.show)
19843 leave : function() {
19844 clearTimeout(this.timeout);
19846 this.hoverState = 'out';
19848 if (!this.delay || !this.delay.hide) {
19853 this.timeout = setTimeout(function () {
19854 if (_t.hoverState == 'out') {
19857 }, this.delay.hide)
19861 * @param {Roo.Element|string|false} - element to align and point to.
19863 show : function (on_el)
19866 on_el = on_el || false; // default to false
19868 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19869 on_el = this.parent().el;
19870 } else if (this.over) {
19871 Roo.get(this.over);
19877 this.render(document.body);
19881 this.el.removeClass([
19882 'fade','top','bottom', 'left', 'right','in',
19883 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19886 if (this.title === false) {
19887 this.headerEl.hide();
19891 var placement = typeof this.placement == 'function' ?
19892 this.placement.call(this, this.el, on_el) :
19896 var autoToken = /\s?auto?\s?/i; /// not sure how this was supposed to work? right auto ? what?
19898 // I think 'auto right' - but
19900 var autoPlace = autoToken.test(placement);
19902 placement = placement.replace(autoToken, '') || 'top';
19908 this.el.dom.style.display='block';
19910 //this.el.appendTo(on_el);
19912 var p = this.getPosition();
19913 var box = this.el.getBox();
19916 var align = Roo.bootstrap.Popover.alignment[placement];
19917 this.el.addClass(align[2]);
19922 this.el.alignTo(on_el, align[0],align[1]);
19924 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19925 var es = this.el.getSize();
19926 var x = Roo.lib.Dom.getViewWidth()/2;
19927 var y = Roo.lib.Dom.getViewHeight()/2;
19928 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19933 //var arrow = this.el.select('.arrow',true).first();
19934 //arrow.set(align[2],
19936 this.el.addClass('in');
19939 if (this.el.hasClass('fade')) {
19943 this.hoverState = 'in';
19946 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19947 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19948 this.maskEl.dom.style.display = 'block';
19949 this.maskEl.addClass('show');
19951 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19955 this.fireEvent('show', this);
19960 this.el.setXY([0,0]);
19961 this.el.removeClass('in');
19963 this.hoverState = null;
19964 this.maskEl.hide(); // always..
19965 this.fireEvent('hide', this);
19971 Roo.apply(Roo.bootstrap.Popover, {
19974 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19975 'right' : ['l-br', [10,0], 'right bs-popover-right'],
19976 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19977 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19982 clickHander : false,
19985 onMouseDown : function(e)
19987 if (!e.getTarget(".roo-popover")) {
19995 register : function(popup)
19997 if (!Roo.bootstrap.Popover.clickHandler) {
19998 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20000 // hide other popups.
20002 this.popups.push(popup);
20004 hideAll : function()
20006 this.popups.forEach(function(p) {
20014 * Card header - holder for the card header elements.
20019 * @class Roo.bootstrap.PopoverNav
20020 * @extends Roo.bootstrap.NavGroup
20021 * Bootstrap Popover header navigation class
20023 * Create a new Popover Header Navigation
20024 * @param {Object} config The config object
20027 Roo.bootstrap.PopoverNav = function(config){
20028 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20031 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20034 container_method : 'getPopoverHeader'
20052 * @class Roo.bootstrap.Progress
20053 * @extends Roo.bootstrap.Component
20054 * Bootstrap Progress class
20055 * @cfg {Boolean} striped striped of the progress bar
20056 * @cfg {Boolean} active animated of the progress bar
20060 * Create a new Progress
20061 * @param {Object} config The config object
20064 Roo.bootstrap.Progress = function(config){
20065 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20068 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20073 getAutoCreate : function(){
20081 cfg.cls += ' progress-striped';
20085 cfg.cls += ' active';
20104 * @class Roo.bootstrap.ProgressBar
20105 * @extends Roo.bootstrap.Component
20106 * Bootstrap ProgressBar class
20107 * @cfg {Number} aria_valuenow aria-value now
20108 * @cfg {Number} aria_valuemin aria-value min
20109 * @cfg {Number} aria_valuemax aria-value max
20110 * @cfg {String} label label for the progress bar
20111 * @cfg {String} panel (success | info | warning | danger )
20112 * @cfg {String} role role of the progress bar
20113 * @cfg {String} sr_only text
20117 * Create a new ProgressBar
20118 * @param {Object} config The config object
20121 Roo.bootstrap.ProgressBar = function(config){
20122 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20125 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20129 aria_valuemax : 100,
20135 getAutoCreate : function()
20140 cls: 'progress-bar',
20141 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20153 cfg.role = this.role;
20156 if(this.aria_valuenow){
20157 cfg['aria-valuenow'] = this.aria_valuenow;
20160 if(this.aria_valuemin){
20161 cfg['aria-valuemin'] = this.aria_valuemin;
20164 if(this.aria_valuemax){
20165 cfg['aria-valuemax'] = this.aria_valuemax;
20168 if(this.label && !this.sr_only){
20169 cfg.html = this.label;
20173 cfg.cls += ' progress-bar-' + this.panel;
20179 update : function(aria_valuenow)
20181 this.aria_valuenow = aria_valuenow;
20183 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20198 * @class Roo.bootstrap.TabGroup
20199 * @extends Roo.bootstrap.Column
20200 * Bootstrap Column class
20201 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20202 * @cfg {Boolean} carousel true to make the group behave like a carousel
20203 * @cfg {Boolean} bullets show bullets for the panels
20204 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20205 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20206 * @cfg {Boolean} showarrow (true|false) show arrow default true
20209 * Create a new TabGroup
20210 * @param {Object} config The config object
20213 Roo.bootstrap.TabGroup = function(config){
20214 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20216 this.navId = Roo.id();
20219 Roo.bootstrap.TabGroup.register(this);
20223 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20226 transition : false,
20231 slideOnTouch : false,
20234 getAutoCreate : function()
20236 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20238 cfg.cls += ' tab-content';
20240 if (this.carousel) {
20241 cfg.cls += ' carousel slide';
20244 cls : 'carousel-inner',
20248 if(this.bullets && !Roo.isTouch){
20251 cls : 'carousel-bullets',
20255 if(this.bullets_cls){
20256 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20263 cfg.cn[0].cn.push(bullets);
20266 if(this.showarrow){
20267 cfg.cn[0].cn.push({
20269 class : 'carousel-arrow',
20273 class : 'carousel-prev',
20277 class : 'fa fa-chevron-left'
20283 class : 'carousel-next',
20287 class : 'fa fa-chevron-right'
20300 initEvents: function()
20302 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20303 // this.el.on("touchstart", this.onTouchStart, this);
20306 if(this.autoslide){
20309 this.slideFn = window.setInterval(function() {
20310 _this.showPanelNext();
20314 if(this.showarrow){
20315 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20316 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20322 // onTouchStart : function(e, el, o)
20324 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20328 // this.showPanelNext();
20332 getChildContainer : function()
20334 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20338 * register a Navigation item
20339 * @param {Roo.bootstrap.NavItem} the navitem to add
20341 register : function(item)
20343 this.tabs.push( item);
20344 item.navId = this.navId; // not really needed..
20349 getActivePanel : function()
20352 Roo.each(this.tabs, function(t) {
20362 getPanelByName : function(n)
20365 Roo.each(this.tabs, function(t) {
20366 if (t.tabId == n) {
20374 indexOfPanel : function(p)
20377 Roo.each(this.tabs, function(t,i) {
20378 if (t.tabId == p.tabId) {
20387 * show a specific panel
20388 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20389 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20391 showPanel : function (pan)
20393 if(this.transition || typeof(pan) == 'undefined'){
20394 Roo.log("waiting for the transitionend");
20398 if (typeof(pan) == 'number') {
20399 pan = this.tabs[pan];
20402 if (typeof(pan) == 'string') {
20403 pan = this.getPanelByName(pan);
20406 var cur = this.getActivePanel();
20409 Roo.log('pan or acitve pan is undefined');
20413 if (pan.tabId == this.getActivePanel().tabId) {
20417 if (false === cur.fireEvent('beforedeactivate')) {
20421 if(this.bullets > 0 && !Roo.isTouch){
20422 this.setActiveBullet(this.indexOfPanel(pan));
20425 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20427 //class="carousel-item carousel-item-next carousel-item-left"
20429 this.transition = true;
20430 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20431 var lr = dir == 'next' ? 'left' : 'right';
20432 pan.el.addClass(dir); // or prev
20433 pan.el.addClass('carousel-item-' + dir); // or prev
20434 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20435 cur.el.addClass(lr); // or right
20436 pan.el.addClass(lr);
20437 cur.el.addClass('carousel-item-' +lr); // or right
20438 pan.el.addClass('carousel-item-' +lr);
20442 cur.el.on('transitionend', function() {
20443 Roo.log("trans end?");
20445 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20446 pan.setActive(true);
20448 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20449 cur.setActive(false);
20451 _this.transition = false;
20453 }, this, { single: true } );
20458 cur.setActive(false);
20459 pan.setActive(true);
20464 showPanelNext : function()
20466 var i = this.indexOfPanel(this.getActivePanel());
20468 if (i >= this.tabs.length - 1 && !this.autoslide) {
20472 if (i >= this.tabs.length - 1 && this.autoslide) {
20476 this.showPanel(this.tabs[i+1]);
20479 showPanelPrev : function()
20481 var i = this.indexOfPanel(this.getActivePanel());
20483 if (i < 1 && !this.autoslide) {
20487 if (i < 1 && this.autoslide) {
20488 i = this.tabs.length;
20491 this.showPanel(this.tabs[i-1]);
20495 addBullet: function()
20497 if(!this.bullets || Roo.isTouch){
20500 var ctr = this.el.select('.carousel-bullets',true).first();
20501 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20502 var bullet = ctr.createChild({
20503 cls : 'bullet bullet-' + i
20504 },ctr.dom.lastChild);
20509 bullet.on('click', (function(e, el, o, ii, t){
20511 e.preventDefault();
20513 this.showPanel(ii);
20515 if(this.autoslide && this.slideFn){
20516 clearInterval(this.slideFn);
20517 this.slideFn = window.setInterval(function() {
20518 _this.showPanelNext();
20522 }).createDelegate(this, [i, bullet], true));
20527 setActiveBullet : function(i)
20533 Roo.each(this.el.select('.bullet', true).elements, function(el){
20534 el.removeClass('selected');
20537 var bullet = this.el.select('.bullet-' + i, true).first();
20543 bullet.addClass('selected');
20554 Roo.apply(Roo.bootstrap.TabGroup, {
20558 * register a Navigation Group
20559 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20561 register : function(navgrp)
20563 this.groups[navgrp.navId] = navgrp;
20567 * fetch a Navigation Group based on the navigation ID
20568 * if one does not exist , it will get created.
20569 * @param {string} the navgroup to add
20570 * @returns {Roo.bootstrap.NavGroup} the navgroup
20572 get: function(navId) {
20573 if (typeof(this.groups[navId]) == 'undefined') {
20574 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20576 return this.groups[navId] ;
20591 * @class Roo.bootstrap.TabPanel
20592 * @extends Roo.bootstrap.Component
20593 * Bootstrap TabPanel class
20594 * @cfg {Boolean} active panel active
20595 * @cfg {String} html panel content
20596 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20597 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20598 * @cfg {String} href click to link..
20599 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20603 * Create a new TabPanel
20604 * @param {Object} config The config object
20607 Roo.bootstrap.TabPanel = function(config){
20608 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20612 * Fires when the active status changes
20613 * @param {Roo.bootstrap.TabPanel} this
20614 * @param {Boolean} state the new state
20619 * @event beforedeactivate
20620 * Fires before a tab is de-activated - can be used to do validation on a form.
20621 * @param {Roo.bootstrap.TabPanel} this
20622 * @return {Boolean} false if there is an error
20625 'beforedeactivate': true
20628 this.tabId = this.tabId || Roo.id();
20632 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20639 touchSlide : false,
20640 getAutoCreate : function(){
20645 // item is needed for carousel - not sure if it has any effect otherwise
20646 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20647 html: this.html || ''
20651 cfg.cls += ' active';
20655 cfg.tabId = this.tabId;
20663 initEvents: function()
20665 var p = this.parent();
20667 this.navId = this.navId || p.navId;
20669 if (typeof(this.navId) != 'undefined') {
20670 // not really needed.. but just in case.. parent should be a NavGroup.
20671 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20675 var i = tg.tabs.length - 1;
20677 if(this.active && tg.bullets > 0 && i < tg.bullets){
20678 tg.setActiveBullet(i);
20682 this.el.on('click', this.onClick, this);
20684 if(Roo.isTouch && this.touchSlide){
20685 this.el.on("touchstart", this.onTouchStart, this);
20686 this.el.on("touchmove", this.onTouchMove, this);
20687 this.el.on("touchend", this.onTouchEnd, this);
20692 onRender : function(ct, position)
20694 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20697 setActive : function(state)
20699 Roo.log("panel - set active " + this.tabId + "=" + state);
20701 this.active = state;
20703 this.el.removeClass('active');
20705 } else if (!this.el.hasClass('active')) {
20706 this.el.addClass('active');
20709 this.fireEvent('changed', this, state);
20712 onClick : function(e)
20714 e.preventDefault();
20716 if(!this.href.length){
20720 window.location.href = this.href;
20729 onTouchStart : function(e)
20731 this.swiping = false;
20733 this.startX = e.browserEvent.touches[0].clientX;
20734 this.startY = e.browserEvent.touches[0].clientY;
20737 onTouchMove : function(e)
20739 this.swiping = true;
20741 this.endX = e.browserEvent.touches[0].clientX;
20742 this.endY = e.browserEvent.touches[0].clientY;
20745 onTouchEnd : function(e)
20752 var tabGroup = this.parent();
20754 if(this.endX > this.startX){ // swiping right
20755 tabGroup.showPanelPrev();
20759 if(this.startX > this.endX){ // swiping left
20760 tabGroup.showPanelNext();
20779 * @class Roo.bootstrap.DateField
20780 * @extends Roo.bootstrap.Input
20781 * Bootstrap DateField class
20782 * @cfg {Number} weekStart default 0
20783 * @cfg {String} viewMode default empty, (months|years)
20784 * @cfg {String} minViewMode default empty, (months|years)
20785 * @cfg {Number} startDate default -Infinity
20786 * @cfg {Number} endDate default Infinity
20787 * @cfg {Boolean} todayHighlight default false
20788 * @cfg {Boolean} todayBtn default false
20789 * @cfg {Boolean} calendarWeeks default false
20790 * @cfg {Object} daysOfWeekDisabled default empty
20791 * @cfg {Boolean} singleMode default false (true | false)
20793 * @cfg {Boolean} keyboardNavigation default true
20794 * @cfg {String} language default en
20797 * Create a new DateField
20798 * @param {Object} config The config object
20801 Roo.bootstrap.DateField = function(config){
20802 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20806 * Fires when this field show.
20807 * @param {Roo.bootstrap.DateField} this
20808 * @param {Mixed} date The date value
20813 * Fires when this field hide.
20814 * @param {Roo.bootstrap.DateField} this
20815 * @param {Mixed} date The date value
20820 * Fires when select a date.
20821 * @param {Roo.bootstrap.DateField} this
20822 * @param {Mixed} date The date value
20826 * @event beforeselect
20827 * Fires when before select a date.
20828 * @param {Roo.bootstrap.DateField} this
20829 * @param {Mixed} date The date value
20831 beforeselect : true
20835 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20838 * @cfg {String} format
20839 * The default date format string which can be overriden for localization support. The format must be
20840 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20844 * @cfg {String} altFormats
20845 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20846 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20848 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20856 todayHighlight : false,
20862 keyboardNavigation: true,
20864 calendarWeeks: false,
20866 startDate: -Infinity,
20870 daysOfWeekDisabled: [],
20874 singleMode : false,
20876 UTCDate: function()
20878 return new Date(Date.UTC.apply(Date, arguments));
20881 UTCToday: function()
20883 var today = new Date();
20884 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20887 getDate: function() {
20888 var d = this.getUTCDate();
20889 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20892 getUTCDate: function() {
20896 setDate: function(d) {
20897 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20900 setUTCDate: function(d) {
20902 this.setValue(this.formatDate(this.date));
20905 onRender: function(ct, position)
20908 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20910 this.language = this.language || 'en';
20911 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20912 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20914 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20915 this.format = this.format || 'm/d/y';
20916 this.isInline = false;
20917 this.isInput = true;
20918 this.component = this.el.select('.add-on', true).first() || false;
20919 this.component = (this.component && this.component.length === 0) ? false : this.component;
20920 this.hasInput = this.component && this.inputEl().length;
20922 if (typeof(this.minViewMode === 'string')) {
20923 switch (this.minViewMode) {
20925 this.minViewMode = 1;
20928 this.minViewMode = 2;
20931 this.minViewMode = 0;
20936 if (typeof(this.viewMode === 'string')) {
20937 switch (this.viewMode) {
20950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20952 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20954 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20956 this.picker().on('mousedown', this.onMousedown, this);
20957 this.picker().on('click', this.onClick, this);
20959 this.picker().addClass('datepicker-dropdown');
20961 this.startViewMode = this.viewMode;
20963 if(this.singleMode){
20964 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20965 v.setVisibilityMode(Roo.Element.DISPLAY);
20969 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20970 v.setStyle('width', '189px');
20974 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20975 if(!this.calendarWeeks){
20980 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20981 v.attr('colspan', function(i, val){
20982 return parseInt(val) + 1;
20987 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20989 this.setStartDate(this.startDate);
20990 this.setEndDate(this.endDate);
20992 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20999 if(this.isInline) {
21004 picker : function()
21006 return this.pickerEl;
21007 // return this.el.select('.datepicker', true).first();
21010 fillDow: function()
21012 var dowCnt = this.weekStart;
21021 if(this.calendarWeeks){
21029 while (dowCnt < this.weekStart + 7) {
21033 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21037 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21040 fillMonths: function()
21043 var months = this.picker().select('>.datepicker-months td', true).first();
21045 months.dom.innerHTML = '';
21051 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21054 months.createChild(month);
21061 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;
21063 if (this.date < this.startDate) {
21064 this.viewDate = new Date(this.startDate);
21065 } else if (this.date > this.endDate) {
21066 this.viewDate = new Date(this.endDate);
21068 this.viewDate = new Date(this.date);
21076 var d = new Date(this.viewDate),
21077 year = d.getUTCFullYear(),
21078 month = d.getUTCMonth(),
21079 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21080 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21081 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21082 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21083 currentDate = this.date && this.date.valueOf(),
21084 today = this.UTCToday();
21086 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21088 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21090 // this.picker.select('>tfoot th.today').
21091 // .text(dates[this.language].today)
21092 // .toggle(this.todayBtn !== false);
21094 this.updateNavArrows();
21097 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21099 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21101 prevMonth.setUTCDate(day);
21103 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21105 var nextMonth = new Date(prevMonth);
21107 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21109 nextMonth = nextMonth.valueOf();
21111 var fillMonths = false;
21113 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21115 while(prevMonth.valueOf() <= nextMonth) {
21118 if (prevMonth.getUTCDay() === this.weekStart) {
21120 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21128 if(this.calendarWeeks){
21129 // ISO 8601: First week contains first thursday.
21130 // ISO also states week starts on Monday, but we can be more abstract here.
21132 // Start of current week: based on weekstart/current date
21133 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21134 // Thursday of this week
21135 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21136 // First Thursday of year, year from thursday
21137 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21138 // Calendar week: ms between thursdays, div ms per day, div 7 days
21139 calWeek = (th - yth) / 864e5 / 7 + 1;
21141 fillMonths.cn.push({
21149 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21151 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21154 if (this.todayHighlight &&
21155 prevMonth.getUTCFullYear() == today.getFullYear() &&
21156 prevMonth.getUTCMonth() == today.getMonth() &&
21157 prevMonth.getUTCDate() == today.getDate()) {
21158 clsName += ' today';
21161 if (currentDate && prevMonth.valueOf() === currentDate) {
21162 clsName += ' active';
21165 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21166 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21167 clsName += ' disabled';
21170 fillMonths.cn.push({
21172 cls: 'day ' + clsName,
21173 html: prevMonth.getDate()
21176 prevMonth.setDate(prevMonth.getDate()+1);
21179 var currentYear = this.date && this.date.getUTCFullYear();
21180 var currentMonth = this.date && this.date.getUTCMonth();
21182 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21184 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21185 v.removeClass('active');
21187 if(currentYear === year && k === currentMonth){
21188 v.addClass('active');
21191 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21192 v.addClass('disabled');
21198 year = parseInt(year/10, 10) * 10;
21200 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21202 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21205 for (var i = -1; i < 11; i++) {
21206 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21208 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21216 showMode: function(dir)
21219 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21222 Roo.each(this.picker().select('>div',true).elements, function(v){
21223 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21226 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21231 if(this.isInline) {
21235 this.picker().removeClass(['bottom', 'top']);
21237 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21239 * place to the top of element!
21243 this.picker().addClass('top');
21244 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21249 this.picker().addClass('bottom');
21251 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21254 parseDate : function(value)
21256 if(!value || value instanceof Date){
21259 var v = Date.parseDate(value, this.format);
21260 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21261 v = Date.parseDate(value, 'Y-m-d');
21263 if(!v && this.altFormats){
21264 if(!this.altFormatsArray){
21265 this.altFormatsArray = this.altFormats.split("|");
21267 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21268 v = Date.parseDate(value, this.altFormatsArray[i]);
21274 formatDate : function(date, fmt)
21276 return (!date || !(date instanceof Date)) ?
21277 date : date.dateFormat(fmt || this.format);
21280 onFocus : function()
21282 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21286 onBlur : function()
21288 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21290 var d = this.inputEl().getValue();
21297 showPopup : function()
21299 this.picker().show();
21303 this.fireEvent('showpopup', this, this.date);
21306 hidePopup : function()
21308 if(this.isInline) {
21311 this.picker().hide();
21312 this.viewMode = this.startViewMode;
21315 this.fireEvent('hidepopup', this, this.date);
21319 onMousedown: function(e)
21321 e.stopPropagation();
21322 e.preventDefault();
21327 Roo.bootstrap.DateField.superclass.keyup.call(this);
21331 setValue: function(v)
21333 if(this.fireEvent('beforeselect', this, v) !== false){
21334 var d = new Date(this.parseDate(v) ).clearTime();
21336 if(isNaN(d.getTime())){
21337 this.date = this.viewDate = '';
21338 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21342 v = this.formatDate(d);
21344 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21346 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21350 this.fireEvent('select', this, this.date);
21354 getValue: function()
21356 return this.formatDate(this.date);
21359 fireKey: function(e)
21361 if (!this.picker().isVisible()){
21362 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21368 var dateChanged = false,
21370 newDate, newViewDate;
21375 e.preventDefault();
21379 if (!this.keyboardNavigation) {
21382 dir = e.keyCode == 37 ? -1 : 1;
21385 newDate = this.moveYear(this.date, dir);
21386 newViewDate = this.moveYear(this.viewDate, dir);
21387 } else if (e.shiftKey){
21388 newDate = this.moveMonth(this.date, dir);
21389 newViewDate = this.moveMonth(this.viewDate, dir);
21391 newDate = new Date(this.date);
21392 newDate.setUTCDate(this.date.getUTCDate() + dir);
21393 newViewDate = new Date(this.viewDate);
21394 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21396 if (this.dateWithinRange(newDate)){
21397 this.date = newDate;
21398 this.viewDate = newViewDate;
21399 this.setValue(this.formatDate(this.date));
21401 e.preventDefault();
21402 dateChanged = true;
21407 if (!this.keyboardNavigation) {
21410 dir = e.keyCode == 38 ? -1 : 1;
21412 newDate = this.moveYear(this.date, dir);
21413 newViewDate = this.moveYear(this.viewDate, dir);
21414 } else if (e.shiftKey){
21415 newDate = this.moveMonth(this.date, dir);
21416 newViewDate = this.moveMonth(this.viewDate, dir);
21418 newDate = new Date(this.date);
21419 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21420 newViewDate = new Date(this.viewDate);
21421 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21423 if (this.dateWithinRange(newDate)){
21424 this.date = newDate;
21425 this.viewDate = newViewDate;
21426 this.setValue(this.formatDate(this.date));
21428 e.preventDefault();
21429 dateChanged = true;
21433 this.setValue(this.formatDate(this.date));
21435 e.preventDefault();
21438 this.setValue(this.formatDate(this.date));
21452 onClick: function(e)
21454 e.stopPropagation();
21455 e.preventDefault();
21457 var target = e.getTarget();
21459 if(target.nodeName.toLowerCase() === 'i'){
21460 target = Roo.get(target).dom.parentNode;
21463 var nodeName = target.nodeName;
21464 var className = target.className;
21465 var html = target.innerHTML;
21466 //Roo.log(nodeName);
21468 switch(nodeName.toLowerCase()) {
21470 switch(className) {
21476 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21477 switch(this.viewMode){
21479 this.viewDate = this.moveMonth(this.viewDate, dir);
21483 this.viewDate = this.moveYear(this.viewDate, dir);
21489 var date = new Date();
21490 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21492 this.setValue(this.formatDate(this.date));
21499 if (className.indexOf('disabled') < 0) {
21500 this.viewDate.setUTCDate(1);
21501 if (className.indexOf('month') > -1) {
21502 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21504 var year = parseInt(html, 10) || 0;
21505 this.viewDate.setUTCFullYear(year);
21509 if(this.singleMode){
21510 this.setValue(this.formatDate(this.viewDate));
21521 //Roo.log(className);
21522 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21523 var day = parseInt(html, 10) || 1;
21524 var year = this.viewDate.getUTCFullYear(),
21525 month = this.viewDate.getUTCMonth();
21527 if (className.indexOf('old') > -1) {
21534 } else if (className.indexOf('new') > -1) {
21542 //Roo.log([year,month,day]);
21543 this.date = this.UTCDate(year, month, day,0,0,0,0);
21544 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21546 //Roo.log(this.formatDate(this.date));
21547 this.setValue(this.formatDate(this.date));
21554 setStartDate: function(startDate)
21556 this.startDate = startDate || -Infinity;
21557 if (this.startDate !== -Infinity) {
21558 this.startDate = this.parseDate(this.startDate);
21561 this.updateNavArrows();
21564 setEndDate: function(endDate)
21566 this.endDate = endDate || Infinity;
21567 if (this.endDate !== Infinity) {
21568 this.endDate = this.parseDate(this.endDate);
21571 this.updateNavArrows();
21574 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21576 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21577 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21578 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21580 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21581 return parseInt(d, 10);
21584 this.updateNavArrows();
21587 updateNavArrows: function()
21589 if(this.singleMode){
21593 var d = new Date(this.viewDate),
21594 year = d.getUTCFullYear(),
21595 month = d.getUTCMonth();
21597 Roo.each(this.picker().select('.prev', true).elements, function(v){
21599 switch (this.viewMode) {
21602 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21608 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21615 Roo.each(this.picker().select('.next', true).elements, function(v){
21617 switch (this.viewMode) {
21620 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21626 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21634 moveMonth: function(date, dir)
21639 var new_date = new Date(date.valueOf()),
21640 day = new_date.getUTCDate(),
21641 month = new_date.getUTCMonth(),
21642 mag = Math.abs(dir),
21644 dir = dir > 0 ? 1 : -1;
21647 // If going back one month, make sure month is not current month
21648 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21650 return new_date.getUTCMonth() == month;
21652 // If going forward one month, make sure month is as expected
21653 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21655 return new_date.getUTCMonth() != new_month;
21657 new_month = month + dir;
21658 new_date.setUTCMonth(new_month);
21659 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21660 if (new_month < 0 || new_month > 11) {
21661 new_month = (new_month + 12) % 12;
21664 // For magnitudes >1, move one month at a time...
21665 for (var i=0; i<mag; i++) {
21666 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21667 new_date = this.moveMonth(new_date, dir);
21669 // ...then reset the day, keeping it in the new month
21670 new_month = new_date.getUTCMonth();
21671 new_date.setUTCDate(day);
21673 return new_month != new_date.getUTCMonth();
21676 // Common date-resetting loop -- if date is beyond end of month, make it
21679 new_date.setUTCDate(--day);
21680 new_date.setUTCMonth(new_month);
21685 moveYear: function(date, dir)
21687 return this.moveMonth(date, dir*12);
21690 dateWithinRange: function(date)
21692 return date >= this.startDate && date <= this.endDate;
21698 this.picker().remove();
21701 validateValue : function(value)
21703 if(this.getVisibilityEl().hasClass('hidden')){
21707 if(value.length < 1) {
21708 if(this.allowBlank){
21714 if(value.length < this.minLength){
21717 if(value.length > this.maxLength){
21721 var vt = Roo.form.VTypes;
21722 if(!vt[this.vtype](value, this)){
21726 if(typeof this.validator == "function"){
21727 var msg = this.validator(value);
21733 if(this.regex && !this.regex.test(value)){
21737 if(typeof(this.parseDate(value)) == 'undefined'){
21741 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21745 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21755 this.date = this.viewDate = '';
21757 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21762 Roo.apply(Roo.bootstrap.DateField, {
21773 html: '<i class="fa fa-arrow-left"/>'
21783 html: '<i class="fa fa-arrow-right"/>'
21825 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21826 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21827 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21828 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21829 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21842 navFnc: 'FullYear',
21847 navFnc: 'FullYear',
21852 Roo.apply(Roo.bootstrap.DateField, {
21856 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21860 cls: 'datepicker-days',
21864 cls: 'table-condensed',
21866 Roo.bootstrap.DateField.head,
21870 Roo.bootstrap.DateField.footer
21877 cls: 'datepicker-months',
21881 cls: 'table-condensed',
21883 Roo.bootstrap.DateField.head,
21884 Roo.bootstrap.DateField.content,
21885 Roo.bootstrap.DateField.footer
21892 cls: 'datepicker-years',
21896 cls: 'table-condensed',
21898 Roo.bootstrap.DateField.head,
21899 Roo.bootstrap.DateField.content,
21900 Roo.bootstrap.DateField.footer
21919 * @class Roo.bootstrap.TimeField
21920 * @extends Roo.bootstrap.Input
21921 * Bootstrap DateField class
21925 * Create a new TimeField
21926 * @param {Object} config The config object
21929 Roo.bootstrap.TimeField = function(config){
21930 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21934 * Fires when this field show.
21935 * @param {Roo.bootstrap.DateField} thisthis
21936 * @param {Mixed} date The date value
21941 * Fires when this field hide.
21942 * @param {Roo.bootstrap.DateField} this
21943 * @param {Mixed} date The date value
21948 * Fires when select a date.
21949 * @param {Roo.bootstrap.DateField} this
21950 * @param {Mixed} date The date value
21956 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21959 * @cfg {String} format
21960 * The default time format string which can be overriden for localization support. The format must be
21961 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21965 onRender: function(ct, position)
21968 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21970 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21972 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21974 this.pop = this.picker().select('>.datepicker-time',true).first();
21975 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21977 this.picker().on('mousedown', this.onMousedown, this);
21978 this.picker().on('click', this.onClick, this);
21980 this.picker().addClass('datepicker-dropdown');
21985 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21986 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21987 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21988 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21989 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21990 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21994 fireKey: function(e){
21995 if (!this.picker().isVisible()){
21996 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22002 e.preventDefault();
22010 this.onTogglePeriod();
22013 this.onIncrementMinutes();
22016 this.onDecrementMinutes();
22025 onClick: function(e) {
22026 e.stopPropagation();
22027 e.preventDefault();
22030 picker : function()
22032 return this.el.select('.datepicker', true).first();
22035 fillTime: function()
22037 var time = this.pop.select('tbody', true).first();
22039 time.dom.innerHTML = '';
22054 cls: 'hours-up glyphicon glyphicon-chevron-up'
22074 cls: 'minutes-up glyphicon glyphicon-chevron-up'
22095 cls: 'timepicker-hour',
22110 cls: 'timepicker-minute',
22125 cls: 'btn btn-primary period',
22147 cls: 'hours-down glyphicon glyphicon-chevron-down'
22167 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22185 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22192 var hours = this.time.getHours();
22193 var minutes = this.time.getMinutes();
22206 hours = hours - 12;
22210 hours = '0' + hours;
22214 minutes = '0' + minutes;
22217 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22218 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22219 this.pop.select('button', true).first().dom.innerHTML = period;
22225 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22227 var cls = ['bottom'];
22229 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22236 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22241 this.picker().addClass(cls.join('-'));
22245 Roo.each(cls, function(c){
22247 _this.picker().setTop(_this.inputEl().getHeight());
22251 _this.picker().setTop(0 - _this.picker().getHeight());
22256 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22260 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22267 onFocus : function()
22269 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22273 onBlur : function()
22275 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22281 this.picker().show();
22286 this.fireEvent('show', this, this.date);
22291 this.picker().hide();
22294 this.fireEvent('hide', this, this.date);
22297 setTime : function()
22300 this.setValue(this.time.format(this.format));
22302 this.fireEvent('select', this, this.date);
22307 onMousedown: function(e){
22308 e.stopPropagation();
22309 e.preventDefault();
22312 onIncrementHours: function()
22314 Roo.log('onIncrementHours');
22315 this.time = this.time.add(Date.HOUR, 1);
22320 onDecrementHours: function()
22322 Roo.log('onDecrementHours');
22323 this.time = this.time.add(Date.HOUR, -1);
22327 onIncrementMinutes: function()
22329 Roo.log('onIncrementMinutes');
22330 this.time = this.time.add(Date.MINUTE, 1);
22334 onDecrementMinutes: function()
22336 Roo.log('onDecrementMinutes');
22337 this.time = this.time.add(Date.MINUTE, -1);
22341 onTogglePeriod: function()
22343 Roo.log('onTogglePeriod');
22344 this.time = this.time.add(Date.HOUR, 12);
22351 Roo.apply(Roo.bootstrap.TimeField, {
22381 cls: 'btn btn-info ok',
22393 Roo.apply(Roo.bootstrap.TimeField, {
22397 cls: 'datepicker dropdown-menu',
22401 cls: 'datepicker-time',
22405 cls: 'table-condensed',
22407 Roo.bootstrap.TimeField.content,
22408 Roo.bootstrap.TimeField.footer
22427 * @class Roo.bootstrap.MonthField
22428 * @extends Roo.bootstrap.Input
22429 * Bootstrap MonthField class
22431 * @cfg {String} language default en
22434 * Create a new MonthField
22435 * @param {Object} config The config object
22438 Roo.bootstrap.MonthField = function(config){
22439 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22444 * Fires when this field show.
22445 * @param {Roo.bootstrap.MonthField} this
22446 * @param {Mixed} date The date value
22451 * Fires when this field hide.
22452 * @param {Roo.bootstrap.MonthField} this
22453 * @param {Mixed} date The date value
22458 * Fires when select a date.
22459 * @param {Roo.bootstrap.MonthField} this
22460 * @param {String} oldvalue The old value
22461 * @param {String} newvalue The new value
22467 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22469 onRender: function(ct, position)
22472 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22474 this.language = this.language || 'en';
22475 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22476 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22478 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22479 this.isInline = false;
22480 this.isInput = true;
22481 this.component = this.el.select('.add-on', true).first() || false;
22482 this.component = (this.component && this.component.length === 0) ? false : this.component;
22483 this.hasInput = this.component && this.inputEL().length;
22485 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22487 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22489 this.picker().on('mousedown', this.onMousedown, this);
22490 this.picker().on('click', this.onClick, this);
22492 this.picker().addClass('datepicker-dropdown');
22494 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22495 v.setStyle('width', '189px');
22502 if(this.isInline) {
22508 setValue: function(v, suppressEvent)
22510 var o = this.getValue();
22512 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22516 if(suppressEvent !== true){
22517 this.fireEvent('select', this, o, v);
22522 getValue: function()
22527 onClick: function(e)
22529 e.stopPropagation();
22530 e.preventDefault();
22532 var target = e.getTarget();
22534 if(target.nodeName.toLowerCase() === 'i'){
22535 target = Roo.get(target).dom.parentNode;
22538 var nodeName = target.nodeName;
22539 var className = target.className;
22540 var html = target.innerHTML;
22542 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22546 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22548 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22554 picker : function()
22556 return this.pickerEl;
22559 fillMonths: function()
22562 var months = this.picker().select('>.datepicker-months td', true).first();
22564 months.dom.innerHTML = '';
22570 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22573 months.createChild(month);
22582 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22583 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22586 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22587 e.removeClass('active');
22589 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22590 e.addClass('active');
22597 if(this.isInline) {
22601 this.picker().removeClass(['bottom', 'top']);
22603 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22605 * place to the top of element!
22609 this.picker().addClass('top');
22610 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22615 this.picker().addClass('bottom');
22617 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22620 onFocus : function()
22622 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22626 onBlur : function()
22628 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22630 var d = this.inputEl().getValue();
22639 this.picker().show();
22640 this.picker().select('>.datepicker-months', true).first().show();
22644 this.fireEvent('show', this, this.date);
22649 if(this.isInline) {
22652 this.picker().hide();
22653 this.fireEvent('hide', this, this.date);
22657 onMousedown: function(e)
22659 e.stopPropagation();
22660 e.preventDefault();
22665 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22669 fireKey: function(e)
22671 if (!this.picker().isVisible()){
22672 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22683 e.preventDefault();
22687 dir = e.keyCode == 37 ? -1 : 1;
22689 this.vIndex = this.vIndex + dir;
22691 if(this.vIndex < 0){
22695 if(this.vIndex > 11){
22699 if(isNaN(this.vIndex)){
22703 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22709 dir = e.keyCode == 38 ? -1 : 1;
22711 this.vIndex = this.vIndex + dir * 4;
22713 if(this.vIndex < 0){
22717 if(this.vIndex > 11){
22721 if(isNaN(this.vIndex)){
22725 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22730 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22731 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22735 e.preventDefault();
22738 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22739 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22755 this.picker().remove();
22760 Roo.apply(Roo.bootstrap.MonthField, {
22779 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22780 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22785 Roo.apply(Roo.bootstrap.MonthField, {
22789 cls: 'datepicker dropdown-menu roo-dynamic',
22793 cls: 'datepicker-months',
22797 cls: 'table-condensed',
22799 Roo.bootstrap.DateField.content
22819 * @class Roo.bootstrap.CheckBox
22820 * @extends Roo.bootstrap.Input
22821 * Bootstrap CheckBox class
22823 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22824 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22825 * @cfg {String} boxLabel The text that appears beside the checkbox
22826 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22827 * @cfg {Boolean} checked initnal the element
22828 * @cfg {Boolean} inline inline the element (default false)
22829 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22830 * @cfg {String} tooltip label tooltip
22833 * Create a new CheckBox
22834 * @param {Object} config The config object
22837 Roo.bootstrap.CheckBox = function(config){
22838 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22843 * Fires when the element is checked or unchecked.
22844 * @param {Roo.bootstrap.CheckBox} this This input
22845 * @param {Boolean} checked The new checked value
22850 * Fires when the element is click.
22851 * @param {Roo.bootstrap.CheckBox} this This input
22858 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22860 inputType: 'checkbox',
22869 // checkbox success does not make any sense really..
22874 getAutoCreate : function()
22876 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22882 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22885 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22891 type : this.inputType,
22892 value : this.inputValue,
22893 cls : 'roo-' + this.inputType, //'form-box',
22894 placeholder : this.placeholder || ''
22898 if(this.inputType != 'radio'){
22902 cls : 'roo-hidden-value',
22903 value : this.checked ? this.inputValue : this.valueOff
22908 if (this.weight) { // Validity check?
22909 cfg.cls += " " + this.inputType + "-" + this.weight;
22912 if (this.disabled) {
22913 input.disabled=true;
22917 input.checked = this.checked;
22922 input.name = this.name;
22924 if(this.inputType != 'radio'){
22925 hidden.name = this.name;
22926 input.name = '_hidden_' + this.name;
22931 input.cls += ' input-' + this.size;
22936 ['xs','sm','md','lg'].map(function(size){
22937 if (settings[size]) {
22938 cfg.cls += ' col-' + size + '-' + settings[size];
22942 var inputblock = input;
22944 if (this.before || this.after) {
22947 cls : 'input-group',
22952 inputblock.cn.push({
22954 cls : 'input-group-addon',
22959 inputblock.cn.push(input);
22961 if(this.inputType != 'radio'){
22962 inputblock.cn.push(hidden);
22966 inputblock.cn.push({
22968 cls : 'input-group-addon',
22974 var boxLabelCfg = false;
22980 //'for': id, // box label is handled by onclick - so no for...
22982 html: this.boxLabel
22985 boxLabelCfg.tooltip = this.tooltip;
22991 if (align ==='left' && this.fieldLabel.length) {
22992 // Roo.log("left and has label");
22997 cls : 'control-label',
22998 html : this.fieldLabel
23009 cfg.cn[1].cn.push(boxLabelCfg);
23012 if(this.labelWidth > 12){
23013 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23016 if(this.labelWidth < 13 && this.labelmd == 0){
23017 this.labelmd = this.labelWidth;
23020 if(this.labellg > 0){
23021 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23022 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23025 if(this.labelmd > 0){
23026 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23027 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23030 if(this.labelsm > 0){
23031 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23032 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23035 if(this.labelxs > 0){
23036 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23037 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23040 } else if ( this.fieldLabel.length) {
23041 // Roo.log(" label");
23045 tag: this.boxLabel ? 'span' : 'label',
23047 cls: 'control-label box-input-label',
23048 //cls : 'input-group-addon',
23049 html : this.fieldLabel
23056 cfg.cn.push(boxLabelCfg);
23061 // Roo.log(" no label && no align");
23062 cfg.cn = [ inputblock ] ;
23064 cfg.cn.push(boxLabelCfg);
23072 if(this.inputType != 'radio'){
23073 cfg.cn.push(hidden);
23081 * return the real input element.
23083 inputEl: function ()
23085 return this.el.select('input.roo-' + this.inputType,true).first();
23087 hiddenEl: function ()
23089 return this.el.select('input.roo-hidden-value',true).first();
23092 labelEl: function()
23094 return this.el.select('label.control-label',true).first();
23096 /* depricated... */
23100 return this.labelEl();
23103 boxLabelEl: function()
23105 return this.el.select('label.box-label',true).first();
23108 initEvents : function()
23110 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23112 this.inputEl().on('click', this.onClick, this);
23114 if (this.boxLabel) {
23115 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23118 this.startValue = this.getValue();
23121 Roo.bootstrap.CheckBox.register(this);
23125 onClick : function(e)
23127 if(this.fireEvent('click', this, e) !== false){
23128 this.setChecked(!this.checked);
23133 setChecked : function(state,suppressEvent)
23135 this.startValue = this.getValue();
23137 if(this.inputType == 'radio'){
23139 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140 e.dom.checked = false;
23143 this.inputEl().dom.checked = true;
23145 this.inputEl().dom.value = this.inputValue;
23147 if(suppressEvent !== true){
23148 this.fireEvent('check', this, true);
23156 this.checked = state;
23158 this.inputEl().dom.checked = state;
23161 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23163 if(suppressEvent !== true){
23164 this.fireEvent('check', this, state);
23170 getValue : function()
23172 if(this.inputType == 'radio'){
23173 return this.getGroupValue();
23176 return this.hiddenEl().dom.value;
23180 getGroupValue : function()
23182 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23186 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23189 setValue : function(v,suppressEvent)
23191 if(this.inputType == 'radio'){
23192 this.setGroupValue(v, suppressEvent);
23196 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23201 setGroupValue : function(v, suppressEvent)
23203 this.startValue = this.getValue();
23205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23206 e.dom.checked = false;
23208 if(e.dom.value == v){
23209 e.dom.checked = true;
23213 if(suppressEvent !== true){
23214 this.fireEvent('check', this, true);
23222 validate : function()
23224 if(this.getVisibilityEl().hasClass('hidden')){
23230 (this.inputType == 'radio' && this.validateRadio()) ||
23231 (this.inputType == 'checkbox' && this.validateCheckbox())
23237 this.markInvalid();
23241 validateRadio : function()
23243 if(this.getVisibilityEl().hasClass('hidden')){
23247 if(this.allowBlank){
23253 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23254 if(!e.dom.checked){
23266 validateCheckbox : function()
23269 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23270 //return (this.getValue() == this.inputValue) ? true : false;
23273 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23281 for(var i in group){
23282 if(group[i].el.isVisible(true)){
23290 for(var i in group){
23295 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23302 * Mark this field as valid
23304 markValid : function()
23308 this.fireEvent('valid', this);
23310 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23313 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23320 if(this.inputType == 'radio'){
23321 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23322 var fg = e.findParent('.form-group', false, true);
23323 if (Roo.bootstrap.version == 3) {
23324 fg.removeClass([_this.invalidClass, _this.validClass]);
23325 fg.addClass(_this.validClass);
23327 fg.removeClass(['is-valid', 'is-invalid']);
23328 fg.addClass('is-valid');
23336 var fg = this.el.findParent('.form-group', false, true);
23337 if (Roo.bootstrap.version == 3) {
23338 fg.removeClass([this.invalidClass, this.validClass]);
23339 fg.addClass(this.validClass);
23341 fg.removeClass(['is-valid', 'is-invalid']);
23342 fg.addClass('is-valid');
23347 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23353 for(var i in group){
23354 var fg = group[i].el.findParent('.form-group', false, true);
23355 if (Roo.bootstrap.version == 3) {
23356 fg.removeClass([this.invalidClass, this.validClass]);
23357 fg.addClass(this.validClass);
23359 fg.removeClass(['is-valid', 'is-invalid']);
23360 fg.addClass('is-valid');
23366 * Mark this field as invalid
23367 * @param {String} msg The validation message
23369 markInvalid : function(msg)
23371 if(this.allowBlank){
23377 this.fireEvent('invalid', this, msg);
23379 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23382 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23386 label.markInvalid();
23389 if(this.inputType == 'radio'){
23391 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23392 var fg = e.findParent('.form-group', false, true);
23393 if (Roo.bootstrap.version == 3) {
23394 fg.removeClass([_this.invalidClass, _this.validClass]);
23395 fg.addClass(_this.invalidClass);
23397 fg.removeClass(['is-invalid', 'is-valid']);
23398 fg.addClass('is-invalid');
23406 var fg = this.el.findParent('.form-group', false, true);
23407 if (Roo.bootstrap.version == 3) {
23408 fg.removeClass([_this.invalidClass, _this.validClass]);
23409 fg.addClass(_this.invalidClass);
23411 fg.removeClass(['is-invalid', 'is-valid']);
23412 fg.addClass('is-invalid');
23417 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23423 for(var i in group){
23424 var fg = group[i].el.findParent('.form-group', false, true);
23425 if (Roo.bootstrap.version == 3) {
23426 fg.removeClass([_this.invalidClass, _this.validClass]);
23427 fg.addClass(_this.invalidClass);
23429 fg.removeClass(['is-invalid', 'is-valid']);
23430 fg.addClass('is-invalid');
23436 clearInvalid : function()
23438 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23440 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23442 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23444 if (label && label.iconEl) {
23445 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23446 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23450 disable : function()
23452 if(this.inputType != 'radio'){
23453 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23460 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23461 _this.getActionEl().addClass(this.disabledClass);
23462 e.dom.disabled = true;
23466 this.disabled = true;
23467 this.fireEvent("disable", this);
23471 enable : function()
23473 if(this.inputType != 'radio'){
23474 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23481 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482 _this.getActionEl().removeClass(this.disabledClass);
23483 e.dom.disabled = false;
23487 this.disabled = false;
23488 this.fireEvent("enable", this);
23492 setBoxLabel : function(v)
23497 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23503 Roo.apply(Roo.bootstrap.CheckBox, {
23508 * register a CheckBox Group
23509 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23511 register : function(checkbox)
23513 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23514 this.groups[checkbox.groupId] = {};
23517 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23521 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23525 * fetch a CheckBox Group based on the group ID
23526 * @param {string} the group ID
23527 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23529 get: function(groupId) {
23530 if (typeof(this.groups[groupId]) == 'undefined') {
23534 return this.groups[groupId] ;
23547 * @class Roo.bootstrap.Radio
23548 * @extends Roo.bootstrap.Component
23549 * Bootstrap Radio class
23550 * @cfg {String} boxLabel - the label associated
23551 * @cfg {String} value - the value of radio
23554 * Create a new Radio
23555 * @param {Object} config The config object
23557 Roo.bootstrap.Radio = function(config){
23558 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23562 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23568 getAutoCreate : function()
23572 cls : 'form-group radio',
23577 html : this.boxLabel
23585 initEvents : function()
23587 this.parent().register(this);
23589 this.el.on('click', this.onClick, this);
23593 onClick : function(e)
23595 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23596 this.setChecked(true);
23600 setChecked : function(state, suppressEvent)
23602 this.parent().setValue(this.value, suppressEvent);
23606 setBoxLabel : function(v)
23611 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23626 * @class Roo.bootstrap.SecurePass
23627 * @extends Roo.bootstrap.Input
23628 * Bootstrap SecurePass class
23632 * Create a new SecurePass
23633 * @param {Object} config The config object
23636 Roo.bootstrap.SecurePass = function (config) {
23637 // these go here, so the translation tool can replace them..
23639 PwdEmpty: "Please type a password, and then retype it to confirm.",
23640 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23641 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23642 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23643 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23644 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23645 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23646 TooWeak: "Your password is Too Weak."
23648 this.meterLabel = "Password strength:";
23649 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23650 this.meterClass = [
23651 "roo-password-meter-tooweak",
23652 "roo-password-meter-weak",
23653 "roo-password-meter-medium",
23654 "roo-password-meter-strong",
23655 "roo-password-meter-grey"
23660 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23663 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23665 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23667 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23668 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23669 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23670 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23671 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23672 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23673 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23683 * @cfg {String/Object} Label for the strength meter (defaults to
23684 * 'Password strength:')
23689 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23690 * ['Weak', 'Medium', 'Strong'])
23693 pwdStrengths: false,
23706 initEvents: function ()
23708 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23710 if (this.el.is('input[type=password]') && Roo.isSafari) {
23711 this.el.on('keydown', this.SafariOnKeyDown, this);
23714 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23717 onRender: function (ct, position)
23719 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23720 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23721 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23723 this.trigger.createChild({
23728 cls: 'roo-password-meter-grey col-xs-12',
23731 //width: this.meterWidth + 'px'
23735 cls: 'roo-password-meter-text'
23741 if (this.hideTrigger) {
23742 this.trigger.setDisplayed(false);
23744 this.setSize(this.width || '', this.height || '');
23747 onDestroy: function ()
23749 if (this.trigger) {
23750 this.trigger.removeAllListeners();
23751 this.trigger.remove();
23754 this.wrap.remove();
23756 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23759 checkStrength: function ()
23761 var pwd = this.inputEl().getValue();
23762 if (pwd == this._lastPwd) {
23767 if (this.ClientSideStrongPassword(pwd)) {
23769 } else if (this.ClientSideMediumPassword(pwd)) {
23771 } else if (this.ClientSideWeakPassword(pwd)) {
23777 Roo.log('strength1: ' + strength);
23779 //var pm = this.trigger.child('div/div/div').dom;
23780 var pm = this.trigger.child('div/div');
23781 pm.removeClass(this.meterClass);
23782 pm.addClass(this.meterClass[strength]);
23785 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23787 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23789 this._lastPwd = pwd;
23793 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23795 this._lastPwd = '';
23797 var pm = this.trigger.child('div/div');
23798 pm.removeClass(this.meterClass);
23799 pm.addClass('roo-password-meter-grey');
23802 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23805 this.inputEl().dom.type='password';
23808 validateValue: function (value)
23810 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23813 if (value.length == 0) {
23814 if (this.allowBlank) {
23815 this.clearInvalid();
23819 this.markInvalid(this.errors.PwdEmpty);
23820 this.errorMsg = this.errors.PwdEmpty;
23828 if (!value.match(/[\x21-\x7e]+/)) {
23829 this.markInvalid(this.errors.PwdBadChar);
23830 this.errorMsg = this.errors.PwdBadChar;
23833 if (value.length < 6) {
23834 this.markInvalid(this.errors.PwdShort);
23835 this.errorMsg = this.errors.PwdShort;
23838 if (value.length > 16) {
23839 this.markInvalid(this.errors.PwdLong);
23840 this.errorMsg = this.errors.PwdLong;
23844 if (this.ClientSideStrongPassword(value)) {
23846 } else if (this.ClientSideMediumPassword(value)) {
23848 } else if (this.ClientSideWeakPassword(value)) {
23855 if (strength < 2) {
23856 //this.markInvalid(this.errors.TooWeak);
23857 this.errorMsg = this.errors.TooWeak;
23862 console.log('strength2: ' + strength);
23864 //var pm = this.trigger.child('div/div/div').dom;
23866 var pm = this.trigger.child('div/div');
23867 pm.removeClass(this.meterClass);
23868 pm.addClass(this.meterClass[strength]);
23870 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23872 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23874 this.errorMsg = '';
23878 CharacterSetChecks: function (type)
23881 this.fResult = false;
23884 isctype: function (character, type)
23887 case this.kCapitalLetter:
23888 if (character >= 'A' && character <= 'Z') {
23893 case this.kSmallLetter:
23894 if (character >= 'a' && character <= 'z') {
23900 if (character >= '0' && character <= '9') {
23905 case this.kPunctuation:
23906 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23917 IsLongEnough: function (pwd, size)
23919 return !(pwd == null || isNaN(size) || pwd.length < size);
23922 SpansEnoughCharacterSets: function (word, nb)
23924 if (!this.IsLongEnough(word, nb))
23929 var characterSetChecks = new Array(
23930 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23931 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23934 for (var index = 0; index < word.length; ++index) {
23935 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23936 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23937 characterSetChecks[nCharSet].fResult = true;
23944 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23945 if (characterSetChecks[nCharSet].fResult) {
23950 if (nCharSets < nb) {
23956 ClientSideStrongPassword: function (pwd)
23958 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23961 ClientSideMediumPassword: function (pwd)
23963 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23966 ClientSideWeakPassword: function (pwd)
23968 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23971 })//<script type="text/javascript">
23974 * Based Ext JS Library 1.1.1
23975 * Copyright(c) 2006-2007, Ext JS, LLC.
23981 * @class Roo.HtmlEditorCore
23982 * @extends Roo.Component
23983 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23985 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23988 Roo.HtmlEditorCore = function(config){
23991 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23996 * @event initialize
23997 * Fires when the editor is fully initialized (including the iframe)
23998 * @param {Roo.HtmlEditorCore} this
24003 * Fires when the editor is first receives the focus. Any insertion must wait
24004 * until after this event.
24005 * @param {Roo.HtmlEditorCore} this
24009 * @event beforesync
24010 * Fires before the textarea is updated with content from the editor iframe. Return false
24011 * to cancel the sync.
24012 * @param {Roo.HtmlEditorCore} this
24013 * @param {String} html
24017 * @event beforepush
24018 * Fires before the iframe editor is updated with content from the textarea. Return false
24019 * to cancel the push.
24020 * @param {Roo.HtmlEditorCore} this
24021 * @param {String} html
24026 * Fires when the textarea is updated with content from the editor iframe.
24027 * @param {Roo.HtmlEditorCore} this
24028 * @param {String} html
24033 * Fires when the iframe editor is updated with content from the textarea.
24034 * @param {Roo.HtmlEditorCore} this
24035 * @param {String} html
24040 * @event editorevent
24041 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24042 * @param {Roo.HtmlEditorCore} this
24048 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24050 // defaults : white / black...
24051 this.applyBlacklists();
24058 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24062 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24068 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24073 * @cfg {Number} height (in pixels)
24077 * @cfg {Number} width (in pixels)
24082 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24085 stylesheets: false,
24090 // private properties
24091 validationEvent : false,
24093 initialized : false,
24095 sourceEditMode : false,
24096 onFocus : Roo.emptyFn,
24098 hideMode:'offsets',
24102 // blacklist + whitelisted elements..
24109 * Protected method that will not generally be called directly. It
24110 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24111 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24113 getDocMarkup : function(){
24117 // inherit styels from page...??
24118 if (this.stylesheets === false) {
24120 Roo.get(document.head).select('style').each(function(node) {
24121 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24124 Roo.get(document.head).select('link').each(function(node) {
24125 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24128 } else if (!this.stylesheets.length) {
24130 st = '<style type="text/css">' +
24131 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24134 for (var i in this.stylesheets) {
24135 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24140 st += '<style type="text/css">' +
24141 'IMG { cursor: pointer } ' +
24144 var cls = 'roo-htmleditor-body';
24146 if(this.bodyCls.length){
24147 cls += ' ' + this.bodyCls;
24150 return '<html><head>' + st +
24151 //<style type="text/css">' +
24152 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24154 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24158 onRender : function(ct, position)
24161 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24162 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24165 this.el.dom.style.border = '0 none';
24166 this.el.dom.setAttribute('tabIndex', -1);
24167 this.el.addClass('x-hidden hide');
24171 if(Roo.isIE){ // fix IE 1px bogus margin
24172 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24176 this.frameId = Roo.id();
24180 var iframe = this.owner.wrap.createChild({
24182 cls: 'form-control', // bootstrap..
24184 name: this.frameId,
24185 frameBorder : 'no',
24186 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24191 this.iframe = iframe.dom;
24193 this.assignDocWin();
24195 this.doc.designMode = 'on';
24198 this.doc.write(this.getDocMarkup());
24202 var task = { // must defer to wait for browser to be ready
24204 //console.log("run task?" + this.doc.readyState);
24205 this.assignDocWin();
24206 if(this.doc.body || this.doc.readyState == 'complete'){
24208 this.doc.designMode="on";
24212 Roo.TaskMgr.stop(task);
24213 this.initEditor.defer(10, this);
24220 Roo.TaskMgr.start(task);
24225 onResize : function(w, h)
24227 Roo.log('resize: ' +w + ',' + h );
24228 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24232 if(typeof w == 'number'){
24234 this.iframe.style.width = w + 'px';
24236 if(typeof h == 'number'){
24238 this.iframe.style.height = h + 'px';
24240 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24247 * Toggles the editor between standard and source edit mode.
24248 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24250 toggleSourceEdit : function(sourceEditMode){
24252 this.sourceEditMode = sourceEditMode === true;
24254 if(this.sourceEditMode){
24256 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24259 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24260 //this.iframe.className = '';
24263 //this.setSize(this.owner.wrap.getSize());
24264 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24271 * Protected method that will not generally be called directly. If you need/want
24272 * custom HTML cleanup, this is the method you should override.
24273 * @param {String} html The HTML to be cleaned
24274 * return {String} The cleaned HTML
24276 cleanHtml : function(html){
24277 html = String(html);
24278 if(html.length > 5){
24279 if(Roo.isSafari){ // strip safari nonsense
24280 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24283 if(html == ' '){
24290 * HTML Editor -> Textarea
24291 * Protected method that will not generally be called directly. Syncs the contents
24292 * of the editor iframe with the textarea.
24294 syncValue : function(){
24295 if(this.initialized){
24296 var bd = (this.doc.body || this.doc.documentElement);
24297 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24298 var html = bd.innerHTML;
24300 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24301 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24303 html = '<div style="'+m[0]+'">' + html + '</div>';
24306 html = this.cleanHtml(html);
24307 // fix up the special chars.. normaly like back quotes in word...
24308 // however we do not want to do this with chinese..
24309 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24311 var cc = match.charCodeAt();
24313 // Get the character value, handling surrogate pairs
24314 if (match.length == 2) {
24315 // It's a surrogate pair, calculate the Unicode code point
24316 var high = match.charCodeAt(0) - 0xD800;
24317 var low = match.charCodeAt(1) - 0xDC00;
24318 cc = (high * 0x400) + low + 0x10000;
24320 (cc >= 0x4E00 && cc < 0xA000 ) ||
24321 (cc >= 0x3400 && cc < 0x4E00 ) ||
24322 (cc >= 0xf900 && cc < 0xfb00 )
24327 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24328 return "&#" + cc + ";";
24335 if(this.owner.fireEvent('beforesync', this, html) !== false){
24336 this.el.dom.value = html;
24337 this.owner.fireEvent('sync', this, html);
24343 * Protected method that will not generally be called directly. Pushes the value of the textarea
24344 * into the iframe editor.
24346 pushValue : function(){
24347 if(this.initialized){
24348 var v = this.el.dom.value.trim();
24350 // if(v.length < 1){
24354 if(this.owner.fireEvent('beforepush', this, v) !== false){
24355 var d = (this.doc.body || this.doc.documentElement);
24357 this.cleanUpPaste();
24358 this.el.dom.value = d.innerHTML;
24359 this.owner.fireEvent('push', this, v);
24365 deferFocus : function(){
24366 this.focus.defer(10, this);
24370 focus : function(){
24371 if(this.win && !this.sourceEditMode){
24378 assignDocWin: function()
24380 var iframe = this.iframe;
24383 this.doc = iframe.contentWindow.document;
24384 this.win = iframe.contentWindow;
24386 // if (!Roo.get(this.frameId)) {
24389 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24390 // this.win = Roo.get(this.frameId).dom.contentWindow;
24392 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24396 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24397 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24402 initEditor : function(){
24403 //console.log("INIT EDITOR");
24404 this.assignDocWin();
24408 this.doc.designMode="on";
24410 this.doc.write(this.getDocMarkup());
24413 var dbody = (this.doc.body || this.doc.documentElement);
24414 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24415 // this copies styles from the containing element into thsi one..
24416 // not sure why we need all of this..
24417 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24419 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24420 //ss['background-attachment'] = 'fixed'; // w3c
24421 dbody.bgProperties = 'fixed'; // ie
24422 //Roo.DomHelper.applyStyles(dbody, ss);
24423 Roo.EventManager.on(this.doc, {
24424 //'mousedown': this.onEditorEvent,
24425 'mouseup': this.onEditorEvent,
24426 'dblclick': this.onEditorEvent,
24427 'click': this.onEditorEvent,
24428 'keyup': this.onEditorEvent,
24433 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24435 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24436 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24438 this.initialized = true;
24440 this.owner.fireEvent('initialize', this);
24445 onDestroy : function(){
24451 //for (var i =0; i < this.toolbars.length;i++) {
24452 // // fixme - ask toolbars for heights?
24453 // this.toolbars[i].onDestroy();
24456 //this.wrap.dom.innerHTML = '';
24457 //this.wrap.remove();
24462 onFirstFocus : function(){
24464 this.assignDocWin();
24467 this.activated = true;
24470 if(Roo.isGecko){ // prevent silly gecko errors
24472 var s = this.win.getSelection();
24473 if(!s.focusNode || s.focusNode.nodeType != 3){
24474 var r = s.getRangeAt(0);
24475 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24480 this.execCmd('useCSS', true);
24481 this.execCmd('styleWithCSS', false);
24484 this.owner.fireEvent('activate', this);
24488 adjustFont: function(btn){
24489 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24490 //if(Roo.isSafari){ // safari
24493 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24494 if(Roo.isSafari){ // safari
24495 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24496 v = (v < 10) ? 10 : v;
24497 v = (v > 48) ? 48 : v;
24498 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24503 v = Math.max(1, v+adjust);
24505 this.execCmd('FontSize', v );
24508 onEditorEvent : function(e)
24510 this.owner.fireEvent('editorevent', this, e);
24511 // this.updateToolbar();
24512 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24515 insertTag : function(tg)
24517 // could be a bit smarter... -> wrap the current selected tRoo..
24518 if (tg.toLowerCase() == 'span' ||
24519 tg.toLowerCase() == 'code' ||
24520 tg.toLowerCase() == 'sup' ||
24521 tg.toLowerCase() == 'sub'
24524 range = this.createRange(this.getSelection());
24525 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24526 wrappingNode.appendChild(range.extractContents());
24527 range.insertNode(wrappingNode);
24534 this.execCmd("formatblock", tg);
24538 insertText : function(txt)
24542 var range = this.createRange();
24543 range.deleteContents();
24544 //alert(Sender.getAttribute('label'));
24546 range.insertNode(this.doc.createTextNode(txt));
24552 * Executes a Midas editor command on the editor document and performs necessary focus and
24553 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24554 * @param {String} cmd The Midas command
24555 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24557 relayCmd : function(cmd, value){
24559 this.execCmd(cmd, value);
24560 this.owner.fireEvent('editorevent', this);
24561 //this.updateToolbar();
24562 this.owner.deferFocus();
24566 * Executes a Midas editor command directly on the editor document.
24567 * For visual commands, you should use {@link #relayCmd} instead.
24568 * <b>This should only be called after the editor is initialized.</b>
24569 * @param {String} cmd The Midas command
24570 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24572 execCmd : function(cmd, value){
24573 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24580 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24582 * @param {String} text | dom node..
24584 insertAtCursor : function(text)
24587 if(!this.activated){
24593 var r = this.doc.selection.createRange();
24604 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24608 // from jquery ui (MIT licenced)
24610 var win = this.win;
24612 if (win.getSelection && win.getSelection().getRangeAt) {
24613 range = win.getSelection().getRangeAt(0);
24614 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24615 range.insertNode(node);
24616 } else if (win.document.selection && win.document.selection.createRange) {
24617 // no firefox support
24618 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24619 win.document.selection.createRange().pasteHTML(txt);
24621 // no firefox support
24622 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24623 this.execCmd('InsertHTML', txt);
24632 mozKeyPress : function(e){
24634 var c = e.getCharCode(), cmd;
24637 c = String.fromCharCode(c).toLowerCase();
24651 this.cleanUpPaste.defer(100, this);
24659 e.preventDefault();
24667 fixKeys : function(){ // load time branching for fastest keydown performance
24669 return function(e){
24670 var k = e.getKey(), r;
24673 r = this.doc.selection.createRange();
24676 r.pasteHTML('    ');
24683 r = this.doc.selection.createRange();
24685 var target = r.parentElement();
24686 if(!target || target.tagName.toLowerCase() != 'li'){
24688 r.pasteHTML('<br />');
24694 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24695 this.cleanUpPaste.defer(100, this);
24701 }else if(Roo.isOpera){
24702 return function(e){
24703 var k = e.getKey();
24707 this.execCmd('InsertHTML','    ');
24710 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24711 this.cleanUpPaste.defer(100, this);
24716 }else if(Roo.isSafari){
24717 return function(e){
24718 var k = e.getKey();
24722 this.execCmd('InsertText','\t');
24726 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24727 this.cleanUpPaste.defer(100, this);
24735 getAllAncestors: function()
24737 var p = this.getSelectedNode();
24740 a.push(p); // push blank onto stack..
24741 p = this.getParentElement();
24745 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24749 a.push(this.doc.body);
24753 lastSelNode : false,
24756 getSelection : function()
24758 this.assignDocWin();
24759 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24762 getSelectedNode: function()
24764 // this may only work on Gecko!!!
24766 // should we cache this!!!!
24771 var range = this.createRange(this.getSelection()).cloneRange();
24774 var parent = range.parentElement();
24776 var testRange = range.duplicate();
24777 testRange.moveToElementText(parent);
24778 if (testRange.inRange(range)) {
24781 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24784 parent = parent.parentElement;
24789 // is ancestor a text element.
24790 var ac = range.commonAncestorContainer;
24791 if (ac.nodeType == 3) {
24792 ac = ac.parentNode;
24795 var ar = ac.childNodes;
24798 var other_nodes = [];
24799 var has_other_nodes = false;
24800 for (var i=0;i<ar.length;i++) {
24801 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24804 // fullly contained node.
24806 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24811 // probably selected..
24812 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24813 other_nodes.push(ar[i]);
24817 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24822 has_other_nodes = true;
24824 if (!nodes.length && other_nodes.length) {
24825 nodes= other_nodes;
24827 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24833 createRange: function(sel)
24835 // this has strange effects when using with
24836 // top toolbar - not sure if it's a great idea.
24837 //this.editor.contentWindow.focus();
24838 if (typeof sel != "undefined") {
24840 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24842 return this.doc.createRange();
24845 return this.doc.createRange();
24848 getParentElement: function()
24851 this.assignDocWin();
24852 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24854 var range = this.createRange(sel);
24857 var p = range.commonAncestorContainer;
24858 while (p.nodeType == 3) { // text node
24869 * Range intersection.. the hard stuff...
24873 * [ -- selected range --- ]
24877 * if end is before start or hits it. fail.
24878 * if start is after end or hits it fail.
24880 * if either hits (but other is outside. - then it's not
24886 // @see http://www.thismuchiknow.co.uk/?p=64.
24887 rangeIntersectsNode : function(range, node)
24889 var nodeRange = node.ownerDocument.createRange();
24891 nodeRange.selectNode(node);
24893 nodeRange.selectNodeContents(node);
24896 var rangeStartRange = range.cloneRange();
24897 rangeStartRange.collapse(true);
24899 var rangeEndRange = range.cloneRange();
24900 rangeEndRange.collapse(false);
24902 var nodeStartRange = nodeRange.cloneRange();
24903 nodeStartRange.collapse(true);
24905 var nodeEndRange = nodeRange.cloneRange();
24906 nodeEndRange.collapse(false);
24908 return rangeStartRange.compareBoundaryPoints(
24909 Range.START_TO_START, nodeEndRange) == -1 &&
24910 rangeEndRange.compareBoundaryPoints(
24911 Range.START_TO_START, nodeStartRange) == 1;
24915 rangeCompareNode : function(range, node)
24917 var nodeRange = node.ownerDocument.createRange();
24919 nodeRange.selectNode(node);
24921 nodeRange.selectNodeContents(node);
24925 range.collapse(true);
24927 nodeRange.collapse(true);
24929 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24930 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24932 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24934 var nodeIsBefore = ss == 1;
24935 var nodeIsAfter = ee == -1;
24937 if (nodeIsBefore && nodeIsAfter) {
24940 if (!nodeIsBefore && nodeIsAfter) {
24941 return 1; //right trailed.
24944 if (nodeIsBefore && !nodeIsAfter) {
24945 return 2; // left trailed.
24951 // private? - in a new class?
24952 cleanUpPaste : function()
24954 // cleans up the whole document..
24955 Roo.log('cleanuppaste');
24957 this.cleanUpChildren(this.doc.body);
24958 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24959 if (clean != this.doc.body.innerHTML) {
24960 this.doc.body.innerHTML = clean;
24965 cleanWordChars : function(input) {// change the chars to hex code
24966 var he = Roo.HtmlEditorCore;
24968 var output = input;
24969 Roo.each(he.swapCodes, function(sw) {
24970 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24972 output = output.replace(swapper, sw[1]);
24979 cleanUpChildren : function (n)
24981 if (!n.childNodes.length) {
24984 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24985 this.cleanUpChild(n.childNodes[i]);
24992 cleanUpChild : function (node)
24995 //console.log(node);
24996 if (node.nodeName == "#text") {
24997 // clean up silly Windows -- stuff?
25000 if (node.nodeName == "#comment") {
25001 node.parentNode.removeChild(node);
25002 // clean up silly Windows -- stuff?
25005 var lcname = node.tagName.toLowerCase();
25006 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25007 // whitelist of tags..
25009 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25011 node.parentNode.removeChild(node);
25016 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25018 // spans with no attributes - just remove them..
25019 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25020 remove_keep_children = true;
25023 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25024 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25026 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25027 // remove_keep_children = true;
25030 if (remove_keep_children) {
25031 this.cleanUpChildren(node);
25032 // inserts everything just before this node...
25033 while (node.childNodes.length) {
25034 var cn = node.childNodes[0];
25035 node.removeChild(cn);
25036 node.parentNode.insertBefore(cn, node);
25038 node.parentNode.removeChild(node);
25042 if (!node.attributes || !node.attributes.length) {
25047 this.cleanUpChildren(node);
25051 function cleanAttr(n,v)
25054 if (v.match(/^\./) || v.match(/^\//)) {
25057 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25060 if (v.match(/^#/)) {
25063 if (v.match(/^\{/)) { // allow template editing.
25066 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25067 node.removeAttribute(n);
25071 var cwhite = this.cwhite;
25072 var cblack = this.cblack;
25074 function cleanStyle(n,v)
25076 if (v.match(/expression/)) { //XSS?? should we even bother..
25077 node.removeAttribute(n);
25081 var parts = v.split(/;/);
25084 Roo.each(parts, function(p) {
25085 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25089 var l = p.split(':').shift().replace(/\s+/g,'');
25090 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25092 if ( cwhite.length && cblack.indexOf(l) > -1) {
25093 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25094 //node.removeAttribute(n);
25098 // only allow 'c whitelisted system attributes'
25099 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25100 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25101 //node.removeAttribute(n);
25111 if (clean.length) {
25112 node.setAttribute(n, clean.join(';'));
25114 node.removeAttribute(n);
25120 for (var i = node.attributes.length-1; i > -1 ; i--) {
25121 var a = node.attributes[i];
25124 if (a.name.toLowerCase().substr(0,2)=='on') {
25125 node.removeAttribute(a.name);
25128 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25129 node.removeAttribute(a.name);
25132 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25133 cleanAttr(a.name,a.value); // fixme..
25136 if (a.name == 'style') {
25137 cleanStyle(a.name,a.value);
25140 /// clean up MS crap..
25141 // tecnically this should be a list of valid class'es..
25144 if (a.name == 'class') {
25145 if (a.value.match(/^Mso/)) {
25146 node.removeAttribute('class');
25149 if (a.value.match(/^body$/)) {
25150 node.removeAttribute('class');
25161 this.cleanUpChildren(node);
25167 * Clean up MS wordisms...
25169 cleanWord : function(node)
25172 this.cleanWord(this.doc.body);
25177 node.nodeName == 'SPAN' &&
25178 !node.hasAttributes() &&
25179 node.childNodes.length == 1 &&
25180 node.firstChild.nodeName == "#text"
25182 var textNode = node.firstChild;
25183 node.removeChild(textNode);
25184 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25185 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25187 node.parentNode.insertBefore(textNode, node);
25188 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25189 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25191 node.parentNode.removeChild(node);
25194 if (node.nodeName == "#text") {
25195 // clean up silly Windows -- stuff?
25198 if (node.nodeName == "#comment") {
25199 node.parentNode.removeChild(node);
25200 // clean up silly Windows -- stuff?
25204 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25205 node.parentNode.removeChild(node);
25208 //Roo.log(node.tagName);
25209 // remove - but keep children..
25210 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25211 //Roo.log('-- removed');
25212 while (node.childNodes.length) {
25213 var cn = node.childNodes[0];
25214 node.removeChild(cn);
25215 node.parentNode.insertBefore(cn, node);
25216 // move node to parent - and clean it..
25217 this.cleanWord(cn);
25219 node.parentNode.removeChild(node);
25220 /// no need to iterate chidlren = it's got none..
25221 //this.iterateChildren(node, this.cleanWord);
25225 if (node.className.length) {
25227 var cn = node.className.split(/\W+/);
25229 Roo.each(cn, function(cls) {
25230 if (cls.match(/Mso[a-zA-Z]+/)) {
25235 node.className = cna.length ? cna.join(' ') : '';
25237 node.removeAttribute("class");
25241 if (node.hasAttribute("lang")) {
25242 node.removeAttribute("lang");
25245 if (node.hasAttribute("style")) {
25247 var styles = node.getAttribute("style").split(";");
25249 Roo.each(styles, function(s) {
25250 if (!s.match(/:/)) {
25253 var kv = s.split(":");
25254 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25257 // what ever is left... we allow.
25260 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25261 if (!nstyle.length) {
25262 node.removeAttribute('style');
25265 this.iterateChildren(node, this.cleanWord);
25271 * iterateChildren of a Node, calling fn each time, using this as the scole..
25272 * @param {DomNode} node node to iterate children of.
25273 * @param {Function} fn method of this class to call on each item.
25275 iterateChildren : function(node, fn)
25277 if (!node.childNodes.length) {
25280 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25281 fn.call(this, node.childNodes[i])
25287 * cleanTableWidths.
25289 * Quite often pasting from word etc.. results in tables with column and widths.
25290 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25293 cleanTableWidths : function(node)
25298 this.cleanTableWidths(this.doc.body);
25303 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25306 Roo.log(node.tagName);
25307 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25308 this.iterateChildren(node, this.cleanTableWidths);
25311 if (node.hasAttribute('width')) {
25312 node.removeAttribute('width');
25316 if (node.hasAttribute("style")) {
25319 var styles = node.getAttribute("style").split(";");
25321 Roo.each(styles, function(s) {
25322 if (!s.match(/:/)) {
25325 var kv = s.split(":");
25326 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25329 // what ever is left... we allow.
25332 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25333 if (!nstyle.length) {
25334 node.removeAttribute('style');
25338 this.iterateChildren(node, this.cleanTableWidths);
25346 domToHTML : function(currentElement, depth, nopadtext) {
25348 depth = depth || 0;
25349 nopadtext = nopadtext || false;
25351 if (!currentElement) {
25352 return this.domToHTML(this.doc.body);
25355 //Roo.log(currentElement);
25357 var allText = false;
25358 var nodeName = currentElement.nodeName;
25359 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25361 if (nodeName == '#text') {
25363 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25368 if (nodeName != 'BODY') {
25371 // Prints the node tagName, such as <A>, <IMG>, etc
25374 for(i = 0; i < currentElement.attributes.length;i++) {
25376 var aname = currentElement.attributes.item(i).name;
25377 if (!currentElement.attributes.item(i).value.length) {
25380 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25383 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25392 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25395 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25400 // Traverse the tree
25402 var currentElementChild = currentElement.childNodes.item(i);
25403 var allText = true;
25404 var innerHTML = '';
25406 while (currentElementChild) {
25407 // Formatting code (indent the tree so it looks nice on the screen)
25408 var nopad = nopadtext;
25409 if (lastnode == 'SPAN') {
25413 if (currentElementChild.nodeName == '#text') {
25414 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25415 toadd = nopadtext ? toadd : toadd.trim();
25416 if (!nopad && toadd.length > 80) {
25417 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25419 innerHTML += toadd;
25422 currentElementChild = currentElement.childNodes.item(i);
25428 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25430 // Recursively traverse the tree structure of the child node
25431 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25432 lastnode = currentElementChild.nodeName;
25434 currentElementChild=currentElement.childNodes.item(i);
25440 // The remaining code is mostly for formatting the tree
25441 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25446 ret+= "</"+tagName+">";
25452 applyBlacklists : function()
25454 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25455 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25459 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25460 if (b.indexOf(tag) > -1) {
25463 this.white.push(tag);
25467 Roo.each(w, function(tag) {
25468 if (b.indexOf(tag) > -1) {
25471 if (this.white.indexOf(tag) > -1) {
25474 this.white.push(tag);
25479 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25480 if (w.indexOf(tag) > -1) {
25483 this.black.push(tag);
25487 Roo.each(b, function(tag) {
25488 if (w.indexOf(tag) > -1) {
25491 if (this.black.indexOf(tag) > -1) {
25494 this.black.push(tag);
25499 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25500 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25504 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25505 if (b.indexOf(tag) > -1) {
25508 this.cwhite.push(tag);
25512 Roo.each(w, function(tag) {
25513 if (b.indexOf(tag) > -1) {
25516 if (this.cwhite.indexOf(tag) > -1) {
25519 this.cwhite.push(tag);
25524 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25525 if (w.indexOf(tag) > -1) {
25528 this.cblack.push(tag);
25532 Roo.each(b, function(tag) {
25533 if (w.indexOf(tag) > -1) {
25536 if (this.cblack.indexOf(tag) > -1) {
25539 this.cblack.push(tag);
25544 setStylesheets : function(stylesheets)
25546 if(typeof(stylesheets) == 'string'){
25547 Roo.get(this.iframe.contentDocument.head).createChild({
25549 rel : 'stylesheet',
25558 Roo.each(stylesheets, function(s) {
25563 Roo.get(_this.iframe.contentDocument.head).createChild({
25565 rel : 'stylesheet',
25574 removeStylesheets : function()
25578 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25583 setStyle : function(style)
25585 Roo.get(this.iframe.contentDocument.head).createChild({
25594 // hide stuff that is not compatible
25608 * @event specialkey
25612 * @cfg {String} fieldClass @hide
25615 * @cfg {String} focusClass @hide
25618 * @cfg {String} autoCreate @hide
25621 * @cfg {String} inputType @hide
25624 * @cfg {String} invalidClass @hide
25627 * @cfg {String} invalidText @hide
25630 * @cfg {String} msgFx @hide
25633 * @cfg {String} validateOnBlur @hide
25637 Roo.HtmlEditorCore.white = [
25638 'area', 'br', 'img', 'input', 'hr', 'wbr',
25640 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25641 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25642 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25643 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25644 'table', 'ul', 'xmp',
25646 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25649 'dir', 'menu', 'ol', 'ul', 'dl',
25655 Roo.HtmlEditorCore.black = [
25656 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25658 'base', 'basefont', 'bgsound', 'blink', 'body',
25659 'frame', 'frameset', 'head', 'html', 'ilayer',
25660 'iframe', 'layer', 'link', 'meta', 'object',
25661 'script', 'style' ,'title', 'xml' // clean later..
25663 Roo.HtmlEditorCore.clean = [
25664 'script', 'style', 'title', 'xml'
25666 Roo.HtmlEditorCore.remove = [
25671 Roo.HtmlEditorCore.ablack = [
25675 Roo.HtmlEditorCore.aclean = [
25676 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25680 Roo.HtmlEditorCore.pwhite= [
25681 'http', 'https', 'mailto'
25684 // white listed style attributes.
25685 Roo.HtmlEditorCore.cwhite= [
25686 // 'text-align', /// default is to allow most things..
25692 // black listed style attributes.
25693 Roo.HtmlEditorCore.cblack= [
25694 // 'font-size' -- this can be set by the project
25698 Roo.HtmlEditorCore.swapCodes =[
25717 * @class Roo.bootstrap.HtmlEditor
25718 * @extends Roo.bootstrap.TextArea
25719 * Bootstrap HtmlEditor class
25722 * Create a new HtmlEditor
25723 * @param {Object} config The config object
25726 Roo.bootstrap.HtmlEditor = function(config){
25727 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25728 if (!this.toolbars) {
25729 this.toolbars = [];
25732 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25735 * @event initialize
25736 * Fires when the editor is fully initialized (including the iframe)
25737 * @param {HtmlEditor} this
25742 * Fires when the editor is first receives the focus. Any insertion must wait
25743 * until after this event.
25744 * @param {HtmlEditor} this
25748 * @event beforesync
25749 * Fires before the textarea is updated with content from the editor iframe. Return false
25750 * to cancel the sync.
25751 * @param {HtmlEditor} this
25752 * @param {String} html
25756 * @event beforepush
25757 * Fires before the iframe editor is updated with content from the textarea. Return false
25758 * to cancel the push.
25759 * @param {HtmlEditor} this
25760 * @param {String} html
25765 * Fires when the textarea is updated with content from the editor iframe.
25766 * @param {HtmlEditor} this
25767 * @param {String} html
25772 * Fires when the iframe editor is updated with content from the textarea.
25773 * @param {HtmlEditor} this
25774 * @param {String} html
25778 * @event editmodechange
25779 * Fires when the editor switches edit modes
25780 * @param {HtmlEditor} this
25781 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25783 editmodechange: true,
25785 * @event editorevent
25786 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25787 * @param {HtmlEditor} this
25791 * @event firstfocus
25792 * Fires when on first focus - needed by toolbars..
25793 * @param {HtmlEditor} this
25798 * Auto save the htmlEditor value as a file into Events
25799 * @param {HtmlEditor} this
25803 * @event savedpreview
25804 * preview the saved version of htmlEditor
25805 * @param {HtmlEditor} this
25812 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25816 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25821 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25826 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25831 * @cfg {Number} height (in pixels)
25835 * @cfg {Number} width (in pixels)
25840 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25843 stylesheets: false,
25848 // private properties
25849 validationEvent : false,
25851 initialized : false,
25854 onFocus : Roo.emptyFn,
25856 hideMode:'offsets',
25858 tbContainer : false,
25862 toolbarContainer :function() {
25863 return this.wrap.select('.x-html-editor-tb',true).first();
25867 * Protected method that will not generally be called directly. It
25868 * is called when the editor creates its toolbar. Override this method if you need to
25869 * add custom toolbar buttons.
25870 * @param {HtmlEditor} editor
25872 createToolbar : function(){
25873 Roo.log('renewing');
25874 Roo.log("create toolbars");
25876 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25877 this.toolbars[0].render(this.toolbarContainer());
25881 // if (!editor.toolbars || !editor.toolbars.length) {
25882 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25885 // for (var i =0 ; i < editor.toolbars.length;i++) {
25886 // editor.toolbars[i] = Roo.factory(
25887 // typeof(editor.toolbars[i]) == 'string' ?
25888 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25889 // Roo.bootstrap.HtmlEditor);
25890 // editor.toolbars[i].init(editor);
25896 onRender : function(ct, position)
25898 // Roo.log("Call onRender: " + this.xtype);
25900 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25902 this.wrap = this.inputEl().wrap({
25903 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25906 this.editorcore.onRender(ct, position);
25908 if (this.resizable) {
25909 this.resizeEl = new Roo.Resizable(this.wrap, {
25913 minHeight : this.height,
25914 height: this.height,
25915 handles : this.resizable,
25918 resize : function(r, w, h) {
25919 _t.onResize(w,h); // -something
25925 this.createToolbar(this);
25928 if(!this.width && this.resizable){
25929 this.setSize(this.wrap.getSize());
25931 if (this.resizeEl) {
25932 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25933 // should trigger onReize..
25939 onResize : function(w, h)
25941 Roo.log('resize: ' +w + ',' + h );
25942 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25946 if(this.inputEl() ){
25947 if(typeof w == 'number'){
25948 var aw = w - this.wrap.getFrameWidth('lr');
25949 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25952 if(typeof h == 'number'){
25953 var tbh = -11; // fixme it needs to tool bar size!
25954 for (var i =0; i < this.toolbars.length;i++) {
25955 // fixme - ask toolbars for heights?
25956 tbh += this.toolbars[i].el.getHeight();
25957 //if (this.toolbars[i].footer) {
25958 // tbh += this.toolbars[i].footer.el.getHeight();
25966 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25967 ah -= 5; // knock a few pixes off for look..
25968 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25972 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25973 this.editorcore.onResize(ew,eh);
25978 * Toggles the editor between standard and source edit mode.
25979 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25981 toggleSourceEdit : function(sourceEditMode)
25983 this.editorcore.toggleSourceEdit(sourceEditMode);
25985 if(this.editorcore.sourceEditMode){
25986 Roo.log('editor - showing textarea');
25989 // Roo.log(this.syncValue());
25991 this.inputEl().removeClass(['hide', 'x-hidden']);
25992 this.inputEl().dom.removeAttribute('tabIndex');
25993 this.inputEl().focus();
25995 Roo.log('editor - hiding textarea');
25997 // Roo.log(this.pushValue());
26000 this.inputEl().addClass(['hide', 'x-hidden']);
26001 this.inputEl().dom.setAttribute('tabIndex', -1);
26002 //this.deferFocus();
26005 if(this.resizable){
26006 this.setSize(this.wrap.getSize());
26009 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26012 // private (for BoxComponent)
26013 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26015 // private (for BoxComponent)
26016 getResizeEl : function(){
26020 // private (for BoxComponent)
26021 getPositionEl : function(){
26026 initEvents : function(){
26027 this.originalValue = this.getValue();
26031 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26034 // markInvalid : Roo.emptyFn,
26036 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26039 // clearInvalid : Roo.emptyFn,
26041 setValue : function(v){
26042 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26043 this.editorcore.pushValue();
26048 deferFocus : function(){
26049 this.focus.defer(10, this);
26053 focus : function(){
26054 this.editorcore.focus();
26060 onDestroy : function(){
26066 for (var i =0; i < this.toolbars.length;i++) {
26067 // fixme - ask toolbars for heights?
26068 this.toolbars[i].onDestroy();
26071 this.wrap.dom.innerHTML = '';
26072 this.wrap.remove();
26077 onFirstFocus : function(){
26078 //Roo.log("onFirstFocus");
26079 this.editorcore.onFirstFocus();
26080 for (var i =0; i < this.toolbars.length;i++) {
26081 this.toolbars[i].onFirstFocus();
26087 syncValue : function()
26089 this.editorcore.syncValue();
26092 pushValue : function()
26094 this.editorcore.pushValue();
26098 // hide stuff that is not compatible
26112 * @event specialkey
26116 * @cfg {String} fieldClass @hide
26119 * @cfg {String} focusClass @hide
26122 * @cfg {String} autoCreate @hide
26125 * @cfg {String} inputType @hide
26129 * @cfg {String} invalidText @hide
26132 * @cfg {String} msgFx @hide
26135 * @cfg {String} validateOnBlur @hide
26144 Roo.namespace('Roo.bootstrap.htmleditor');
26146 * @class Roo.bootstrap.HtmlEditorToolbar1
26152 new Roo.bootstrap.HtmlEditor({
26155 new Roo.bootstrap.HtmlEditorToolbar1({
26156 disable : { fonts: 1 , format: 1, ..., ... , ...],
26162 * @cfg {Object} disable List of elements to disable..
26163 * @cfg {Array} btns List of additional buttons.
26167 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26170 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26173 Roo.apply(this, config);
26175 // default disabled, based on 'good practice'..
26176 this.disable = this.disable || {};
26177 Roo.applyIf(this.disable, {
26180 specialElements : true
26182 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26184 this.editor = config.editor;
26185 this.editorcore = config.editor.editorcore;
26187 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26189 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26190 // dont call parent... till later.
26192 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26197 editorcore : false,
26202 "h1","h2","h3","h4","h5","h6",
26204 "abbr", "acronym", "address", "cite", "samp", "var",
26208 onRender : function(ct, position)
26210 // Roo.log("Call onRender: " + this.xtype);
26212 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26214 this.el.dom.style.marginBottom = '0';
26216 var editorcore = this.editorcore;
26217 var editor= this.editor;
26220 var btn = function(id,cmd , toggle, handler, html){
26222 var event = toggle ? 'toggle' : 'click';
26227 xns: Roo.bootstrap,
26231 enableToggle:toggle !== false,
26233 pressed : toggle ? false : null,
26236 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26237 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26243 // var cb_box = function...
26248 xns: Roo.bootstrap,
26253 xns: Roo.bootstrap,
26257 Roo.each(this.formats, function(f) {
26258 style.menu.items.push({
26260 xns: Roo.bootstrap,
26261 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26266 editorcore.insertTag(this.tagname);
26273 children.push(style);
26275 btn('bold',false,true);
26276 btn('italic',false,true);
26277 btn('align-left', 'justifyleft',true);
26278 btn('align-center', 'justifycenter',true);
26279 btn('align-right' , 'justifyright',true);
26280 btn('link', false, false, function(btn) {
26281 //Roo.log("create link?");
26282 var url = prompt(this.createLinkText, this.defaultLinkValue);
26283 if(url && url != 'http:/'+'/'){
26284 this.editorcore.relayCmd('createlink', url);
26287 btn('list','insertunorderedlist',true);
26288 btn('pencil', false,true, function(btn){
26290 this.toggleSourceEdit(btn.pressed);
26293 if (this.editor.btns.length > 0) {
26294 for (var i = 0; i<this.editor.btns.length; i++) {
26295 children.push(this.editor.btns[i]);
26303 xns: Roo.bootstrap,
26308 xns: Roo.bootstrap,
26313 cog.menu.items.push({
26315 xns: Roo.bootstrap,
26316 html : Clean styles,
26321 editorcore.insertTag(this.tagname);
26330 this.xtype = 'NavSimplebar';
26332 for(var i=0;i< children.length;i++) {
26334 this.buttons.add(this.addxtypeChild(children[i]));
26338 editor.on('editorevent', this.updateToolbar, this);
26340 onBtnClick : function(id)
26342 this.editorcore.relayCmd(id);
26343 this.editorcore.focus();
26347 * Protected method that will not generally be called directly. It triggers
26348 * a toolbar update by reading the markup state of the current selection in the editor.
26350 updateToolbar: function(){
26352 if(!this.editorcore.activated){
26353 this.editor.onFirstFocus(); // is this neeed?
26357 var btns = this.buttons;
26358 var doc = this.editorcore.doc;
26359 btns.get('bold').setActive(doc.queryCommandState('bold'));
26360 btns.get('italic').setActive(doc.queryCommandState('italic'));
26361 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26363 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26364 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26365 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26367 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26368 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26371 var ans = this.editorcore.getAllAncestors();
26372 if (this.formatCombo) {
26375 var store = this.formatCombo.store;
26376 this.formatCombo.setValue("");
26377 for (var i =0; i < ans.length;i++) {
26378 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26380 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26388 // hides menus... - so this cant be on a menu...
26389 Roo.bootstrap.MenuMgr.hideAll();
26391 Roo.bootstrap.MenuMgr.hideAll();
26392 //this.editorsyncValue();
26394 onFirstFocus: function() {
26395 this.buttons.each(function(item){
26399 toggleSourceEdit : function(sourceEditMode){
26402 if(sourceEditMode){
26403 Roo.log("disabling buttons");
26404 this.buttons.each( function(item){
26405 if(item.cmd != 'pencil'){
26411 Roo.log("enabling buttons");
26412 if(this.editorcore.initialized){
26413 this.buttons.each( function(item){
26419 Roo.log("calling toggole on editor");
26420 // tell the editor that it's been pressed..
26421 this.editor.toggleSourceEdit(sourceEditMode);
26435 * @class Roo.bootstrap.Markdown
26436 * @extends Roo.bootstrap.TextArea
26437 * Bootstrap Showdown editable area
26438 * @cfg {string} content
26441 * Create a new Showdown
26444 Roo.bootstrap.Markdown = function(config){
26445 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26449 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26453 initEvents : function()
26456 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26457 this.markdownEl = this.el.createChild({
26458 cls : 'roo-markdown-area'
26460 this.inputEl().addClass('d-none');
26461 if (this.getValue() == '') {
26462 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26465 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26467 this.markdownEl.on('click', this.toggleTextEdit, this);
26468 this.on('blur', this.toggleTextEdit, this);
26469 this.on('specialkey', this.resizeTextArea, this);
26472 toggleTextEdit : function()
26474 var sh = this.markdownEl.getHeight();
26475 this.inputEl().addClass('d-none');
26476 this.markdownEl.addClass('d-none');
26477 if (!this.editing) {
26479 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26480 this.inputEl().removeClass('d-none');
26481 this.inputEl().focus();
26482 this.editing = true;
26485 // show showdown...
26486 this.updateMarkdown();
26487 this.markdownEl.removeClass('d-none');
26488 this.editing = false;
26491 updateMarkdown : function()
26493 if (this.getValue() == '') {
26494 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26498 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26501 resizeTextArea: function () {
26504 Roo.log([sh, this.getValue().split("\n").length * 30]);
26505 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26507 setValue : function(val)
26509 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26510 if (!this.editing) {
26511 this.updateMarkdown();
26517 if (!this.editing) {
26518 this.toggleTextEdit();
26526 * @class Roo.bootstrap.Table.AbstractSelectionModel
26527 * @extends Roo.util.Observable
26528 * Abstract base class for grid SelectionModels. It provides the interface that should be
26529 * implemented by descendant classes. This class should not be directly instantiated.
26532 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26533 this.locked = false;
26534 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26538 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26539 /** @ignore Called by the grid automatically. Do not call directly. */
26540 init : function(grid){
26546 * Locks the selections.
26549 this.locked = true;
26553 * Unlocks the selections.
26555 unlock : function(){
26556 this.locked = false;
26560 * Returns true if the selections are locked.
26561 * @return {Boolean}
26563 isLocked : function(){
26564 return this.locked;
26568 initEvents : function ()
26574 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26575 * @class Roo.bootstrap.Table.RowSelectionModel
26576 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26577 * It supports multiple selections and keyboard selection/navigation.
26579 * @param {Object} config
26582 Roo.bootstrap.Table.RowSelectionModel = function(config){
26583 Roo.apply(this, config);
26584 this.selections = new Roo.util.MixedCollection(false, function(o){
26589 this.lastActive = false;
26593 * @event selectionchange
26594 * Fires when the selection changes
26595 * @param {SelectionModel} this
26597 "selectionchange" : true,
26599 * @event afterselectionchange
26600 * Fires after the selection changes (eg. by key press or clicking)
26601 * @param {SelectionModel} this
26603 "afterselectionchange" : true,
26605 * @event beforerowselect
26606 * Fires when a row is selected being selected, return false to cancel.
26607 * @param {SelectionModel} this
26608 * @param {Number} rowIndex The selected index
26609 * @param {Boolean} keepExisting False if other selections will be cleared
26611 "beforerowselect" : true,
26614 * Fires when a row is selected.
26615 * @param {SelectionModel} this
26616 * @param {Number} rowIndex The selected index
26617 * @param {Roo.data.Record} r The record
26619 "rowselect" : true,
26621 * @event rowdeselect
26622 * Fires when a row is deselected.
26623 * @param {SelectionModel} this
26624 * @param {Number} rowIndex The selected index
26626 "rowdeselect" : true
26628 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26629 this.locked = false;
26632 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26634 * @cfg {Boolean} singleSelect
26635 * True to allow selection of only one row at a time (defaults to false)
26637 singleSelect : false,
26640 initEvents : function()
26643 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26644 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26645 //}else{ // allow click to work like normal
26646 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26648 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26649 this.grid.on("rowclick", this.handleMouseDown, this);
26651 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26652 "up" : function(e){
26654 this.selectPrevious(e.shiftKey);
26655 }else if(this.last !== false && this.lastActive !== false){
26656 var last = this.last;
26657 this.selectRange(this.last, this.lastActive-1);
26658 this.grid.getView().focusRow(this.lastActive);
26659 if(last !== false){
26663 this.selectFirstRow();
26665 this.fireEvent("afterselectionchange", this);
26667 "down" : function(e){
26669 this.selectNext(e.shiftKey);
26670 }else if(this.last !== false && this.lastActive !== false){
26671 var last = this.last;
26672 this.selectRange(this.last, this.lastActive+1);
26673 this.grid.getView().focusRow(this.lastActive);
26674 if(last !== false){
26678 this.selectFirstRow();
26680 this.fireEvent("afterselectionchange", this);
26684 this.grid.store.on('load', function(){
26685 this.selections.clear();
26688 var view = this.grid.view;
26689 view.on("refresh", this.onRefresh, this);
26690 view.on("rowupdated", this.onRowUpdated, this);
26691 view.on("rowremoved", this.onRemove, this);
26696 onRefresh : function()
26698 var ds = this.grid.store, i, v = this.grid.view;
26699 var s = this.selections;
26700 s.each(function(r){
26701 if((i = ds.indexOfId(r.id)) != -1){
26710 onRemove : function(v, index, r){
26711 this.selections.remove(r);
26715 onRowUpdated : function(v, index, r){
26716 if(this.isSelected(r)){
26717 v.onRowSelect(index);
26723 * @param {Array} records The records to select
26724 * @param {Boolean} keepExisting (optional) True to keep existing selections
26726 selectRecords : function(records, keepExisting)
26729 this.clearSelections();
26731 var ds = this.grid.store;
26732 for(var i = 0, len = records.length; i < len; i++){
26733 this.selectRow(ds.indexOf(records[i]), true);
26738 * Gets the number of selected rows.
26741 getCount : function(){
26742 return this.selections.length;
26746 * Selects the first row in the grid.
26748 selectFirstRow : function(){
26753 * Select the last row.
26754 * @param {Boolean} keepExisting (optional) True to keep existing selections
26756 selectLastRow : function(keepExisting){
26757 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26758 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26762 * Selects the row immediately following the last selected row.
26763 * @param {Boolean} keepExisting (optional) True to keep existing selections
26765 selectNext : function(keepExisting)
26767 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26768 this.selectRow(this.last+1, keepExisting);
26769 this.grid.getView().focusRow(this.last);
26774 * Selects the row that precedes the last selected row.
26775 * @param {Boolean} keepExisting (optional) True to keep existing selections
26777 selectPrevious : function(keepExisting){
26779 this.selectRow(this.last-1, keepExisting);
26780 this.grid.getView().focusRow(this.last);
26785 * Returns the selected records
26786 * @return {Array} Array of selected records
26788 getSelections : function(){
26789 return [].concat(this.selections.items);
26793 * Returns the first selected record.
26796 getSelected : function(){
26797 return this.selections.itemAt(0);
26802 * Clears all selections.
26804 clearSelections : function(fast)
26810 var ds = this.grid.store;
26811 var s = this.selections;
26812 s.each(function(r){
26813 this.deselectRow(ds.indexOfId(r.id));
26817 this.selections.clear();
26824 * Selects all rows.
26826 selectAll : function(){
26830 this.selections.clear();
26831 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26832 this.selectRow(i, true);
26837 * Returns True if there is a selection.
26838 * @return {Boolean}
26840 hasSelection : function(){
26841 return this.selections.length > 0;
26845 * Returns True if the specified row is selected.
26846 * @param {Number/Record} record The record or index of the record to check
26847 * @return {Boolean}
26849 isSelected : function(index){
26850 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26851 return (r && this.selections.key(r.id) ? true : false);
26855 * Returns True if the specified record id is selected.
26856 * @param {String} id The id of record to check
26857 * @return {Boolean}
26859 isIdSelected : function(id){
26860 return (this.selections.key(id) ? true : false);
26865 handleMouseDBClick : function(e, t){
26869 handleMouseDown : function(e, t)
26871 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26872 if(this.isLocked() || rowIndex < 0 ){
26875 if(e.shiftKey && this.last !== false){
26876 var last = this.last;
26877 this.selectRange(last, rowIndex, e.ctrlKey);
26878 this.last = last; // reset the last
26882 var isSelected = this.isSelected(rowIndex);
26883 //Roo.log("select row:" + rowIndex);
26885 this.deselectRow(rowIndex);
26887 this.selectRow(rowIndex, true);
26891 if(e.button !== 0 && isSelected){
26892 alert('rowIndex 2: ' + rowIndex);
26893 view.focusRow(rowIndex);
26894 }else if(e.ctrlKey && isSelected){
26895 this.deselectRow(rowIndex);
26896 }else if(!isSelected){
26897 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26898 view.focusRow(rowIndex);
26902 this.fireEvent("afterselectionchange", this);
26905 handleDragableRowClick : function(grid, rowIndex, e)
26907 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26908 this.selectRow(rowIndex, false);
26909 grid.view.focusRow(rowIndex);
26910 this.fireEvent("afterselectionchange", this);
26915 * Selects multiple rows.
26916 * @param {Array} rows Array of the indexes of the row to select
26917 * @param {Boolean} keepExisting (optional) True to keep existing selections
26919 selectRows : function(rows, keepExisting){
26921 this.clearSelections();
26923 for(var i = 0, len = rows.length; i < len; i++){
26924 this.selectRow(rows[i], true);
26929 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26930 * @param {Number} startRow The index of the first row in the range
26931 * @param {Number} endRow The index of the last row in the range
26932 * @param {Boolean} keepExisting (optional) True to retain existing selections
26934 selectRange : function(startRow, endRow, keepExisting){
26939 this.clearSelections();
26941 if(startRow <= endRow){
26942 for(var i = startRow; i <= endRow; i++){
26943 this.selectRow(i, true);
26946 for(var i = startRow; i >= endRow; i--){
26947 this.selectRow(i, true);
26953 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26954 * @param {Number} startRow The index of the first row in the range
26955 * @param {Number} endRow The index of the last row in the range
26957 deselectRange : function(startRow, endRow, preventViewNotify){
26961 for(var i = startRow; i <= endRow; i++){
26962 this.deselectRow(i, preventViewNotify);
26968 * @param {Number} row The index of the row to select
26969 * @param {Boolean} keepExisting (optional) True to keep existing selections
26971 selectRow : function(index, keepExisting, preventViewNotify)
26973 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26976 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26977 if(!keepExisting || this.singleSelect){
26978 this.clearSelections();
26981 var r = this.grid.store.getAt(index);
26982 //console.log('selectRow - record id :' + r.id);
26984 this.selections.add(r);
26985 this.last = this.lastActive = index;
26986 if(!preventViewNotify){
26987 var proxy = new Roo.Element(
26988 this.grid.getRowDom(index)
26990 proxy.addClass('bg-info info');
26992 this.fireEvent("rowselect", this, index, r);
26993 this.fireEvent("selectionchange", this);
26999 * @param {Number} row The index of the row to deselect
27001 deselectRow : function(index, preventViewNotify)
27006 if(this.last == index){
27009 if(this.lastActive == index){
27010 this.lastActive = false;
27013 var r = this.grid.store.getAt(index);
27018 this.selections.remove(r);
27019 //.console.log('deselectRow - record id :' + r.id);
27020 if(!preventViewNotify){
27022 var proxy = new Roo.Element(
27023 this.grid.getRowDom(index)
27025 proxy.removeClass('bg-info info');
27027 this.fireEvent("rowdeselect", this, index);
27028 this.fireEvent("selectionchange", this);
27032 restoreLast : function(){
27034 this.last = this._last;
27039 acceptsNav : function(row, col, cm){
27040 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27044 onEditorKey : function(field, e){
27045 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27050 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27052 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27054 }else if(k == e.ENTER && !e.ctrlKey){
27058 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27060 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27062 }else if(k == e.ESC){
27066 g.startEditing(newCell[0], newCell[1]);
27072 * Ext JS Library 1.1.1
27073 * Copyright(c) 2006-2007, Ext JS, LLC.
27075 * Originally Released Under LGPL - original licence link has changed is not relivant.
27078 * <script type="text/javascript">
27082 * @class Roo.bootstrap.PagingToolbar
27083 * @extends Roo.bootstrap.NavSimplebar
27084 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27086 * Create a new PagingToolbar
27087 * @param {Object} config The config object
27088 * @param {Roo.data.Store} store
27090 Roo.bootstrap.PagingToolbar = function(config)
27092 // old args format still supported... - xtype is prefered..
27093 // created from xtype...
27095 this.ds = config.dataSource;
27097 if (config.store && !this.ds) {
27098 this.store= Roo.factory(config.store, Roo.data);
27099 this.ds = this.store;
27100 this.ds.xmodule = this.xmodule || false;
27103 this.toolbarItems = [];
27104 if (config.items) {
27105 this.toolbarItems = config.items;
27108 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27113 this.bind(this.ds);
27116 if (Roo.bootstrap.version == 4) {
27117 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27119 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27124 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27126 * @cfg {Roo.data.Store} dataSource
27127 * The underlying data store providing the paged data
27130 * @cfg {String/HTMLElement/Element} container
27131 * container The id or element that will contain the toolbar
27134 * @cfg {Boolean} displayInfo
27135 * True to display the displayMsg (defaults to false)
27138 * @cfg {Number} pageSize
27139 * The number of records to display per page (defaults to 20)
27143 * @cfg {String} displayMsg
27144 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27146 displayMsg : 'Displaying {0} - {1} of {2}',
27148 * @cfg {String} emptyMsg
27149 * The message to display when no records are found (defaults to "No data to display")
27151 emptyMsg : 'No data to display',
27153 * Customizable piece of the default paging text (defaults to "Page")
27156 beforePageText : "Page",
27158 * Customizable piece of the default paging text (defaults to "of %0")
27161 afterPageText : "of {0}",
27163 * Customizable piece of the default paging text (defaults to "First Page")
27166 firstText : "First Page",
27168 * Customizable piece of the default paging text (defaults to "Previous Page")
27171 prevText : "Previous Page",
27173 * Customizable piece of the default paging text (defaults to "Next Page")
27176 nextText : "Next Page",
27178 * Customizable piece of the default paging text (defaults to "Last Page")
27181 lastText : "Last Page",
27183 * Customizable piece of the default paging text (defaults to "Refresh")
27186 refreshText : "Refresh",
27190 onRender : function(ct, position)
27192 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27193 this.navgroup.parentId = this.id;
27194 this.navgroup.onRender(this.el, null);
27195 // add the buttons to the navgroup
27197 if(this.displayInfo){
27198 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27199 this.displayEl = this.el.select('.x-paging-info', true).first();
27200 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27201 // this.displayEl = navel.el.select('span',true).first();
27207 Roo.each(_this.buttons, function(e){ // this might need to use render????
27208 Roo.factory(e).render(_this.el);
27212 Roo.each(_this.toolbarItems, function(e) {
27213 _this.navgroup.addItem(e);
27217 this.first = this.navgroup.addItem({
27218 tooltip: this.firstText,
27219 cls: "prev btn-outline-secondary",
27220 html : ' <i class="fa fa-step-backward"></i>',
27222 preventDefault: true,
27223 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27226 this.prev = this.navgroup.addItem({
27227 tooltip: this.prevText,
27228 cls: "prev btn-outline-secondary",
27229 html : ' <i class="fa fa-backward"></i>',
27231 preventDefault: true,
27232 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27234 //this.addSeparator();
27237 var field = this.navgroup.addItem( {
27239 cls : 'x-paging-position btn-outline-secondary',
27241 html : this.beforePageText +
27242 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27243 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27246 this.field = field.el.select('input', true).first();
27247 this.field.on("keydown", this.onPagingKeydown, this);
27248 this.field.on("focus", function(){this.dom.select();});
27251 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27252 //this.field.setHeight(18);
27253 //this.addSeparator();
27254 this.next = this.navgroup.addItem({
27255 tooltip: this.nextText,
27256 cls: "next btn-outline-secondary",
27257 html : ' <i class="fa fa-forward"></i>',
27259 preventDefault: true,
27260 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27262 this.last = this.navgroup.addItem({
27263 tooltip: this.lastText,
27264 html : ' <i class="fa fa-step-forward"></i>',
27265 cls: "next btn-outline-secondary",
27267 preventDefault: true,
27268 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27270 //this.addSeparator();
27271 this.loading = this.navgroup.addItem({
27272 tooltip: this.refreshText,
27273 cls: "btn-outline-secondary",
27274 html : ' <i class="fa fa-refresh"></i>',
27275 preventDefault: true,
27276 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27282 updateInfo : function(){
27283 if(this.displayEl){
27284 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27285 var msg = count == 0 ?
27289 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27291 this.displayEl.update(msg);
27296 onLoad : function(ds, r, o)
27298 this.cursor = o.params.start ? o.params.start : 0;
27300 var d = this.getPageData(),
27305 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27306 this.field.dom.value = ap;
27307 this.first.setDisabled(ap == 1);
27308 this.prev.setDisabled(ap == 1);
27309 this.next.setDisabled(ap == ps);
27310 this.last.setDisabled(ap == ps);
27311 this.loading.enable();
27316 getPageData : function(){
27317 var total = this.ds.getTotalCount();
27320 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27321 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27326 onLoadError : function(){
27327 this.loading.enable();
27331 onPagingKeydown : function(e){
27332 var k = e.getKey();
27333 var d = this.getPageData();
27335 var v = this.field.dom.value, pageNum;
27336 if(!v || isNaN(pageNum = parseInt(v, 10))){
27337 this.field.dom.value = d.activePage;
27340 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27341 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27344 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))
27346 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27347 this.field.dom.value = pageNum;
27348 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27351 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27353 var v = this.field.dom.value, pageNum;
27354 var increment = (e.shiftKey) ? 10 : 1;
27355 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27358 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27359 this.field.dom.value = d.activePage;
27362 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27364 this.field.dom.value = parseInt(v, 10) + increment;
27365 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27366 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27373 beforeLoad : function(){
27375 this.loading.disable();
27380 onClick : function(which){
27389 ds.load({params:{start: 0, limit: this.pageSize}});
27392 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27395 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27398 var total = ds.getTotalCount();
27399 var extra = total % this.pageSize;
27400 var lastStart = extra ? (total - extra) : total-this.pageSize;
27401 ds.load({params:{start: lastStart, limit: this.pageSize}});
27404 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27410 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27411 * @param {Roo.data.Store} store The data store to unbind
27413 unbind : function(ds){
27414 ds.un("beforeload", this.beforeLoad, this);
27415 ds.un("load", this.onLoad, this);
27416 ds.un("loadexception", this.onLoadError, this);
27417 ds.un("remove", this.updateInfo, this);
27418 ds.un("add", this.updateInfo, this);
27419 this.ds = undefined;
27423 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27424 * @param {Roo.data.Store} store The data store to bind
27426 bind : function(ds){
27427 ds.on("beforeload", this.beforeLoad, this);
27428 ds.on("load", this.onLoad, this);
27429 ds.on("loadexception", this.onLoadError, this);
27430 ds.on("remove", this.updateInfo, this);
27431 ds.on("add", this.updateInfo, this);
27442 * @class Roo.bootstrap.MessageBar
27443 * @extends Roo.bootstrap.Component
27444 * Bootstrap MessageBar class
27445 * @cfg {String} html contents of the MessageBar
27446 * @cfg {String} weight (info | success | warning | danger) default info
27447 * @cfg {String} beforeClass insert the bar before the given class
27448 * @cfg {Boolean} closable (true | false) default false
27449 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27452 * Create a new Element
27453 * @param {Object} config The config object
27456 Roo.bootstrap.MessageBar = function(config){
27457 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27460 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27466 beforeClass: 'bootstrap-sticky-wrap',
27468 getAutoCreate : function(){
27472 cls: 'alert alert-dismissable alert-' + this.weight,
27477 html: this.html || ''
27483 cfg.cls += ' alert-messages-fixed';
27497 onRender : function(ct, position)
27499 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27502 var cfg = Roo.apply({}, this.getAutoCreate());
27506 cfg.cls += ' ' + this.cls;
27509 cfg.style = this.style;
27511 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27513 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27516 this.el.select('>button.close').on('click', this.hide, this);
27522 if (!this.rendered) {
27528 this.fireEvent('show', this);
27534 if (!this.rendered) {
27540 this.fireEvent('hide', this);
27543 update : function()
27545 // var e = this.el.dom.firstChild;
27547 // if(this.closable){
27548 // e = e.nextSibling;
27551 // e.data = this.html || '';
27553 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27569 * @class Roo.bootstrap.Graph
27570 * @extends Roo.bootstrap.Component
27571 * Bootstrap Graph class
27575 @cfg {String} graphtype bar | vbar | pie
27576 @cfg {number} g_x coodinator | centre x (pie)
27577 @cfg {number} g_y coodinator | centre y (pie)
27578 @cfg {number} g_r radius (pie)
27579 @cfg {number} g_height height of the chart (respected by all elements in the set)
27580 @cfg {number} g_width width of the chart (respected by all elements in the set)
27581 @cfg {Object} title The title of the chart
27584 -opts (object) options for the chart
27586 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27587 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27589 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.
27590 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27592 o stretch (boolean)
27594 -opts (object) options for the pie
27597 o startAngle (number)
27598 o endAngle (number)
27602 * Create a new Input
27603 * @param {Object} config The config object
27606 Roo.bootstrap.Graph = function(config){
27607 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27613 * The img click event for the img.
27614 * @param {Roo.EventObject} e
27620 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27631 //g_colors: this.colors,
27638 getAutoCreate : function(){
27649 onRender : function(ct,position){
27652 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27654 if (typeof(Raphael) == 'undefined') {
27655 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27659 this.raphael = Raphael(this.el.dom);
27661 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27662 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27663 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27664 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27666 r.text(160, 10, "Single Series Chart").attr(txtattr);
27667 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27668 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27669 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27671 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27672 r.barchart(330, 10, 300, 220, data1);
27673 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27674 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27677 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27678 // r.barchart(30, 30, 560, 250, xdata, {
27679 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27680 // axis : "0 0 1 1",
27681 // axisxlabels : xdata
27682 // //yvalues : cols,
27685 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27687 // this.load(null,xdata,{
27688 // axis : "0 0 1 1",
27689 // axisxlabels : xdata
27694 load : function(graphtype,xdata,opts)
27696 this.raphael.clear();
27698 graphtype = this.graphtype;
27703 var r = this.raphael,
27704 fin = function () {
27705 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27707 fout = function () {
27708 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27710 pfin = function() {
27711 this.sector.stop();
27712 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27715 this.label[0].stop();
27716 this.label[0].attr({ r: 7.5 });
27717 this.label[1].attr({ "font-weight": 800 });
27720 pfout = function() {
27721 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27724 this.label[0].animate({ r: 5 }, 500, "bounce");
27725 this.label[1].attr({ "font-weight": 400 });
27731 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27734 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27737 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27738 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27740 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27747 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27752 setTitle: function(o)
27757 initEvents: function() {
27760 this.el.on('click', this.onClick, this);
27764 onClick : function(e)
27766 Roo.log('img onclick');
27767 this.fireEvent('click', this, e);
27779 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27782 * @class Roo.bootstrap.dash.NumberBox
27783 * @extends Roo.bootstrap.Component
27784 * Bootstrap NumberBox class
27785 * @cfg {String} headline Box headline
27786 * @cfg {String} content Box content
27787 * @cfg {String} icon Box icon
27788 * @cfg {String} footer Footer text
27789 * @cfg {String} fhref Footer href
27792 * Create a new NumberBox
27793 * @param {Object} config The config object
27797 Roo.bootstrap.dash.NumberBox = function(config){
27798 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27802 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27811 getAutoCreate : function(){
27815 cls : 'small-box ',
27823 cls : 'roo-headline',
27824 html : this.headline
27828 cls : 'roo-content',
27829 html : this.content
27843 cls : 'ion ' + this.icon
27852 cls : 'small-box-footer',
27853 href : this.fhref || '#',
27857 cfg.cn.push(footer);
27864 onRender : function(ct,position){
27865 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27872 setHeadline: function (value)
27874 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27877 setFooter: function (value, href)
27879 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27882 this.el.select('a.small-box-footer',true).first().attr('href', href);
27887 setContent: function (value)
27889 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27892 initEvents: function()
27906 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27909 * @class Roo.bootstrap.dash.TabBox
27910 * @extends Roo.bootstrap.Component
27911 * Bootstrap TabBox class
27912 * @cfg {String} title Title of the TabBox
27913 * @cfg {String} icon Icon of the TabBox
27914 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27915 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27918 * Create a new TabBox
27919 * @param {Object} config The config object
27923 Roo.bootstrap.dash.TabBox = function(config){
27924 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27929 * When a pane is added
27930 * @param {Roo.bootstrap.dash.TabPane} pane
27934 * @event activatepane
27935 * When a pane is activated
27936 * @param {Roo.bootstrap.dash.TabPane} pane
27938 "activatepane" : true
27946 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27951 tabScrollable : false,
27953 getChildContainer : function()
27955 return this.el.select('.tab-content', true).first();
27958 getAutoCreate : function(){
27962 cls: 'pull-left header',
27970 cls: 'fa ' + this.icon
27976 cls: 'nav nav-tabs pull-right',
27982 if(this.tabScrollable){
27989 cls: 'nav nav-tabs pull-right',
28000 cls: 'nav-tabs-custom',
28005 cls: 'tab-content no-padding',
28013 initEvents : function()
28015 //Roo.log('add add pane handler');
28016 this.on('addpane', this.onAddPane, this);
28019 * Updates the box title
28020 * @param {String} html to set the title to.
28022 setTitle : function(value)
28024 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28026 onAddPane : function(pane)
28028 this.panes.push(pane);
28029 //Roo.log('addpane');
28031 // tabs are rendere left to right..
28032 if(!this.showtabs){
28036 var ctr = this.el.select('.nav-tabs', true).first();
28039 var existing = ctr.select('.nav-tab',true);
28040 var qty = existing.getCount();;
28043 var tab = ctr.createChild({
28045 cls : 'nav-tab' + (qty ? '' : ' active'),
28053 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28056 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28058 pane.el.addClass('active');
28063 onTabClick : function(ev,un,ob,pane)
28065 //Roo.log('tab - prev default');
28066 ev.preventDefault();
28069 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28070 pane.tab.addClass('active');
28071 //Roo.log(pane.title);
28072 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28073 // technically we should have a deactivate event.. but maybe add later.
28074 // and it should not de-activate the selected tab...
28075 this.fireEvent('activatepane', pane);
28076 pane.el.addClass('active');
28077 pane.fireEvent('activate');
28082 getActivePane : function()
28085 Roo.each(this.panes, function(p) {
28086 if(p.el.hasClass('active')){
28107 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28109 * @class Roo.bootstrap.TabPane
28110 * @extends Roo.bootstrap.Component
28111 * Bootstrap TabPane class
28112 * @cfg {Boolean} active (false | true) Default false
28113 * @cfg {String} title title of panel
28117 * Create a new TabPane
28118 * @param {Object} config The config object
28121 Roo.bootstrap.dash.TabPane = function(config){
28122 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28128 * When a pane is activated
28129 * @param {Roo.bootstrap.dash.TabPane} pane
28136 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28141 // the tabBox that this is attached to.
28144 getAutoCreate : function()
28152 cfg.cls += ' active';
28157 initEvents : function()
28159 //Roo.log('trigger add pane handler');
28160 this.parent().fireEvent('addpane', this)
28164 * Updates the tab title
28165 * @param {String} html to set the title to.
28167 setTitle: function(str)
28173 this.tab.select('a', true).first().dom.innerHTML = str;
28190 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28193 * @class Roo.bootstrap.menu.Menu
28194 * @extends Roo.bootstrap.Component
28195 * Bootstrap Menu class - container for Menu
28196 * @cfg {String} html Text of the menu
28197 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28198 * @cfg {String} icon Font awesome icon
28199 * @cfg {String} pos Menu align to (top | bottom) default bottom
28203 * Create a new Menu
28204 * @param {Object} config The config object
28208 Roo.bootstrap.menu.Menu = function(config){
28209 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28213 * @event beforeshow
28214 * Fires before this menu is displayed
28215 * @param {Roo.bootstrap.menu.Menu} this
28219 * @event beforehide
28220 * Fires before this menu is hidden
28221 * @param {Roo.bootstrap.menu.Menu} this
28226 * Fires after this menu is displayed
28227 * @param {Roo.bootstrap.menu.Menu} this
28232 * Fires after this menu is hidden
28233 * @param {Roo.bootstrap.menu.Menu} this
28238 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28239 * @param {Roo.bootstrap.menu.Menu} this
28240 * @param {Roo.EventObject} e
28247 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28251 weight : 'default',
28256 getChildContainer : function() {
28257 if(this.isSubMenu){
28261 return this.el.select('ul.dropdown-menu', true).first();
28264 getAutoCreate : function()
28269 cls : 'roo-menu-text',
28277 cls : 'fa ' + this.icon
28288 cls : 'dropdown-button btn btn-' + this.weight,
28293 cls : 'dropdown-toggle btn btn-' + this.weight,
28303 cls : 'dropdown-menu'
28309 if(this.pos == 'top'){
28310 cfg.cls += ' dropup';
28313 if(this.isSubMenu){
28316 cls : 'dropdown-menu'
28323 onRender : function(ct, position)
28325 this.isSubMenu = ct.hasClass('dropdown-submenu');
28327 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28330 initEvents : function()
28332 if(this.isSubMenu){
28336 this.hidden = true;
28338 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28339 this.triggerEl.on('click', this.onTriggerPress, this);
28341 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28342 this.buttonEl.on('click', this.onClick, this);
28348 if(this.isSubMenu){
28352 return this.el.select('ul.dropdown-menu', true).first();
28355 onClick : function(e)
28357 this.fireEvent("click", this, e);
28360 onTriggerPress : function(e)
28362 if (this.isVisible()) {
28369 isVisible : function(){
28370 return !this.hidden;
28375 this.fireEvent("beforeshow", this);
28377 this.hidden = false;
28378 this.el.addClass('open');
28380 Roo.get(document).on("mouseup", this.onMouseUp, this);
28382 this.fireEvent("show", this);
28389 this.fireEvent("beforehide", this);
28391 this.hidden = true;
28392 this.el.removeClass('open');
28394 Roo.get(document).un("mouseup", this.onMouseUp);
28396 this.fireEvent("hide", this);
28399 onMouseUp : function()
28413 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28416 * @class Roo.bootstrap.menu.Item
28417 * @extends Roo.bootstrap.Component
28418 * Bootstrap MenuItem class
28419 * @cfg {Boolean} submenu (true | false) default false
28420 * @cfg {String} html text of the item
28421 * @cfg {String} href the link
28422 * @cfg {Boolean} disable (true | false) default false
28423 * @cfg {Boolean} preventDefault (true | false) default true
28424 * @cfg {String} icon Font awesome icon
28425 * @cfg {String} pos Submenu align to (left | right) default right
28429 * Create a new Item
28430 * @param {Object} config The config object
28434 Roo.bootstrap.menu.Item = function(config){
28435 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28439 * Fires when the mouse is hovering over this menu
28440 * @param {Roo.bootstrap.menu.Item} this
28441 * @param {Roo.EventObject} e
28446 * Fires when the mouse exits this menu
28447 * @param {Roo.bootstrap.menu.Item} this
28448 * @param {Roo.EventObject} e
28454 * The raw click event for the entire grid.
28455 * @param {Roo.EventObject} e
28461 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28466 preventDefault: true,
28471 getAutoCreate : function()
28476 cls : 'roo-menu-item-text',
28484 cls : 'fa ' + this.icon
28493 href : this.href || '#',
28500 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28504 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28506 if(this.pos == 'left'){
28507 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28514 initEvents : function()
28516 this.el.on('mouseover', this.onMouseOver, this);
28517 this.el.on('mouseout', this.onMouseOut, this);
28519 this.el.select('a', true).first().on('click', this.onClick, this);
28523 onClick : function(e)
28525 if(this.preventDefault){
28526 e.preventDefault();
28529 this.fireEvent("click", this, e);
28532 onMouseOver : function(e)
28534 if(this.submenu && this.pos == 'left'){
28535 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28538 this.fireEvent("mouseover", this, e);
28541 onMouseOut : function(e)
28543 this.fireEvent("mouseout", this, e);
28555 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28558 * @class Roo.bootstrap.menu.Separator
28559 * @extends Roo.bootstrap.Component
28560 * Bootstrap Separator class
28563 * Create a new Separator
28564 * @param {Object} config The config object
28568 Roo.bootstrap.menu.Separator = function(config){
28569 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28572 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28574 getAutoCreate : function(){
28595 * @class Roo.bootstrap.Tooltip
28596 * Bootstrap Tooltip class
28597 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28598 * to determine which dom element triggers the tooltip.
28600 * It needs to add support for additional attributes like tooltip-position
28603 * Create a new Toolti
28604 * @param {Object} config The config object
28607 Roo.bootstrap.Tooltip = function(config){
28608 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28610 this.alignment = Roo.bootstrap.Tooltip.alignment;
28612 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28613 this.alignment = config.alignment;
28618 Roo.apply(Roo.bootstrap.Tooltip, {
28620 * @function init initialize tooltip monitoring.
28624 currentTip : false,
28625 currentRegion : false,
28631 Roo.get(document).on('mouseover', this.enter ,this);
28632 Roo.get(document).on('mouseout', this.leave, this);
28635 this.currentTip = new Roo.bootstrap.Tooltip();
28638 enter : function(ev)
28640 var dom = ev.getTarget();
28642 //Roo.log(['enter',dom]);
28643 var el = Roo.fly(dom);
28644 if (this.currentEl) {
28646 //Roo.log(this.currentEl);
28647 //Roo.log(this.currentEl.contains(dom));
28648 if (this.currentEl == el) {
28651 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28657 if (this.currentTip.el) {
28658 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28662 if(!el || el.dom == document){
28668 // you can not look for children, as if el is the body.. then everythign is the child..
28669 if (!el.attr('tooltip')) { //
28670 if (!el.select("[tooltip]").elements.length) {
28673 // is the mouse over this child...?
28674 bindEl = el.select("[tooltip]").first();
28675 var xy = ev.getXY();
28676 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28677 //Roo.log("not in region.");
28680 //Roo.log("child element over..");
28683 this.currentEl = bindEl;
28684 this.currentTip.bind(bindEl);
28685 this.currentRegion = Roo.lib.Region.getRegion(dom);
28686 this.currentTip.enter();
28689 leave : function(ev)
28691 var dom = ev.getTarget();
28692 //Roo.log(['leave',dom]);
28693 if (!this.currentEl) {
28698 if (dom != this.currentEl.dom) {
28701 var xy = ev.getXY();
28702 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28705 // only activate leave if mouse cursor is outside... bounding box..
28710 if (this.currentTip) {
28711 this.currentTip.leave();
28713 //Roo.log('clear currentEl');
28714 this.currentEl = false;
28719 'left' : ['r-l', [-2,0], 'right'],
28720 'right' : ['l-r', [2,0], 'left'],
28721 'bottom' : ['t-b', [0,2], 'top'],
28722 'top' : [ 'b-t', [0,-2], 'bottom']
28728 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28733 delay : null, // can be { show : 300 , hide: 500}
28737 hoverState : null, //???
28739 placement : 'bottom',
28743 getAutoCreate : function(){
28750 cls : 'tooltip-arrow arrow'
28753 cls : 'tooltip-inner'
28760 bind : function(el)
28765 initEvents : function()
28767 this.arrowEl = this.el.select('.arrow', true).first();
28768 this.innerEl = this.el.select('.tooltip-inner', true).first();
28771 enter : function () {
28773 if (this.timeout != null) {
28774 clearTimeout(this.timeout);
28777 this.hoverState = 'in';
28778 //Roo.log("enter - show");
28779 if (!this.delay || !this.delay.show) {
28784 this.timeout = setTimeout(function () {
28785 if (_t.hoverState == 'in') {
28788 }, this.delay.show);
28792 clearTimeout(this.timeout);
28794 this.hoverState = 'out';
28795 if (!this.delay || !this.delay.hide) {
28801 this.timeout = setTimeout(function () {
28802 //Roo.log("leave - timeout");
28804 if (_t.hoverState == 'out') {
28806 Roo.bootstrap.Tooltip.currentEl = false;
28811 show : function (msg)
28814 this.render(document.body);
28817 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28819 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28821 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28823 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28824 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28826 var placement = typeof this.placement == 'function' ?
28827 this.placement.call(this, this.el, on_el) :
28830 var autoToken = /\s?auto?\s?/i;
28831 var autoPlace = autoToken.test(placement);
28833 placement = placement.replace(autoToken, '') || 'top';
28837 //this.el.setXY([0,0]);
28839 //this.el.dom.style.display='block';
28841 //this.el.appendTo(on_el);
28843 var p = this.getPosition();
28844 var box = this.el.getBox();
28850 var align = this.alignment[placement];
28852 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28854 if(placement == 'top' || placement == 'bottom'){
28856 placement = 'right';
28859 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28860 placement = 'left';
28863 var scroll = Roo.select('body', true).first().getScroll();
28865 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28869 align = this.alignment[placement];
28871 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28875 this.el.alignTo(this.bindEl, align[0],align[1]);
28876 //var arrow = this.el.select('.arrow',true).first();
28877 //arrow.set(align[2],
28879 this.el.addClass(placement);
28880 this.el.addClass("bs-tooltip-"+ placement);
28882 this.el.addClass('in fade show');
28884 this.hoverState = null;
28886 if (this.el.hasClass('fade')) {
28901 //this.el.setXY([0,0]);
28902 this.el.removeClass(['show', 'in']);
28918 * @class Roo.bootstrap.LocationPicker
28919 * @extends Roo.bootstrap.Component
28920 * Bootstrap LocationPicker class
28921 * @cfg {Number} latitude Position when init default 0
28922 * @cfg {Number} longitude Position when init default 0
28923 * @cfg {Number} zoom default 15
28924 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28925 * @cfg {Boolean} mapTypeControl default false
28926 * @cfg {Boolean} disableDoubleClickZoom default false
28927 * @cfg {Boolean} scrollwheel default true
28928 * @cfg {Boolean} streetViewControl default false
28929 * @cfg {Number} radius default 0
28930 * @cfg {String} locationName
28931 * @cfg {Boolean} draggable default true
28932 * @cfg {Boolean} enableAutocomplete default false
28933 * @cfg {Boolean} enableReverseGeocode default true
28934 * @cfg {String} markerTitle
28937 * Create a new LocationPicker
28938 * @param {Object} config The config object
28942 Roo.bootstrap.LocationPicker = function(config){
28944 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28949 * Fires when the picker initialized.
28950 * @param {Roo.bootstrap.LocationPicker} this
28951 * @param {Google Location} location
28955 * @event positionchanged
28956 * Fires when the picker position changed.
28957 * @param {Roo.bootstrap.LocationPicker} this
28958 * @param {Google Location} location
28960 positionchanged : true,
28963 * Fires when the map resize.
28964 * @param {Roo.bootstrap.LocationPicker} this
28969 * Fires when the map show.
28970 * @param {Roo.bootstrap.LocationPicker} this
28975 * Fires when the map hide.
28976 * @param {Roo.bootstrap.LocationPicker} this
28981 * Fires when click the map.
28982 * @param {Roo.bootstrap.LocationPicker} this
28983 * @param {Map event} e
28987 * @event mapRightClick
28988 * Fires when right click the map.
28989 * @param {Roo.bootstrap.LocationPicker} this
28990 * @param {Map event} e
28992 mapRightClick : true,
28994 * @event markerClick
28995 * Fires when click the marker.
28996 * @param {Roo.bootstrap.LocationPicker} this
28997 * @param {Map event} e
28999 markerClick : true,
29001 * @event markerRightClick
29002 * Fires when right click the marker.
29003 * @param {Roo.bootstrap.LocationPicker} this
29004 * @param {Map event} e
29006 markerRightClick : true,
29008 * @event OverlayViewDraw
29009 * Fires when OverlayView Draw
29010 * @param {Roo.bootstrap.LocationPicker} this
29012 OverlayViewDraw : true,
29014 * @event OverlayViewOnAdd
29015 * Fires when OverlayView Draw
29016 * @param {Roo.bootstrap.LocationPicker} this
29018 OverlayViewOnAdd : true,
29020 * @event OverlayViewOnRemove
29021 * Fires when OverlayView Draw
29022 * @param {Roo.bootstrap.LocationPicker} this
29024 OverlayViewOnRemove : true,
29026 * @event OverlayViewShow
29027 * Fires when OverlayView Draw
29028 * @param {Roo.bootstrap.LocationPicker} this
29029 * @param {Pixel} cpx
29031 OverlayViewShow : true,
29033 * @event OverlayViewHide
29034 * Fires when OverlayView Draw
29035 * @param {Roo.bootstrap.LocationPicker} this
29037 OverlayViewHide : true,
29039 * @event loadexception
29040 * Fires when load google lib failed.
29041 * @param {Roo.bootstrap.LocationPicker} this
29043 loadexception : true
29048 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29050 gMapContext: false,
29056 mapTypeControl: false,
29057 disableDoubleClickZoom: false,
29059 streetViewControl: false,
29063 enableAutocomplete: false,
29064 enableReverseGeocode: true,
29067 getAutoCreate: function()
29072 cls: 'roo-location-picker'
29078 initEvents: function(ct, position)
29080 if(!this.el.getWidth() || this.isApplied()){
29084 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29089 initial: function()
29091 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29092 this.fireEvent('loadexception', this);
29096 if(!this.mapTypeId){
29097 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29100 this.gMapContext = this.GMapContext();
29102 this.initOverlayView();
29104 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29108 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29109 _this.setPosition(_this.gMapContext.marker.position);
29112 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29113 _this.fireEvent('mapClick', this, event);
29117 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29118 _this.fireEvent('mapRightClick', this, event);
29122 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29123 _this.fireEvent('markerClick', this, event);
29127 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29128 _this.fireEvent('markerRightClick', this, event);
29132 this.setPosition(this.gMapContext.location);
29134 this.fireEvent('initial', this, this.gMapContext.location);
29137 initOverlayView: function()
29141 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29145 _this.fireEvent('OverlayViewDraw', _this);
29150 _this.fireEvent('OverlayViewOnAdd', _this);
29153 onRemove: function()
29155 _this.fireEvent('OverlayViewOnRemove', _this);
29158 show: function(cpx)
29160 _this.fireEvent('OverlayViewShow', _this, cpx);
29165 _this.fireEvent('OverlayViewHide', _this);
29171 fromLatLngToContainerPixel: function(event)
29173 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29176 isApplied: function()
29178 return this.getGmapContext() == false ? false : true;
29181 getGmapContext: function()
29183 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29186 GMapContext: function()
29188 var position = new google.maps.LatLng(this.latitude, this.longitude);
29190 var _map = new google.maps.Map(this.el.dom, {
29193 mapTypeId: this.mapTypeId,
29194 mapTypeControl: this.mapTypeControl,
29195 disableDoubleClickZoom: this.disableDoubleClickZoom,
29196 scrollwheel: this.scrollwheel,
29197 streetViewControl: this.streetViewControl,
29198 locationName: this.locationName,
29199 draggable: this.draggable,
29200 enableAutocomplete: this.enableAutocomplete,
29201 enableReverseGeocode: this.enableReverseGeocode
29204 var _marker = new google.maps.Marker({
29205 position: position,
29207 title: this.markerTitle,
29208 draggable: this.draggable
29215 location: position,
29216 radius: this.radius,
29217 locationName: this.locationName,
29218 addressComponents: {
29219 formatted_address: null,
29220 addressLine1: null,
29221 addressLine2: null,
29223 streetNumber: null,
29227 stateOrProvince: null
29230 domContainer: this.el.dom,
29231 geodecoder: new google.maps.Geocoder()
29235 drawCircle: function(center, radius, options)
29237 if (this.gMapContext.circle != null) {
29238 this.gMapContext.circle.setMap(null);
29242 options = Roo.apply({}, options, {
29243 strokeColor: "#0000FF",
29244 strokeOpacity: .35,
29246 fillColor: "#0000FF",
29250 options.map = this.gMapContext.map;
29251 options.radius = radius;
29252 options.center = center;
29253 this.gMapContext.circle = new google.maps.Circle(options);
29254 return this.gMapContext.circle;
29260 setPosition: function(location)
29262 this.gMapContext.location = location;
29263 this.gMapContext.marker.setPosition(location);
29264 this.gMapContext.map.panTo(location);
29265 this.drawCircle(location, this.gMapContext.radius, {});
29269 if (this.gMapContext.settings.enableReverseGeocode) {
29270 this.gMapContext.geodecoder.geocode({
29271 latLng: this.gMapContext.location
29272 }, function(results, status) {
29274 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29275 _this.gMapContext.locationName = results[0].formatted_address;
29276 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29278 _this.fireEvent('positionchanged', this, location);
29285 this.fireEvent('positionchanged', this, location);
29290 google.maps.event.trigger(this.gMapContext.map, "resize");
29292 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29294 this.fireEvent('resize', this);
29297 setPositionByLatLng: function(latitude, longitude)
29299 this.setPosition(new google.maps.LatLng(latitude, longitude));
29302 getCurrentPosition: function()
29305 latitude: this.gMapContext.location.lat(),
29306 longitude: this.gMapContext.location.lng()
29310 getAddressName: function()
29312 return this.gMapContext.locationName;
29315 getAddressComponents: function()
29317 return this.gMapContext.addressComponents;
29320 address_component_from_google_geocode: function(address_components)
29324 for (var i = 0; i < address_components.length; i++) {
29325 var component = address_components[i];
29326 if (component.types.indexOf("postal_code") >= 0) {
29327 result.postalCode = component.short_name;
29328 } else if (component.types.indexOf("street_number") >= 0) {
29329 result.streetNumber = component.short_name;
29330 } else if (component.types.indexOf("route") >= 0) {
29331 result.streetName = component.short_name;
29332 } else if (component.types.indexOf("neighborhood") >= 0) {
29333 result.city = component.short_name;
29334 } else if (component.types.indexOf("locality") >= 0) {
29335 result.city = component.short_name;
29336 } else if (component.types.indexOf("sublocality") >= 0) {
29337 result.district = component.short_name;
29338 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29339 result.stateOrProvince = component.short_name;
29340 } else if (component.types.indexOf("country") >= 0) {
29341 result.country = component.short_name;
29345 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29346 result.addressLine2 = "";
29350 setZoomLevel: function(zoom)
29352 this.gMapContext.map.setZoom(zoom);
29365 this.fireEvent('show', this);
29376 this.fireEvent('hide', this);
29381 Roo.apply(Roo.bootstrap.LocationPicker, {
29383 OverlayView : function(map, options)
29385 options = options || {};
29392 * @class Roo.bootstrap.Alert
29393 * @extends Roo.bootstrap.Component
29394 * Bootstrap Alert class - shows an alert area box
29396 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29397 Enter a valid email address
29400 * @cfg {String} title The title of alert
29401 * @cfg {String} html The content of alert
29402 * @cfg {String} weight ( success | info | warning | danger )
29403 * @cfg {String} faicon font-awesomeicon
29406 * Create a new alert
29407 * @param {Object} config The config object
29411 Roo.bootstrap.Alert = function(config){
29412 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29416 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29423 getAutoCreate : function()
29432 cls : 'roo-alert-icon'
29437 cls : 'roo-alert-title',
29442 cls : 'roo-alert-text',
29449 cfg.cn[0].cls += ' fa ' + this.faicon;
29453 cfg.cls += ' alert-' + this.weight;
29459 initEvents: function()
29461 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29464 setTitle : function(str)
29466 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29469 setText : function(str)
29471 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29474 setWeight : function(weight)
29477 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29480 this.weight = weight;
29482 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29485 setIcon : function(icon)
29488 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29491 this.faicon = icon;
29493 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29514 * @class Roo.bootstrap.UploadCropbox
29515 * @extends Roo.bootstrap.Component
29516 * Bootstrap UploadCropbox class
29517 * @cfg {String} emptyText show when image has been loaded
29518 * @cfg {String} rotateNotify show when image too small to rotate
29519 * @cfg {Number} errorTimeout default 3000
29520 * @cfg {Number} minWidth default 300
29521 * @cfg {Number} minHeight default 300
29522 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29523 * @cfg {Boolean} isDocument (true|false) default false
29524 * @cfg {String} url action url
29525 * @cfg {String} paramName default 'imageUpload'
29526 * @cfg {String} method default POST
29527 * @cfg {Boolean} loadMask (true|false) default true
29528 * @cfg {Boolean} loadingText default 'Loading...'
29531 * Create a new UploadCropbox
29532 * @param {Object} config The config object
29535 Roo.bootstrap.UploadCropbox = function(config){
29536 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29540 * @event beforeselectfile
29541 * Fire before select file
29542 * @param {Roo.bootstrap.UploadCropbox} this
29544 "beforeselectfile" : true,
29547 * Fire after initEvent
29548 * @param {Roo.bootstrap.UploadCropbox} this
29553 * Fire after initEvent
29554 * @param {Roo.bootstrap.UploadCropbox} this
29555 * @param {String} data
29560 * Fire when preparing the file data
29561 * @param {Roo.bootstrap.UploadCropbox} this
29562 * @param {Object} file
29567 * Fire when get exception
29568 * @param {Roo.bootstrap.UploadCropbox} this
29569 * @param {XMLHttpRequest} xhr
29571 "exception" : true,
29573 * @event beforeloadcanvas
29574 * Fire before load the canvas
29575 * @param {Roo.bootstrap.UploadCropbox} this
29576 * @param {String} src
29578 "beforeloadcanvas" : true,
29581 * Fire when trash image
29582 * @param {Roo.bootstrap.UploadCropbox} this
29587 * Fire when download the image
29588 * @param {Roo.bootstrap.UploadCropbox} this
29592 * @event footerbuttonclick
29593 * Fire when footerbuttonclick
29594 * @param {Roo.bootstrap.UploadCropbox} this
29595 * @param {String} type
29597 "footerbuttonclick" : true,
29601 * @param {Roo.bootstrap.UploadCropbox} this
29606 * Fire when rotate the image
29607 * @param {Roo.bootstrap.UploadCropbox} this
29608 * @param {String} pos
29613 * Fire when inspect the file
29614 * @param {Roo.bootstrap.UploadCropbox} this
29615 * @param {Object} file
29620 * Fire when xhr upload the file
29621 * @param {Roo.bootstrap.UploadCropbox} this
29622 * @param {Object} data
29627 * Fire when arrange the file data
29628 * @param {Roo.bootstrap.UploadCropbox} this
29629 * @param {Object} formData
29634 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29637 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29639 emptyText : 'Click to upload image',
29640 rotateNotify : 'Image is too small to rotate',
29641 errorTimeout : 3000,
29655 cropType : 'image/jpeg',
29657 canvasLoaded : false,
29658 isDocument : false,
29660 paramName : 'imageUpload',
29662 loadingText : 'Loading...',
29665 getAutoCreate : function()
29669 cls : 'roo-upload-cropbox',
29673 cls : 'roo-upload-cropbox-selector',
29678 cls : 'roo-upload-cropbox-body',
29679 style : 'cursor:pointer',
29683 cls : 'roo-upload-cropbox-preview'
29687 cls : 'roo-upload-cropbox-thumb'
29691 cls : 'roo-upload-cropbox-empty-notify',
29692 html : this.emptyText
29696 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29697 html : this.rotateNotify
29703 cls : 'roo-upload-cropbox-footer',
29706 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29716 onRender : function(ct, position)
29718 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29720 if (this.buttons.length) {
29722 Roo.each(this.buttons, function(bb) {
29724 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29726 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29732 this.maskEl = this.el;
29736 initEvents : function()
29738 this.urlAPI = (window.createObjectURL && window) ||
29739 (window.URL && URL.revokeObjectURL && URL) ||
29740 (window.webkitURL && webkitURL);
29742 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29743 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29745 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29746 this.selectorEl.hide();
29748 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29749 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29751 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29752 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29753 this.thumbEl.hide();
29755 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29756 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29758 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29759 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29760 this.errorEl.hide();
29762 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29763 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29764 this.footerEl.hide();
29766 this.setThumbBoxSize();
29772 this.fireEvent('initial', this);
29779 window.addEventListener("resize", function() { _this.resize(); } );
29781 this.bodyEl.on('click', this.beforeSelectFile, this);
29784 this.bodyEl.on('touchstart', this.onTouchStart, this);
29785 this.bodyEl.on('touchmove', this.onTouchMove, this);
29786 this.bodyEl.on('touchend', this.onTouchEnd, this);
29790 this.bodyEl.on('mousedown', this.onMouseDown, this);
29791 this.bodyEl.on('mousemove', this.onMouseMove, this);
29792 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29793 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29794 Roo.get(document).on('mouseup', this.onMouseUp, this);
29797 this.selectorEl.on('change', this.onFileSelected, this);
29803 this.baseScale = 1;
29805 this.baseRotate = 1;
29806 this.dragable = false;
29807 this.pinching = false;
29810 this.cropData = false;
29811 this.notifyEl.dom.innerHTML = this.emptyText;
29813 this.selectorEl.dom.value = '';
29817 resize : function()
29819 if(this.fireEvent('resize', this) != false){
29820 this.setThumbBoxPosition();
29821 this.setCanvasPosition();
29825 onFooterButtonClick : function(e, el, o, type)
29828 case 'rotate-left' :
29829 this.onRotateLeft(e);
29831 case 'rotate-right' :
29832 this.onRotateRight(e);
29835 this.beforeSelectFile(e);
29850 this.fireEvent('footerbuttonclick', this, type);
29853 beforeSelectFile : function(e)
29855 e.preventDefault();
29857 if(this.fireEvent('beforeselectfile', this) != false){
29858 this.selectorEl.dom.click();
29862 onFileSelected : function(e)
29864 e.preventDefault();
29866 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29870 var file = this.selectorEl.dom.files[0];
29872 if(this.fireEvent('inspect', this, file) != false){
29873 this.prepare(file);
29878 trash : function(e)
29880 this.fireEvent('trash', this);
29883 download : function(e)
29885 this.fireEvent('download', this);
29888 loadCanvas : function(src)
29890 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29894 this.imageEl = document.createElement('img');
29898 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29900 this.imageEl.src = src;
29904 onLoadCanvas : function()
29906 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29907 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29909 this.bodyEl.un('click', this.beforeSelectFile, this);
29911 this.notifyEl.hide();
29912 this.thumbEl.show();
29913 this.footerEl.show();
29915 this.baseRotateLevel();
29917 if(this.isDocument){
29918 this.setThumbBoxSize();
29921 this.setThumbBoxPosition();
29923 this.baseScaleLevel();
29929 this.canvasLoaded = true;
29932 this.maskEl.unmask();
29937 setCanvasPosition : function()
29939 if(!this.canvasEl){
29943 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29944 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29946 this.previewEl.setLeft(pw);
29947 this.previewEl.setTop(ph);
29951 onMouseDown : function(e)
29955 this.dragable = true;
29956 this.pinching = false;
29958 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29959 this.dragable = false;
29963 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29964 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29968 onMouseMove : function(e)
29972 if(!this.canvasLoaded){
29976 if (!this.dragable){
29980 var minX = Math.ceil(this.thumbEl.getLeft(true));
29981 var minY = Math.ceil(this.thumbEl.getTop(true));
29983 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29984 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29986 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29987 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29989 x = x - this.mouseX;
29990 y = y - this.mouseY;
29992 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29993 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29995 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29996 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29998 this.previewEl.setLeft(bgX);
29999 this.previewEl.setTop(bgY);
30001 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30002 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30005 onMouseUp : function(e)
30009 this.dragable = false;
30012 onMouseWheel : function(e)
30016 this.startScale = this.scale;
30018 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30020 if(!this.zoomable()){
30021 this.scale = this.startScale;
30030 zoomable : function()
30032 var minScale = this.thumbEl.getWidth() / this.minWidth;
30034 if(this.minWidth < this.minHeight){
30035 minScale = this.thumbEl.getHeight() / this.minHeight;
30038 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30039 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30043 (this.rotate == 0 || this.rotate == 180) &&
30045 width > this.imageEl.OriginWidth ||
30046 height > this.imageEl.OriginHeight ||
30047 (width < this.minWidth && height < this.minHeight)
30055 (this.rotate == 90 || this.rotate == 270) &&
30057 width > this.imageEl.OriginWidth ||
30058 height > this.imageEl.OriginHeight ||
30059 (width < this.minHeight && height < this.minWidth)
30066 !this.isDocument &&
30067 (this.rotate == 0 || this.rotate == 180) &&
30069 width < this.minWidth ||
30070 width > this.imageEl.OriginWidth ||
30071 height < this.minHeight ||
30072 height > this.imageEl.OriginHeight
30079 !this.isDocument &&
30080 (this.rotate == 90 || this.rotate == 270) &&
30082 width < this.minHeight ||
30083 width > this.imageEl.OriginWidth ||
30084 height < this.minWidth ||
30085 height > this.imageEl.OriginHeight
30095 onRotateLeft : function(e)
30097 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30099 var minScale = this.thumbEl.getWidth() / this.minWidth;
30101 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30102 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30104 this.startScale = this.scale;
30106 while (this.getScaleLevel() < minScale){
30108 this.scale = this.scale + 1;
30110 if(!this.zoomable()){
30115 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30116 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30121 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30128 this.scale = this.startScale;
30130 this.onRotateFail();
30135 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30137 if(this.isDocument){
30138 this.setThumbBoxSize();
30139 this.setThumbBoxPosition();
30140 this.setCanvasPosition();
30145 this.fireEvent('rotate', this, 'left');
30149 onRotateRight : function(e)
30151 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30153 var minScale = this.thumbEl.getWidth() / this.minWidth;
30155 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30156 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30158 this.startScale = this.scale;
30160 while (this.getScaleLevel() < minScale){
30162 this.scale = this.scale + 1;
30164 if(!this.zoomable()){
30169 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30170 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30175 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30182 this.scale = this.startScale;
30184 this.onRotateFail();
30189 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30191 if(this.isDocument){
30192 this.setThumbBoxSize();
30193 this.setThumbBoxPosition();
30194 this.setCanvasPosition();
30199 this.fireEvent('rotate', this, 'right');
30202 onRotateFail : function()
30204 this.errorEl.show(true);
30208 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30213 this.previewEl.dom.innerHTML = '';
30215 var canvasEl = document.createElement("canvas");
30217 var contextEl = canvasEl.getContext("2d");
30219 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30220 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30221 var center = this.imageEl.OriginWidth / 2;
30223 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30224 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30225 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30226 center = this.imageEl.OriginHeight / 2;
30229 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30231 contextEl.translate(center, center);
30232 contextEl.rotate(this.rotate * Math.PI / 180);
30234 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30236 this.canvasEl = document.createElement("canvas");
30238 this.contextEl = this.canvasEl.getContext("2d");
30240 switch (this.rotate) {
30243 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30244 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30246 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30251 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30252 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30254 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30255 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);
30259 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30264 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30265 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30267 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30268 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);
30272 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);
30277 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30278 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30280 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30281 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30285 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);
30292 this.previewEl.appendChild(this.canvasEl);
30294 this.setCanvasPosition();
30299 if(!this.canvasLoaded){
30303 var imageCanvas = document.createElement("canvas");
30305 var imageContext = imageCanvas.getContext("2d");
30307 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30308 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30310 var center = imageCanvas.width / 2;
30312 imageContext.translate(center, center);
30314 imageContext.rotate(this.rotate * Math.PI / 180);
30316 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30318 var canvas = document.createElement("canvas");
30320 var context = canvas.getContext("2d");
30322 canvas.width = this.minWidth;
30323 canvas.height = this.minHeight;
30325 switch (this.rotate) {
30328 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30329 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30331 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30332 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30334 var targetWidth = this.minWidth - 2 * x;
30335 var targetHeight = this.minHeight - 2 * y;
30339 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30340 scale = targetWidth / width;
30343 if(x > 0 && y == 0){
30344 scale = targetHeight / height;
30347 if(x > 0 && y > 0){
30348 scale = targetWidth / width;
30350 if(width < height){
30351 scale = targetHeight / height;
30355 context.scale(scale, scale);
30357 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30358 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30360 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30361 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30363 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30368 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30369 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30371 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30372 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30374 var targetWidth = this.minWidth - 2 * x;
30375 var targetHeight = this.minHeight - 2 * y;
30379 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30380 scale = targetWidth / width;
30383 if(x > 0 && y == 0){
30384 scale = targetHeight / height;
30387 if(x > 0 && y > 0){
30388 scale = targetWidth / width;
30390 if(width < height){
30391 scale = targetHeight / height;
30395 context.scale(scale, scale);
30397 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30398 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30400 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30401 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30403 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30405 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30410 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30411 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30413 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30414 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30416 var targetWidth = this.minWidth - 2 * x;
30417 var targetHeight = this.minHeight - 2 * y;
30421 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30422 scale = targetWidth / width;
30425 if(x > 0 && y == 0){
30426 scale = targetHeight / height;
30429 if(x > 0 && y > 0){
30430 scale = targetWidth / width;
30432 if(width < height){
30433 scale = targetHeight / height;
30437 context.scale(scale, scale);
30439 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30440 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30442 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30443 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30445 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30446 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30448 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30453 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30454 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30456 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30457 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30459 var targetWidth = this.minWidth - 2 * x;
30460 var targetHeight = this.minHeight - 2 * y;
30464 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30465 scale = targetWidth / width;
30468 if(x > 0 && y == 0){
30469 scale = targetHeight / height;
30472 if(x > 0 && y > 0){
30473 scale = targetWidth / width;
30475 if(width < height){
30476 scale = targetHeight / height;
30480 context.scale(scale, scale);
30482 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30483 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30485 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30486 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30488 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30490 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30497 this.cropData = canvas.toDataURL(this.cropType);
30499 if(this.fireEvent('crop', this, this.cropData) !== false){
30500 this.process(this.file, this.cropData);
30507 setThumbBoxSize : function()
30511 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30512 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30513 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30515 this.minWidth = width;
30516 this.minHeight = height;
30518 if(this.rotate == 90 || this.rotate == 270){
30519 this.minWidth = height;
30520 this.minHeight = width;
30525 width = Math.ceil(this.minWidth * height / this.minHeight);
30527 if(this.minWidth > this.minHeight){
30529 height = Math.ceil(this.minHeight * width / this.minWidth);
30532 this.thumbEl.setStyle({
30533 width : width + 'px',
30534 height : height + 'px'
30541 setThumbBoxPosition : function()
30543 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30544 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30546 this.thumbEl.setLeft(x);
30547 this.thumbEl.setTop(y);
30551 baseRotateLevel : function()
30553 this.baseRotate = 1;
30556 typeof(this.exif) != 'undefined' &&
30557 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30558 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30560 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30563 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30567 baseScaleLevel : function()
30571 if(this.isDocument){
30573 if(this.baseRotate == 6 || this.baseRotate == 8){
30575 height = this.thumbEl.getHeight();
30576 this.baseScale = height / this.imageEl.OriginWidth;
30578 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30579 width = this.thumbEl.getWidth();
30580 this.baseScale = width / this.imageEl.OriginHeight;
30586 height = this.thumbEl.getHeight();
30587 this.baseScale = height / this.imageEl.OriginHeight;
30589 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30590 width = this.thumbEl.getWidth();
30591 this.baseScale = width / this.imageEl.OriginWidth;
30597 if(this.baseRotate == 6 || this.baseRotate == 8){
30599 width = this.thumbEl.getHeight();
30600 this.baseScale = width / this.imageEl.OriginHeight;
30602 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30603 height = this.thumbEl.getWidth();
30604 this.baseScale = height / this.imageEl.OriginHeight;
30607 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30608 height = this.thumbEl.getWidth();
30609 this.baseScale = height / this.imageEl.OriginHeight;
30611 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30612 width = this.thumbEl.getHeight();
30613 this.baseScale = width / this.imageEl.OriginWidth;
30620 width = this.thumbEl.getWidth();
30621 this.baseScale = width / this.imageEl.OriginWidth;
30623 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30624 height = this.thumbEl.getHeight();
30625 this.baseScale = height / this.imageEl.OriginHeight;
30628 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30630 height = this.thumbEl.getHeight();
30631 this.baseScale = height / this.imageEl.OriginHeight;
30633 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30634 width = this.thumbEl.getWidth();
30635 this.baseScale = width / this.imageEl.OriginWidth;
30643 getScaleLevel : function()
30645 return this.baseScale * Math.pow(1.1, this.scale);
30648 onTouchStart : function(e)
30650 if(!this.canvasLoaded){
30651 this.beforeSelectFile(e);
30655 var touches = e.browserEvent.touches;
30661 if(touches.length == 1){
30662 this.onMouseDown(e);
30666 if(touches.length != 2){
30672 for(var i = 0, finger; finger = touches[i]; i++){
30673 coords.push(finger.pageX, finger.pageY);
30676 var x = Math.pow(coords[0] - coords[2], 2);
30677 var y = Math.pow(coords[1] - coords[3], 2);
30679 this.startDistance = Math.sqrt(x + y);
30681 this.startScale = this.scale;
30683 this.pinching = true;
30684 this.dragable = false;
30688 onTouchMove : function(e)
30690 if(!this.pinching && !this.dragable){
30694 var touches = e.browserEvent.touches;
30701 this.onMouseMove(e);
30707 for(var i = 0, finger; finger = touches[i]; i++){
30708 coords.push(finger.pageX, finger.pageY);
30711 var x = Math.pow(coords[0] - coords[2], 2);
30712 var y = Math.pow(coords[1] - coords[3], 2);
30714 this.endDistance = Math.sqrt(x + y);
30716 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30718 if(!this.zoomable()){
30719 this.scale = this.startScale;
30727 onTouchEnd : function(e)
30729 this.pinching = false;
30730 this.dragable = false;
30734 process : function(file, crop)
30737 this.maskEl.mask(this.loadingText);
30740 this.xhr = new XMLHttpRequest();
30742 file.xhr = this.xhr;
30744 this.xhr.open(this.method, this.url, true);
30747 "Accept": "application/json",
30748 "Cache-Control": "no-cache",
30749 "X-Requested-With": "XMLHttpRequest"
30752 for (var headerName in headers) {
30753 var headerValue = headers[headerName];
30755 this.xhr.setRequestHeader(headerName, headerValue);
30761 this.xhr.onload = function()
30763 _this.xhrOnLoad(_this.xhr);
30766 this.xhr.onerror = function()
30768 _this.xhrOnError(_this.xhr);
30771 var formData = new FormData();
30773 formData.append('returnHTML', 'NO');
30776 formData.append('crop', crop);
30779 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30780 formData.append(this.paramName, file, file.name);
30783 if(typeof(file.filename) != 'undefined'){
30784 formData.append('filename', file.filename);
30787 if(typeof(file.mimetype) != 'undefined'){
30788 formData.append('mimetype', file.mimetype);
30791 if(this.fireEvent('arrange', this, formData) != false){
30792 this.xhr.send(formData);
30796 xhrOnLoad : function(xhr)
30799 this.maskEl.unmask();
30802 if (xhr.readyState !== 4) {
30803 this.fireEvent('exception', this, xhr);
30807 var response = Roo.decode(xhr.responseText);
30809 if(!response.success){
30810 this.fireEvent('exception', this, xhr);
30814 var response = Roo.decode(xhr.responseText);
30816 this.fireEvent('upload', this, response);
30820 xhrOnError : function()
30823 this.maskEl.unmask();
30826 Roo.log('xhr on error');
30828 var response = Roo.decode(xhr.responseText);
30834 prepare : function(file)
30837 this.maskEl.mask(this.loadingText);
30843 if(typeof(file) === 'string'){
30844 this.loadCanvas(file);
30848 if(!file || !this.urlAPI){
30853 this.cropType = file.type;
30857 if(this.fireEvent('prepare', this, this.file) != false){
30859 var reader = new FileReader();
30861 reader.onload = function (e) {
30862 if (e.target.error) {
30863 Roo.log(e.target.error);
30867 var buffer = e.target.result,
30868 dataView = new DataView(buffer),
30870 maxOffset = dataView.byteLength - 4,
30874 if (dataView.getUint16(0) === 0xffd8) {
30875 while (offset < maxOffset) {
30876 markerBytes = dataView.getUint16(offset);
30878 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30879 markerLength = dataView.getUint16(offset + 2) + 2;
30880 if (offset + markerLength > dataView.byteLength) {
30881 Roo.log('Invalid meta data: Invalid segment size.');
30885 if(markerBytes == 0xffe1){
30886 _this.parseExifData(
30893 offset += markerLength;
30903 var url = _this.urlAPI.createObjectURL(_this.file);
30905 _this.loadCanvas(url);
30910 reader.readAsArrayBuffer(this.file);
30916 parseExifData : function(dataView, offset, length)
30918 var tiffOffset = offset + 10,
30922 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30923 // No Exif data, might be XMP data instead
30927 // Check for the ASCII code for "Exif" (0x45786966):
30928 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30929 // No Exif data, might be XMP data instead
30932 if (tiffOffset + 8 > dataView.byteLength) {
30933 Roo.log('Invalid Exif data: Invalid segment size.');
30936 // Check for the two null bytes:
30937 if (dataView.getUint16(offset + 8) !== 0x0000) {
30938 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30941 // Check the byte alignment:
30942 switch (dataView.getUint16(tiffOffset)) {
30944 littleEndian = true;
30947 littleEndian = false;
30950 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30953 // Check for the TIFF tag marker (0x002A):
30954 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30955 Roo.log('Invalid Exif data: Missing TIFF marker.');
30958 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30959 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30961 this.parseExifTags(
30964 tiffOffset + dirOffset,
30969 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30974 if (dirOffset + 6 > dataView.byteLength) {
30975 Roo.log('Invalid Exif data: Invalid directory offset.');
30978 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30979 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30980 if (dirEndOffset + 4 > dataView.byteLength) {
30981 Roo.log('Invalid Exif data: Invalid directory size.');
30984 for (i = 0; i < tagsNumber; i += 1) {
30988 dirOffset + 2 + 12 * i, // tag offset
30992 // Return the offset to the next directory:
30993 return dataView.getUint32(dirEndOffset, littleEndian);
30996 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30998 var tag = dataView.getUint16(offset, littleEndian);
31000 this.exif[tag] = this.getExifValue(
31004 dataView.getUint16(offset + 2, littleEndian), // tag type
31005 dataView.getUint32(offset + 4, littleEndian), // tag length
31010 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31012 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31021 Roo.log('Invalid Exif data: Invalid tag type.');
31025 tagSize = tagType.size * length;
31026 // Determine if the value is contained in the dataOffset bytes,
31027 // or if the value at the dataOffset is a pointer to the actual data:
31028 dataOffset = tagSize > 4 ?
31029 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31030 if (dataOffset + tagSize > dataView.byteLength) {
31031 Roo.log('Invalid Exif data: Invalid data offset.');
31034 if (length === 1) {
31035 return tagType.getValue(dataView, dataOffset, littleEndian);
31038 for (i = 0; i < length; i += 1) {
31039 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31042 if (tagType.ascii) {
31044 // Concatenate the chars:
31045 for (i = 0; i < values.length; i += 1) {
31047 // Ignore the terminating NULL byte(s):
31048 if (c === '\u0000') {
31060 Roo.apply(Roo.bootstrap.UploadCropbox, {
31062 'Orientation': 0x0112
31066 1: 0, //'top-left',
31068 3: 180, //'bottom-right',
31069 // 4: 'bottom-left',
31071 6: 90, //'right-top',
31072 // 7: 'right-bottom',
31073 8: 270 //'left-bottom'
31077 // byte, 8-bit unsigned int:
31079 getValue: function (dataView, dataOffset) {
31080 return dataView.getUint8(dataOffset);
31084 // ascii, 8-bit byte:
31086 getValue: function (dataView, dataOffset) {
31087 return String.fromCharCode(dataView.getUint8(dataOffset));
31092 // short, 16 bit int:
31094 getValue: function (dataView, dataOffset, littleEndian) {
31095 return dataView.getUint16(dataOffset, littleEndian);
31099 // long, 32 bit int:
31101 getValue: function (dataView, dataOffset, littleEndian) {
31102 return dataView.getUint32(dataOffset, littleEndian);
31106 // rational = two long values, first is numerator, second is denominator:
31108 getValue: function (dataView, dataOffset, littleEndian) {
31109 return dataView.getUint32(dataOffset, littleEndian) /
31110 dataView.getUint32(dataOffset + 4, littleEndian);
31114 // slong, 32 bit signed int:
31116 getValue: function (dataView, dataOffset, littleEndian) {
31117 return dataView.getInt32(dataOffset, littleEndian);
31121 // srational, two slongs, first is numerator, second is denominator:
31123 getValue: function (dataView, dataOffset, littleEndian) {
31124 return dataView.getInt32(dataOffset, littleEndian) /
31125 dataView.getInt32(dataOffset + 4, littleEndian);
31135 cls : 'btn-group roo-upload-cropbox-rotate-left',
31136 action : 'rotate-left',
31140 cls : 'btn btn-default',
31141 html : '<i class="fa fa-undo"></i>'
31147 cls : 'btn-group roo-upload-cropbox-picture',
31148 action : 'picture',
31152 cls : 'btn btn-default',
31153 html : '<i class="fa fa-picture-o"></i>'
31159 cls : 'btn-group roo-upload-cropbox-rotate-right',
31160 action : 'rotate-right',
31164 cls : 'btn btn-default',
31165 html : '<i class="fa fa-repeat"></i>'
31173 cls : 'btn-group roo-upload-cropbox-rotate-left',
31174 action : 'rotate-left',
31178 cls : 'btn btn-default',
31179 html : '<i class="fa fa-undo"></i>'
31185 cls : 'btn-group roo-upload-cropbox-download',
31186 action : 'download',
31190 cls : 'btn btn-default',
31191 html : '<i class="fa fa-download"></i>'
31197 cls : 'btn-group roo-upload-cropbox-crop',
31202 cls : 'btn btn-default',
31203 html : '<i class="fa fa-crop"></i>'
31209 cls : 'btn-group roo-upload-cropbox-trash',
31214 cls : 'btn btn-default',
31215 html : '<i class="fa fa-trash"></i>'
31221 cls : 'btn-group roo-upload-cropbox-rotate-right',
31222 action : 'rotate-right',
31226 cls : 'btn btn-default',
31227 html : '<i class="fa fa-repeat"></i>'
31235 cls : 'btn-group roo-upload-cropbox-rotate-left',
31236 action : 'rotate-left',
31240 cls : 'btn btn-default',
31241 html : '<i class="fa fa-undo"></i>'
31247 cls : 'btn-group roo-upload-cropbox-rotate-right',
31248 action : 'rotate-right',
31252 cls : 'btn btn-default',
31253 html : '<i class="fa fa-repeat"></i>'
31266 * @class Roo.bootstrap.DocumentManager
31267 * @extends Roo.bootstrap.Component
31268 * Bootstrap DocumentManager class
31269 * @cfg {String} paramName default 'imageUpload'
31270 * @cfg {String} toolTipName default 'filename'
31271 * @cfg {String} method default POST
31272 * @cfg {String} url action url
31273 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31274 * @cfg {Boolean} multiple multiple upload default true
31275 * @cfg {Number} thumbSize default 300
31276 * @cfg {String} fieldLabel
31277 * @cfg {Number} labelWidth default 4
31278 * @cfg {String} labelAlign (left|top) default left
31279 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31280 * @cfg {Number} labellg set the width of label (1-12)
31281 * @cfg {Number} labelmd set the width of label (1-12)
31282 * @cfg {Number} labelsm set the width of label (1-12)
31283 * @cfg {Number} labelxs set the width of label (1-12)
31286 * Create a new DocumentManager
31287 * @param {Object} config The config object
31290 Roo.bootstrap.DocumentManager = function(config){
31291 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31294 this.delegates = [];
31299 * Fire when initial the DocumentManager
31300 * @param {Roo.bootstrap.DocumentManager} this
31305 * inspect selected file
31306 * @param {Roo.bootstrap.DocumentManager} this
31307 * @param {File} file
31312 * Fire when xhr load exception
31313 * @param {Roo.bootstrap.DocumentManager} this
31314 * @param {XMLHttpRequest} xhr
31316 "exception" : true,
31318 * @event afterupload
31319 * Fire when xhr load exception
31320 * @param {Roo.bootstrap.DocumentManager} this
31321 * @param {XMLHttpRequest} xhr
31323 "afterupload" : true,
31326 * prepare the form data
31327 * @param {Roo.bootstrap.DocumentManager} this
31328 * @param {Object} formData
31333 * Fire when remove the file
31334 * @param {Roo.bootstrap.DocumentManager} this
31335 * @param {Object} file
31340 * Fire after refresh the file
31341 * @param {Roo.bootstrap.DocumentManager} this
31346 * Fire after click the image
31347 * @param {Roo.bootstrap.DocumentManager} this
31348 * @param {Object} file
31353 * Fire when upload a image and editable set to true
31354 * @param {Roo.bootstrap.DocumentManager} this
31355 * @param {Object} file
31359 * @event beforeselectfile
31360 * Fire before select file
31361 * @param {Roo.bootstrap.DocumentManager} this
31363 "beforeselectfile" : true,
31366 * Fire before process file
31367 * @param {Roo.bootstrap.DocumentManager} this
31368 * @param {Object} file
31372 * @event previewrendered
31373 * Fire when preview rendered
31374 * @param {Roo.bootstrap.DocumentManager} this
31375 * @param {Object} file
31377 "previewrendered" : true,
31380 "previewResize" : true
31385 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31394 paramName : 'imageUpload',
31395 toolTipName : 'filename',
31398 labelAlign : 'left',
31408 getAutoCreate : function()
31410 var managerWidget = {
31412 cls : 'roo-document-manager',
31416 cls : 'roo-document-manager-selector',
31421 cls : 'roo-document-manager-uploader',
31425 cls : 'roo-document-manager-upload-btn',
31426 html : '<i class="fa fa-plus"></i>'
31437 cls : 'column col-md-12',
31442 if(this.fieldLabel.length){
31447 cls : 'column col-md-12',
31448 html : this.fieldLabel
31452 cls : 'column col-md-12',
31457 if(this.labelAlign == 'left'){
31462 html : this.fieldLabel
31471 if(this.labelWidth > 12){
31472 content[0].style = "width: " + this.labelWidth + 'px';
31475 if(this.labelWidth < 13 && this.labelmd == 0){
31476 this.labelmd = this.labelWidth;
31479 if(this.labellg > 0){
31480 content[0].cls += ' col-lg-' + this.labellg;
31481 content[1].cls += ' col-lg-' + (12 - this.labellg);
31484 if(this.labelmd > 0){
31485 content[0].cls += ' col-md-' + this.labelmd;
31486 content[1].cls += ' col-md-' + (12 - this.labelmd);
31489 if(this.labelsm > 0){
31490 content[0].cls += ' col-sm-' + this.labelsm;
31491 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31494 if(this.labelxs > 0){
31495 content[0].cls += ' col-xs-' + this.labelxs;
31496 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31504 cls : 'row clearfix',
31512 initEvents : function()
31514 this.managerEl = this.el.select('.roo-document-manager', true).first();
31515 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31517 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31518 this.selectorEl.hide();
31521 this.selectorEl.attr('multiple', 'multiple');
31524 this.selectorEl.on('change', this.onFileSelected, this);
31526 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31527 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31529 this.uploader.on('click', this.onUploaderClick, this);
31531 this.renderProgressDialog();
31535 window.addEventListener("resize", function() { _this.refresh(); } );
31537 this.fireEvent('initial', this);
31540 renderProgressDialog : function()
31544 this.progressDialog = new Roo.bootstrap.Modal({
31545 cls : 'roo-document-manager-progress-dialog',
31546 allow_close : false,
31557 btnclick : function() {
31558 _this.uploadCancel();
31564 this.progressDialog.render(Roo.get(document.body));
31566 this.progress = new Roo.bootstrap.Progress({
31567 cls : 'roo-document-manager-progress',
31572 this.progress.render(this.progressDialog.getChildContainer());
31574 this.progressBar = new Roo.bootstrap.ProgressBar({
31575 cls : 'roo-document-manager-progress-bar',
31578 aria_valuemax : 12,
31582 this.progressBar.render(this.progress.getChildContainer());
31585 onUploaderClick : function(e)
31587 e.preventDefault();
31589 if(this.fireEvent('beforeselectfile', this) != false){
31590 this.selectorEl.dom.click();
31595 onFileSelected : function(e)
31597 e.preventDefault();
31599 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31603 Roo.each(this.selectorEl.dom.files, function(file){
31604 if(this.fireEvent('inspect', this, file) != false){
31605 this.files.push(file);
31615 this.selectorEl.dom.value = '';
31617 if(!this.files || !this.files.length){
31621 if(this.boxes > 0 && this.files.length > this.boxes){
31622 this.files = this.files.slice(0, this.boxes);
31625 this.uploader.show();
31627 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31628 this.uploader.hide();
31637 Roo.each(this.files, function(file){
31639 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31640 var f = this.renderPreview(file);
31645 if(file.type.indexOf('image') != -1){
31646 this.delegates.push(
31648 _this.process(file);
31649 }).createDelegate(this)
31657 _this.process(file);
31658 }).createDelegate(this)
31663 this.files = files;
31665 this.delegates = this.delegates.concat(docs);
31667 if(!this.delegates.length){
31672 this.progressBar.aria_valuemax = this.delegates.length;
31679 arrange : function()
31681 if(!this.delegates.length){
31682 this.progressDialog.hide();
31687 var delegate = this.delegates.shift();
31689 this.progressDialog.show();
31691 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31693 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31698 refresh : function()
31700 this.uploader.show();
31702 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31703 this.uploader.hide();
31706 Roo.isTouch ? this.closable(false) : this.closable(true);
31708 this.fireEvent('refresh', this);
31711 onRemove : function(e, el, o)
31713 e.preventDefault();
31715 this.fireEvent('remove', this, o);
31719 remove : function(o)
31723 Roo.each(this.files, function(file){
31724 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31733 this.files = files;
31740 Roo.each(this.files, function(file){
31745 file.target.remove();
31754 onClick : function(e, el, o)
31756 e.preventDefault();
31758 this.fireEvent('click', this, o);
31762 closable : function(closable)
31764 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31766 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31778 xhrOnLoad : function(xhr)
31780 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31784 if (xhr.readyState !== 4) {
31786 this.fireEvent('exception', this, xhr);
31790 var response = Roo.decode(xhr.responseText);
31792 if(!response.success){
31794 this.fireEvent('exception', this, xhr);
31798 var file = this.renderPreview(response.data);
31800 this.files.push(file);
31804 this.fireEvent('afterupload', this, xhr);
31808 xhrOnError : function(xhr)
31810 Roo.log('xhr on error');
31812 var response = Roo.decode(xhr.responseText);
31819 process : function(file)
31821 if(this.fireEvent('process', this, file) !== false){
31822 if(this.editable && file.type.indexOf('image') != -1){
31823 this.fireEvent('edit', this, file);
31827 this.uploadStart(file, false);
31834 uploadStart : function(file, crop)
31836 this.xhr = new XMLHttpRequest();
31838 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31843 file.xhr = this.xhr;
31845 this.managerEl.createChild({
31847 cls : 'roo-document-manager-loading',
31851 tooltip : file.name,
31852 cls : 'roo-document-manager-thumb',
31853 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31859 this.xhr.open(this.method, this.url, true);
31862 "Accept": "application/json",
31863 "Cache-Control": "no-cache",
31864 "X-Requested-With": "XMLHttpRequest"
31867 for (var headerName in headers) {
31868 var headerValue = headers[headerName];
31870 this.xhr.setRequestHeader(headerName, headerValue);
31876 this.xhr.onload = function()
31878 _this.xhrOnLoad(_this.xhr);
31881 this.xhr.onerror = function()
31883 _this.xhrOnError(_this.xhr);
31886 var formData = new FormData();
31888 formData.append('returnHTML', 'NO');
31891 formData.append('crop', crop);
31894 formData.append(this.paramName, file, file.name);
31901 if(this.fireEvent('prepare', this, formData, options) != false){
31903 if(options.manually){
31907 this.xhr.send(formData);
31911 this.uploadCancel();
31914 uploadCancel : function()
31920 this.delegates = [];
31922 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31929 renderPreview : function(file)
31931 if(typeof(file.target) != 'undefined' && file.target){
31935 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31937 var previewEl = this.managerEl.createChild({
31939 cls : 'roo-document-manager-preview',
31943 tooltip : file[this.toolTipName],
31944 cls : 'roo-document-manager-thumb',
31945 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31950 html : '<i class="fa fa-times-circle"></i>'
31955 var close = previewEl.select('button.close', true).first();
31957 close.on('click', this.onRemove, this, file);
31959 file.target = previewEl;
31961 var image = previewEl.select('img', true).first();
31965 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31967 image.on('click', this.onClick, this, file);
31969 this.fireEvent('previewrendered', this, file);
31975 onPreviewLoad : function(file, image)
31977 if(typeof(file.target) == 'undefined' || !file.target){
31981 var width = image.dom.naturalWidth || image.dom.width;
31982 var height = image.dom.naturalHeight || image.dom.height;
31984 if(!this.previewResize) {
31988 if(width > height){
31989 file.target.addClass('wide');
31993 file.target.addClass('tall');
31998 uploadFromSource : function(file, crop)
32000 this.xhr = new XMLHttpRequest();
32002 this.managerEl.createChild({
32004 cls : 'roo-document-manager-loading',
32008 tooltip : file.name,
32009 cls : 'roo-document-manager-thumb',
32010 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32016 this.xhr.open(this.method, this.url, true);
32019 "Accept": "application/json",
32020 "Cache-Control": "no-cache",
32021 "X-Requested-With": "XMLHttpRequest"
32024 for (var headerName in headers) {
32025 var headerValue = headers[headerName];
32027 this.xhr.setRequestHeader(headerName, headerValue);
32033 this.xhr.onload = function()
32035 _this.xhrOnLoad(_this.xhr);
32038 this.xhr.onerror = function()
32040 _this.xhrOnError(_this.xhr);
32043 var formData = new FormData();
32045 formData.append('returnHTML', 'NO');
32047 formData.append('crop', crop);
32049 if(typeof(file.filename) != 'undefined'){
32050 formData.append('filename', file.filename);
32053 if(typeof(file.mimetype) != 'undefined'){
32054 formData.append('mimetype', file.mimetype);
32059 if(this.fireEvent('prepare', this, formData) != false){
32060 this.xhr.send(formData);
32070 * @class Roo.bootstrap.DocumentViewer
32071 * @extends Roo.bootstrap.Component
32072 * Bootstrap DocumentViewer class
32073 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32074 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32077 * Create a new DocumentViewer
32078 * @param {Object} config The config object
32081 Roo.bootstrap.DocumentViewer = function(config){
32082 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32087 * Fire after initEvent
32088 * @param {Roo.bootstrap.DocumentViewer} this
32094 * @param {Roo.bootstrap.DocumentViewer} this
32099 * Fire after download button
32100 * @param {Roo.bootstrap.DocumentViewer} this
32105 * Fire after trash button
32106 * @param {Roo.bootstrap.DocumentViewer} this
32113 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32115 showDownload : true,
32119 getAutoCreate : function()
32123 cls : 'roo-document-viewer',
32127 cls : 'roo-document-viewer-body',
32131 cls : 'roo-document-viewer-thumb',
32135 cls : 'roo-document-viewer-image'
32143 cls : 'roo-document-viewer-footer',
32146 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32150 cls : 'btn-group roo-document-viewer-download',
32154 cls : 'btn btn-default',
32155 html : '<i class="fa fa-download"></i>'
32161 cls : 'btn-group roo-document-viewer-trash',
32165 cls : 'btn btn-default',
32166 html : '<i class="fa fa-trash"></i>'
32179 initEvents : function()
32181 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32182 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32184 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32185 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32187 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32188 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32190 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32191 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32193 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32194 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32196 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32197 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32199 this.bodyEl.on('click', this.onClick, this);
32200 this.downloadBtn.on('click', this.onDownload, this);
32201 this.trashBtn.on('click', this.onTrash, this);
32203 this.downloadBtn.hide();
32204 this.trashBtn.hide();
32206 if(this.showDownload){
32207 this.downloadBtn.show();
32210 if(this.showTrash){
32211 this.trashBtn.show();
32214 if(!this.showDownload && !this.showTrash) {
32215 this.footerEl.hide();
32220 initial : function()
32222 this.fireEvent('initial', this);
32226 onClick : function(e)
32228 e.preventDefault();
32230 this.fireEvent('click', this);
32233 onDownload : function(e)
32235 e.preventDefault();
32237 this.fireEvent('download', this);
32240 onTrash : function(e)
32242 e.preventDefault();
32244 this.fireEvent('trash', this);
32256 * @class Roo.bootstrap.NavProgressBar
32257 * @extends Roo.bootstrap.Component
32258 * Bootstrap NavProgressBar class
32261 * Create a new nav progress bar
32262 * @param {Object} config The config object
32265 Roo.bootstrap.NavProgressBar = function(config){
32266 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32268 this.bullets = this.bullets || [];
32270 // Roo.bootstrap.NavProgressBar.register(this);
32274 * Fires when the active item changes
32275 * @param {Roo.bootstrap.NavProgressBar} this
32276 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32277 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32284 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32289 getAutoCreate : function()
32291 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32295 cls : 'roo-navigation-bar-group',
32299 cls : 'roo-navigation-top-bar'
32303 cls : 'roo-navigation-bullets-bar',
32307 cls : 'roo-navigation-bar'
32314 cls : 'roo-navigation-bottom-bar'
32324 initEvents: function()
32329 onRender : function(ct, position)
32331 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32333 if(this.bullets.length){
32334 Roo.each(this.bullets, function(b){
32343 addItem : function(cfg)
32345 var item = new Roo.bootstrap.NavProgressItem(cfg);
32347 item.parentId = this.id;
32348 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32351 var top = new Roo.bootstrap.Element({
32353 cls : 'roo-navigation-bar-text'
32356 var bottom = new Roo.bootstrap.Element({
32358 cls : 'roo-navigation-bar-text'
32361 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32362 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32364 var topText = new Roo.bootstrap.Element({
32366 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32369 var bottomText = new Roo.bootstrap.Element({
32371 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32374 topText.onRender(top.el, null);
32375 bottomText.onRender(bottom.el, null);
32378 item.bottomEl = bottom;
32381 this.barItems.push(item);
32386 getActive : function()
32388 var active = false;
32390 Roo.each(this.barItems, function(v){
32392 if (!v.isActive()) {
32404 setActiveItem : function(item)
32408 Roo.each(this.barItems, function(v){
32409 if (v.rid == item.rid) {
32413 if (v.isActive()) {
32414 v.setActive(false);
32419 item.setActive(true);
32421 this.fireEvent('changed', this, item, prev);
32424 getBarItem: function(rid)
32428 Roo.each(this.barItems, function(e) {
32429 if (e.rid != rid) {
32440 indexOfItem : function(item)
32444 Roo.each(this.barItems, function(v, i){
32446 if (v.rid != item.rid) {
32457 setActiveNext : function()
32459 var i = this.indexOfItem(this.getActive());
32461 if (i > this.barItems.length) {
32465 this.setActiveItem(this.barItems[i+1]);
32468 setActivePrev : function()
32470 var i = this.indexOfItem(this.getActive());
32476 this.setActiveItem(this.barItems[i-1]);
32479 format : function()
32481 if(!this.barItems.length){
32485 var width = 100 / this.barItems.length;
32487 Roo.each(this.barItems, function(i){
32488 i.el.setStyle('width', width + '%');
32489 i.topEl.el.setStyle('width', width + '%');
32490 i.bottomEl.el.setStyle('width', width + '%');
32499 * Nav Progress Item
32504 * @class Roo.bootstrap.NavProgressItem
32505 * @extends Roo.bootstrap.Component
32506 * Bootstrap NavProgressItem class
32507 * @cfg {String} rid the reference id
32508 * @cfg {Boolean} active (true|false) Is item active default false
32509 * @cfg {Boolean} disabled (true|false) Is item active default false
32510 * @cfg {String} html
32511 * @cfg {String} position (top|bottom) text position default bottom
32512 * @cfg {String} icon show icon instead of number
32515 * Create a new NavProgressItem
32516 * @param {Object} config The config object
32518 Roo.bootstrap.NavProgressItem = function(config){
32519 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32524 * The raw click event for the entire grid.
32525 * @param {Roo.bootstrap.NavProgressItem} this
32526 * @param {Roo.EventObject} e
32533 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32539 position : 'bottom',
32542 getAutoCreate : function()
32544 var iconCls = 'roo-navigation-bar-item-icon';
32546 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32550 cls: 'roo-navigation-bar-item',
32560 cfg.cls += ' active';
32563 cfg.cls += ' disabled';
32569 disable : function()
32571 this.setDisabled(true);
32574 enable : function()
32576 this.setDisabled(false);
32579 initEvents: function()
32581 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32583 this.iconEl.on('click', this.onClick, this);
32586 onClick : function(e)
32588 e.preventDefault();
32594 if(this.fireEvent('click', this, e) === false){
32598 this.parent().setActiveItem(this);
32601 isActive: function ()
32603 return this.active;
32606 setActive : function(state)
32608 if(this.active == state){
32612 this.active = state;
32615 this.el.addClass('active');
32619 this.el.removeClass('active');
32624 setDisabled : function(state)
32626 if(this.disabled == state){
32630 this.disabled = state;
32633 this.el.addClass('disabled');
32637 this.el.removeClass('disabled');
32640 tooltipEl : function()
32642 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32655 * @class Roo.bootstrap.FieldLabel
32656 * @extends Roo.bootstrap.Component
32657 * Bootstrap FieldLabel class
32658 * @cfg {String} html contents of the element
32659 * @cfg {String} tag tag of the element default label
32660 * @cfg {String} cls class of the element
32661 * @cfg {String} target label target
32662 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32663 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32664 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32665 * @cfg {String} iconTooltip default "This field is required"
32666 * @cfg {String} indicatorpos (left|right) default left
32669 * Create a new FieldLabel
32670 * @param {Object} config The config object
32673 Roo.bootstrap.FieldLabel = function(config){
32674 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32679 * Fires after the field has been marked as invalid.
32680 * @param {Roo.form.FieldLabel} this
32681 * @param {String} msg The validation message
32686 * Fires after the field has been validated with no errors.
32687 * @param {Roo.form.FieldLabel} this
32693 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32700 invalidClass : 'has-warning',
32701 validClass : 'has-success',
32702 iconTooltip : 'This field is required',
32703 indicatorpos : 'left',
32705 getAutoCreate : function(){
32708 if (!this.allowBlank) {
32714 cls : 'roo-bootstrap-field-label ' + this.cls,
32719 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32720 tooltip : this.iconTooltip
32729 if(this.indicatorpos == 'right'){
32732 cls : 'roo-bootstrap-field-label ' + this.cls,
32741 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32742 tooltip : this.iconTooltip
32751 initEvents: function()
32753 Roo.bootstrap.Element.superclass.initEvents.call(this);
32755 this.indicator = this.indicatorEl();
32757 if(this.indicator){
32758 this.indicator.removeClass('visible');
32759 this.indicator.addClass('invisible');
32762 Roo.bootstrap.FieldLabel.register(this);
32765 indicatorEl : function()
32767 var indicator = this.el.select('i.roo-required-indicator',true).first();
32778 * Mark this field as valid
32780 markValid : function()
32782 if(this.indicator){
32783 this.indicator.removeClass('visible');
32784 this.indicator.addClass('invisible');
32786 if (Roo.bootstrap.version == 3) {
32787 this.el.removeClass(this.invalidClass);
32788 this.el.addClass(this.validClass);
32790 this.el.removeClass('is-invalid');
32791 this.el.addClass('is-valid');
32795 this.fireEvent('valid', this);
32799 * Mark this field as invalid
32800 * @param {String} msg The validation message
32802 markInvalid : function(msg)
32804 if(this.indicator){
32805 this.indicator.removeClass('invisible');
32806 this.indicator.addClass('visible');
32808 if (Roo.bootstrap.version == 3) {
32809 this.el.removeClass(this.validClass);
32810 this.el.addClass(this.invalidClass);
32812 this.el.removeClass('is-valid');
32813 this.el.addClass('is-invalid');
32817 this.fireEvent('invalid', this, msg);
32823 Roo.apply(Roo.bootstrap.FieldLabel, {
32828 * register a FieldLabel Group
32829 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32831 register : function(label)
32833 if(this.groups.hasOwnProperty(label.target)){
32837 this.groups[label.target] = label;
32841 * fetch a FieldLabel Group based on the target
32842 * @param {string} target
32843 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32845 get: function(target) {
32846 if (typeof(this.groups[target]) == 'undefined') {
32850 return this.groups[target] ;
32859 * page DateSplitField.
32865 * @class Roo.bootstrap.DateSplitField
32866 * @extends Roo.bootstrap.Component
32867 * Bootstrap DateSplitField class
32868 * @cfg {string} fieldLabel - the label associated
32869 * @cfg {Number} labelWidth set the width of label (0-12)
32870 * @cfg {String} labelAlign (top|left)
32871 * @cfg {Boolean} dayAllowBlank (true|false) default false
32872 * @cfg {Boolean} monthAllowBlank (true|false) default false
32873 * @cfg {Boolean} yearAllowBlank (true|false) default false
32874 * @cfg {string} dayPlaceholder
32875 * @cfg {string} monthPlaceholder
32876 * @cfg {string} yearPlaceholder
32877 * @cfg {string} dayFormat default 'd'
32878 * @cfg {string} monthFormat default 'm'
32879 * @cfg {string} yearFormat default 'Y'
32880 * @cfg {Number} labellg set the width of label (1-12)
32881 * @cfg {Number} labelmd set the width of label (1-12)
32882 * @cfg {Number} labelsm set the width of label (1-12)
32883 * @cfg {Number} labelxs set the width of label (1-12)
32887 * Create a new DateSplitField
32888 * @param {Object} config The config object
32891 Roo.bootstrap.DateSplitField = function(config){
32892 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32898 * getting the data of years
32899 * @param {Roo.bootstrap.DateSplitField} this
32900 * @param {Object} years
32905 * getting the data of days
32906 * @param {Roo.bootstrap.DateSplitField} this
32907 * @param {Object} days
32912 * Fires after the field has been marked as invalid.
32913 * @param {Roo.form.Field} this
32914 * @param {String} msg The validation message
32919 * Fires after the field has been validated with no errors.
32920 * @param {Roo.form.Field} this
32926 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32929 labelAlign : 'top',
32931 dayAllowBlank : false,
32932 monthAllowBlank : false,
32933 yearAllowBlank : false,
32934 dayPlaceholder : '',
32935 monthPlaceholder : '',
32936 yearPlaceholder : '',
32940 isFormField : true,
32946 getAutoCreate : function()
32950 cls : 'row roo-date-split-field-group',
32955 cls : 'form-hidden-field roo-date-split-field-group-value',
32961 var labelCls = 'col-md-12';
32962 var contentCls = 'col-md-4';
32964 if(this.fieldLabel){
32968 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32972 html : this.fieldLabel
32977 if(this.labelAlign == 'left'){
32979 if(this.labelWidth > 12){
32980 label.style = "width: " + this.labelWidth + 'px';
32983 if(this.labelWidth < 13 && this.labelmd == 0){
32984 this.labelmd = this.labelWidth;
32987 if(this.labellg > 0){
32988 labelCls = ' col-lg-' + this.labellg;
32989 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32992 if(this.labelmd > 0){
32993 labelCls = ' col-md-' + this.labelmd;
32994 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32997 if(this.labelsm > 0){
32998 labelCls = ' col-sm-' + this.labelsm;
32999 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33002 if(this.labelxs > 0){
33003 labelCls = ' col-xs-' + this.labelxs;
33004 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33008 label.cls += ' ' + labelCls;
33010 cfg.cn.push(label);
33013 Roo.each(['day', 'month', 'year'], function(t){
33016 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33023 inputEl: function ()
33025 return this.el.select('.roo-date-split-field-group-value', true).first();
33028 onRender : function(ct, position)
33032 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33034 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33036 this.dayField = new Roo.bootstrap.ComboBox({
33037 allowBlank : this.dayAllowBlank,
33038 alwaysQuery : true,
33039 displayField : 'value',
33042 forceSelection : true,
33044 placeholder : this.dayPlaceholder,
33045 selectOnFocus : true,
33046 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33047 triggerAction : 'all',
33049 valueField : 'value',
33050 store : new Roo.data.SimpleStore({
33051 data : (function() {
33053 _this.fireEvent('days', _this, days);
33056 fields : [ 'value' ]
33059 select : function (_self, record, index)
33061 _this.setValue(_this.getValue());
33066 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33068 this.monthField = new Roo.bootstrap.MonthField({
33069 after : '<i class=\"fa fa-calendar\"></i>',
33070 allowBlank : this.monthAllowBlank,
33071 placeholder : this.monthPlaceholder,
33074 render : function (_self)
33076 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33077 e.preventDefault();
33081 select : function (_self, oldvalue, newvalue)
33083 _this.setValue(_this.getValue());
33088 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33090 this.yearField = new Roo.bootstrap.ComboBox({
33091 allowBlank : this.yearAllowBlank,
33092 alwaysQuery : true,
33093 displayField : 'value',
33096 forceSelection : true,
33098 placeholder : this.yearPlaceholder,
33099 selectOnFocus : true,
33100 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33101 triggerAction : 'all',
33103 valueField : 'value',
33104 store : new Roo.data.SimpleStore({
33105 data : (function() {
33107 _this.fireEvent('years', _this, years);
33110 fields : [ 'value' ]
33113 select : function (_self, record, index)
33115 _this.setValue(_this.getValue());
33120 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33123 setValue : function(v, format)
33125 this.inputEl.dom.value = v;
33127 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33129 var d = Date.parseDate(v, f);
33136 this.setDay(d.format(this.dayFormat));
33137 this.setMonth(d.format(this.monthFormat));
33138 this.setYear(d.format(this.yearFormat));
33145 setDay : function(v)
33147 this.dayField.setValue(v);
33148 this.inputEl.dom.value = this.getValue();
33153 setMonth : function(v)
33155 this.monthField.setValue(v, true);
33156 this.inputEl.dom.value = this.getValue();
33161 setYear : function(v)
33163 this.yearField.setValue(v);
33164 this.inputEl.dom.value = this.getValue();
33169 getDay : function()
33171 return this.dayField.getValue();
33174 getMonth : function()
33176 return this.monthField.getValue();
33179 getYear : function()
33181 return this.yearField.getValue();
33184 getValue : function()
33186 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33188 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33198 this.inputEl.dom.value = '';
33203 validate : function()
33205 var d = this.dayField.validate();
33206 var m = this.monthField.validate();
33207 var y = this.yearField.validate();
33212 (!this.dayAllowBlank && !d) ||
33213 (!this.monthAllowBlank && !m) ||
33214 (!this.yearAllowBlank && !y)
33219 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33228 this.markInvalid();
33233 markValid : function()
33236 var label = this.el.select('label', true).first();
33237 var icon = this.el.select('i.fa-star', true).first();
33243 this.fireEvent('valid', this);
33247 * Mark this field as invalid
33248 * @param {String} msg The validation message
33250 markInvalid : function(msg)
33253 var label = this.el.select('label', true).first();
33254 var icon = this.el.select('i.fa-star', true).first();
33256 if(label && !icon){
33257 this.el.select('.roo-date-split-field-label', true).createChild({
33259 cls : 'text-danger fa fa-lg fa-star',
33260 tooltip : 'This field is required',
33261 style : 'margin-right:5px;'
33265 this.fireEvent('invalid', this, msg);
33268 clearInvalid : function()
33270 var label = this.el.select('label', true).first();
33271 var icon = this.el.select('i.fa-star', true).first();
33277 this.fireEvent('valid', this);
33280 getName: function()
33290 * http://masonry.desandro.com
33292 * The idea is to render all the bricks based on vertical width...
33294 * The original code extends 'outlayer' - we might need to use that....
33300 * @class Roo.bootstrap.LayoutMasonry
33301 * @extends Roo.bootstrap.Component
33302 * Bootstrap Layout Masonry class
33305 * Create a new Element
33306 * @param {Object} config The config object
33309 Roo.bootstrap.LayoutMasonry = function(config){
33311 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33315 Roo.bootstrap.LayoutMasonry.register(this);
33321 * Fire after layout the items
33322 * @param {Roo.bootstrap.LayoutMasonry} this
33323 * @param {Roo.EventObject} e
33330 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33333 * @cfg {Boolean} isLayoutInstant = no animation?
33335 isLayoutInstant : false, // needed?
33338 * @cfg {Number} boxWidth width of the columns
33343 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33348 * @cfg {Number} padWidth padding below box..
33353 * @cfg {Number} gutter gutter width..
33358 * @cfg {Number} maxCols maximum number of columns
33364 * @cfg {Boolean} isAutoInitial defalut true
33366 isAutoInitial : true,
33371 * @cfg {Boolean} isHorizontal defalut false
33373 isHorizontal : false,
33375 currentSize : null,
33381 bricks: null, //CompositeElement
33385 _isLayoutInited : false,
33387 // isAlternative : false, // only use for vertical layout...
33390 * @cfg {Number} alternativePadWidth padding below box..
33392 alternativePadWidth : 50,
33394 selectedBrick : [],
33396 getAutoCreate : function(){
33398 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33402 cls: 'blog-masonary-wrapper ' + this.cls,
33404 cls : 'mas-boxes masonary'
33411 getChildContainer: function( )
33413 if (this.boxesEl) {
33414 return this.boxesEl;
33417 this.boxesEl = this.el.select('.mas-boxes').first();
33419 return this.boxesEl;
33423 initEvents : function()
33427 if(this.isAutoInitial){
33428 Roo.log('hook children rendered');
33429 this.on('childrenrendered', function() {
33430 Roo.log('children rendered');
33436 initial : function()
33438 this.selectedBrick = [];
33440 this.currentSize = this.el.getBox(true);
33442 Roo.EventManager.onWindowResize(this.resize, this);
33444 if(!this.isAutoInitial){
33452 //this.layout.defer(500,this);
33456 resize : function()
33458 var cs = this.el.getBox(true);
33461 this.currentSize.width == cs.width &&
33462 this.currentSize.x == cs.x &&
33463 this.currentSize.height == cs.height &&
33464 this.currentSize.y == cs.y
33466 Roo.log("no change in with or X or Y");
33470 this.currentSize = cs;
33476 layout : function()
33478 this._resetLayout();
33480 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33482 this.layoutItems( isInstant );
33484 this._isLayoutInited = true;
33486 this.fireEvent('layout', this);
33490 _resetLayout : function()
33492 if(this.isHorizontal){
33493 this.horizontalMeasureColumns();
33497 this.verticalMeasureColumns();
33501 verticalMeasureColumns : function()
33503 this.getContainerWidth();
33505 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33506 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33510 var boxWidth = this.boxWidth + this.padWidth;
33512 if(this.containerWidth < this.boxWidth){
33513 boxWidth = this.containerWidth
33516 var containerWidth = this.containerWidth;
33518 var cols = Math.floor(containerWidth / boxWidth);
33520 this.cols = Math.max( cols, 1 );
33522 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33524 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33526 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33528 this.colWidth = boxWidth + avail - this.padWidth;
33530 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33531 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33534 horizontalMeasureColumns : function()
33536 this.getContainerWidth();
33538 var boxWidth = this.boxWidth;
33540 if(this.containerWidth < boxWidth){
33541 boxWidth = this.containerWidth;
33544 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33546 this.el.setHeight(boxWidth);
33550 getContainerWidth : function()
33552 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33555 layoutItems : function( isInstant )
33557 Roo.log(this.bricks);
33559 var items = Roo.apply([], this.bricks);
33561 if(this.isHorizontal){
33562 this._horizontalLayoutItems( items , isInstant );
33566 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33567 // this._verticalAlternativeLayoutItems( items , isInstant );
33571 this._verticalLayoutItems( items , isInstant );
33575 _verticalLayoutItems : function ( items , isInstant)
33577 if ( !items || !items.length ) {
33582 ['xs', 'xs', 'xs', 'tall'],
33583 ['xs', 'xs', 'tall'],
33584 ['xs', 'xs', 'sm'],
33585 ['xs', 'xs', 'xs'],
33591 ['sm', 'xs', 'xs'],
33595 ['tall', 'xs', 'xs', 'xs'],
33596 ['tall', 'xs', 'xs'],
33608 Roo.each(items, function(item, k){
33610 switch (item.size) {
33611 // these layouts take up a full box,
33622 boxes.push([item]);
33645 var filterPattern = function(box, length)
33653 var pattern = box.slice(0, length);
33657 Roo.each(pattern, function(i){
33658 format.push(i.size);
33661 Roo.each(standard, function(s){
33663 if(String(s) != String(format)){
33672 if(!match && length == 1){
33677 filterPattern(box, length - 1);
33681 queue.push(pattern);
33683 box = box.slice(length, box.length);
33685 filterPattern(box, 4);
33691 Roo.each(boxes, function(box, k){
33697 if(box.length == 1){
33702 filterPattern(box, 4);
33706 this._processVerticalLayoutQueue( queue, isInstant );
33710 // _verticalAlternativeLayoutItems : function( items , isInstant )
33712 // if ( !items || !items.length ) {
33716 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33720 _horizontalLayoutItems : function ( items , isInstant)
33722 if ( !items || !items.length || items.length < 3) {
33728 var eItems = items.slice(0, 3);
33730 items = items.slice(3, items.length);
33733 ['xs', 'xs', 'xs', 'wide'],
33734 ['xs', 'xs', 'wide'],
33735 ['xs', 'xs', 'sm'],
33736 ['xs', 'xs', 'xs'],
33742 ['sm', 'xs', 'xs'],
33746 ['wide', 'xs', 'xs', 'xs'],
33747 ['wide', 'xs', 'xs'],
33760 Roo.each(items, function(item, k){
33762 switch (item.size) {
33773 boxes.push([item]);
33797 var filterPattern = function(box, length)
33805 var pattern = box.slice(0, length);
33809 Roo.each(pattern, function(i){
33810 format.push(i.size);
33813 Roo.each(standard, function(s){
33815 if(String(s) != String(format)){
33824 if(!match && length == 1){
33829 filterPattern(box, length - 1);
33833 queue.push(pattern);
33835 box = box.slice(length, box.length);
33837 filterPattern(box, 4);
33843 Roo.each(boxes, function(box, k){
33849 if(box.length == 1){
33854 filterPattern(box, 4);
33861 var pos = this.el.getBox(true);
33865 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33867 var hit_end = false;
33869 Roo.each(queue, function(box){
33873 Roo.each(box, function(b){
33875 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33885 Roo.each(box, function(b){
33887 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33890 mx = Math.max(mx, b.x);
33894 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33898 Roo.each(box, function(b){
33900 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33914 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33917 /** Sets position of item in DOM
33918 * @param {Element} item
33919 * @param {Number} x - horizontal position
33920 * @param {Number} y - vertical position
33921 * @param {Boolean} isInstant - disables transitions
33923 _processVerticalLayoutQueue : function( queue, isInstant )
33925 var pos = this.el.getBox(true);
33930 for (var i = 0; i < this.cols; i++){
33934 Roo.each(queue, function(box, k){
33936 var col = k % this.cols;
33938 Roo.each(box, function(b,kk){
33940 b.el.position('absolute');
33942 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33943 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33945 if(b.size == 'md-left' || b.size == 'md-right'){
33946 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33947 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33950 b.el.setWidth(width);
33951 b.el.setHeight(height);
33953 b.el.select('iframe',true).setSize(width,height);
33957 for (var i = 0; i < this.cols; i++){
33959 if(maxY[i] < maxY[col]){
33964 col = Math.min(col, i);
33968 x = pos.x + col * (this.colWidth + this.padWidth);
33972 var positions = [];
33974 switch (box.length){
33976 positions = this.getVerticalOneBoxColPositions(x, y, box);
33979 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33982 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33985 positions = this.getVerticalFourBoxColPositions(x, y, box);
33991 Roo.each(box, function(b,kk){
33993 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33995 var sz = b.el.getSize();
33997 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34005 for (var i = 0; i < this.cols; i++){
34006 mY = Math.max(mY, maxY[i]);
34009 this.el.setHeight(mY - pos.y);
34013 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34015 // var pos = this.el.getBox(true);
34018 // var maxX = pos.right;
34020 // var maxHeight = 0;
34022 // Roo.each(items, function(item, k){
34026 // item.el.position('absolute');
34028 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34030 // item.el.setWidth(width);
34032 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34034 // item.el.setHeight(height);
34037 // item.el.setXY([x, y], isInstant ? false : true);
34039 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34042 // y = y + height + this.alternativePadWidth;
34044 // maxHeight = maxHeight + height + this.alternativePadWidth;
34048 // this.el.setHeight(maxHeight);
34052 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34054 var pos = this.el.getBox(true);
34059 var maxX = pos.right;
34061 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34063 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34065 Roo.each(queue, function(box, k){
34067 Roo.each(box, function(b, kk){
34069 b.el.position('absolute');
34071 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34072 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34074 if(b.size == 'md-left' || b.size == 'md-right'){
34075 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34076 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34079 b.el.setWidth(width);
34080 b.el.setHeight(height);
34088 var positions = [];
34090 switch (box.length){
34092 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34095 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34098 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34101 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34107 Roo.each(box, function(b,kk){
34109 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34111 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34119 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34121 Roo.each(eItems, function(b,k){
34123 b.size = (k == 0) ? 'sm' : 'xs';
34124 b.x = (k == 0) ? 2 : 1;
34125 b.y = (k == 0) ? 2 : 1;
34127 b.el.position('absolute');
34129 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34131 b.el.setWidth(width);
34133 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34135 b.el.setHeight(height);
34139 var positions = [];
34142 x : maxX - this.unitWidth * 2 - this.gutter,
34147 x : maxX - this.unitWidth,
34148 y : minY + (this.unitWidth + this.gutter) * 2
34152 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34156 Roo.each(eItems, function(b,k){
34158 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34164 getVerticalOneBoxColPositions : function(x, y, box)
34168 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34170 if(box[0].size == 'md-left'){
34174 if(box[0].size == 'md-right'){
34179 x : x + (this.unitWidth + this.gutter) * rand,
34186 getVerticalTwoBoxColPositions : function(x, y, box)
34190 if(box[0].size == 'xs'){
34194 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34198 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34212 x : x + (this.unitWidth + this.gutter) * 2,
34213 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34220 getVerticalThreeBoxColPositions : function(x, y, box)
34224 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34232 x : x + (this.unitWidth + this.gutter) * 1,
34237 x : x + (this.unitWidth + this.gutter) * 2,
34245 if(box[0].size == 'xs' && box[1].size == 'xs'){
34254 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34258 x : x + (this.unitWidth + this.gutter) * 1,
34272 x : x + (this.unitWidth + this.gutter) * 2,
34277 x : x + (this.unitWidth + this.gutter) * 2,
34278 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34285 getVerticalFourBoxColPositions : function(x, y, box)
34289 if(box[0].size == 'xs'){
34298 y : y + (this.unitHeight + this.gutter) * 1
34303 y : y + (this.unitHeight + this.gutter) * 2
34307 x : x + (this.unitWidth + this.gutter) * 1,
34321 x : x + (this.unitWidth + this.gutter) * 2,
34326 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34327 y : y + (this.unitHeight + this.gutter) * 1
34331 x : x + (this.unitWidth + this.gutter) * 2,
34332 y : y + (this.unitWidth + this.gutter) * 2
34339 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34343 if(box[0].size == 'md-left'){
34345 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34352 if(box[0].size == 'md-right'){
34354 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34355 y : minY + (this.unitWidth + this.gutter) * 1
34361 var rand = Math.floor(Math.random() * (4 - box[0].y));
34364 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34365 y : minY + (this.unitWidth + this.gutter) * rand
34372 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34376 if(box[0].size == 'xs'){
34379 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34384 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34385 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34393 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34398 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34399 y : minY + (this.unitWidth + this.gutter) * 2
34406 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34410 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34413 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34418 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34419 y : minY + (this.unitWidth + this.gutter) * 1
34423 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34424 y : minY + (this.unitWidth + this.gutter) * 2
34431 if(box[0].size == 'xs' && box[1].size == 'xs'){
34434 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34439 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34444 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34445 y : minY + (this.unitWidth + this.gutter) * 1
34453 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34458 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34459 y : minY + (this.unitWidth + this.gutter) * 2
34463 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34464 y : minY + (this.unitWidth + this.gutter) * 2
34471 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34475 if(box[0].size == 'xs'){
34478 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
34488 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),
34493 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34494 y : minY + (this.unitWidth + this.gutter) * 1
34502 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34507 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
34513 y : minY + (this.unitWidth + this.gutter) * 2
34517 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),
34518 y : minY + (this.unitWidth + this.gutter) * 2
34526 * remove a Masonry Brick
34527 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34529 removeBrick : function(brick_id)
34535 for (var i = 0; i<this.bricks.length; i++) {
34536 if (this.bricks[i].id == brick_id) {
34537 this.bricks.splice(i,1);
34538 this.el.dom.removeChild(Roo.get(brick_id).dom);
34545 * adds a Masonry Brick
34546 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34548 addBrick : function(cfg)
34550 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34551 //this.register(cn);
34552 cn.parentId = this.id;
34553 cn.render(this.el);
34558 * register a Masonry Brick
34559 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34562 register : function(brick)
34564 this.bricks.push(brick);
34565 brick.masonryId = this.id;
34569 * clear all the Masonry Brick
34571 clearAll : function()
34574 //this.getChildContainer().dom.innerHTML = "";
34575 this.el.dom.innerHTML = '';
34578 getSelected : function()
34580 if (!this.selectedBrick) {
34584 return this.selectedBrick;
34588 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34592 * register a Masonry Layout
34593 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34596 register : function(layout)
34598 this.groups[layout.id] = layout;
34601 * fetch a Masonry Layout based on the masonry layout ID
34602 * @param {string} the masonry layout to add
34603 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34606 get: function(layout_id) {
34607 if (typeof(this.groups[layout_id]) == 'undefined') {
34610 return this.groups[layout_id] ;
34622 * http://masonry.desandro.com
34624 * The idea is to render all the bricks based on vertical width...
34626 * The original code extends 'outlayer' - we might need to use that....
34632 * @class Roo.bootstrap.LayoutMasonryAuto
34633 * @extends Roo.bootstrap.Component
34634 * Bootstrap Layout Masonry class
34637 * Create a new Element
34638 * @param {Object} config The config object
34641 Roo.bootstrap.LayoutMasonryAuto = function(config){
34642 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34645 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34648 * @cfg {Boolean} isFitWidth - resize the width..
34650 isFitWidth : false, // options..
34652 * @cfg {Boolean} isOriginLeft = left align?
34654 isOriginLeft : true,
34656 * @cfg {Boolean} isOriginTop = top align?
34658 isOriginTop : false,
34660 * @cfg {Boolean} isLayoutInstant = no animation?
34662 isLayoutInstant : false, // needed?
34664 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34666 isResizingContainer : true,
34668 * @cfg {Number} columnWidth width of the columns
34674 * @cfg {Number} maxCols maximum number of columns
34679 * @cfg {Number} padHeight padding below box..
34685 * @cfg {Boolean} isAutoInitial defalut true
34688 isAutoInitial : true,
34694 initialColumnWidth : 0,
34695 currentSize : null,
34697 colYs : null, // array.
34704 bricks: null, //CompositeElement
34705 cols : 0, // array?
34706 // element : null, // wrapped now this.el
34707 _isLayoutInited : null,
34710 getAutoCreate : function(){
34714 cls: 'blog-masonary-wrapper ' + this.cls,
34716 cls : 'mas-boxes masonary'
34723 getChildContainer: function( )
34725 if (this.boxesEl) {
34726 return this.boxesEl;
34729 this.boxesEl = this.el.select('.mas-boxes').first();
34731 return this.boxesEl;
34735 initEvents : function()
34739 if(this.isAutoInitial){
34740 Roo.log('hook children rendered');
34741 this.on('childrenrendered', function() {
34742 Roo.log('children rendered');
34749 initial : function()
34751 this.reloadItems();
34753 this.currentSize = this.el.getBox(true);
34755 /// was window resize... - let's see if this works..
34756 Roo.EventManager.onWindowResize(this.resize, this);
34758 if(!this.isAutoInitial){
34763 this.layout.defer(500,this);
34766 reloadItems: function()
34768 this.bricks = this.el.select('.masonry-brick', true);
34770 this.bricks.each(function(b) {
34771 //Roo.log(b.getSize());
34772 if (!b.attr('originalwidth')) {
34773 b.attr('originalwidth', b.getSize().width);
34778 Roo.log(this.bricks.elements.length);
34781 resize : function()
34784 var cs = this.el.getBox(true);
34786 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34787 Roo.log("no change in with or X");
34790 this.currentSize = cs;
34794 layout : function()
34797 this._resetLayout();
34798 //this._manageStamps();
34800 // don't animate first layout
34801 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34802 this.layoutItems( isInstant );
34804 // flag for initalized
34805 this._isLayoutInited = true;
34808 layoutItems : function( isInstant )
34810 //var items = this._getItemsForLayout( this.items );
34811 // original code supports filtering layout items.. we just ignore it..
34813 this._layoutItems( this.bricks , isInstant );
34815 this._postLayout();
34817 _layoutItems : function ( items , isInstant)
34819 //this.fireEvent( 'layout', this, items );
34822 if ( !items || !items.elements.length ) {
34823 // no items, emit event with empty array
34828 items.each(function(item) {
34829 Roo.log("layout item");
34831 // get x/y object from method
34832 var position = this._getItemLayoutPosition( item );
34834 position.item = item;
34835 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34836 queue.push( position );
34839 this._processLayoutQueue( queue );
34841 /** Sets position of item in DOM
34842 * @param {Element} item
34843 * @param {Number} x - horizontal position
34844 * @param {Number} y - vertical position
34845 * @param {Boolean} isInstant - disables transitions
34847 _processLayoutQueue : function( queue )
34849 for ( var i=0, len = queue.length; i < len; i++ ) {
34850 var obj = queue[i];
34851 obj.item.position('absolute');
34852 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34858 * Any logic you want to do after each layout,
34859 * i.e. size the container
34861 _postLayout : function()
34863 this.resizeContainer();
34866 resizeContainer : function()
34868 if ( !this.isResizingContainer ) {
34871 var size = this._getContainerSize();
34873 this.el.setSize(size.width,size.height);
34874 this.boxesEl.setSize(size.width,size.height);
34880 _resetLayout : function()
34882 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34883 this.colWidth = this.el.getWidth();
34884 //this.gutter = this.el.getWidth();
34886 this.measureColumns();
34892 this.colYs.push( 0 );
34898 measureColumns : function()
34900 this.getContainerWidth();
34901 // if columnWidth is 0, default to outerWidth of first item
34902 if ( !this.columnWidth ) {
34903 var firstItem = this.bricks.first();
34904 Roo.log(firstItem);
34905 this.columnWidth = this.containerWidth;
34906 if (firstItem && firstItem.attr('originalwidth') ) {
34907 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34909 // columnWidth fall back to item of first element
34910 Roo.log("set column width?");
34911 this.initialColumnWidth = this.columnWidth ;
34913 // if first elem has no width, default to size of container
34918 if (this.initialColumnWidth) {
34919 this.columnWidth = this.initialColumnWidth;
34924 // column width is fixed at the top - however if container width get's smaller we should
34927 // this bit calcs how man columns..
34929 var columnWidth = this.columnWidth += this.gutter;
34931 // calculate columns
34932 var containerWidth = this.containerWidth + this.gutter;
34934 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34935 // fix rounding errors, typically with gutters
34936 var excess = columnWidth - containerWidth % columnWidth;
34939 // if overshoot is less than a pixel, round up, otherwise floor it
34940 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34941 cols = Math[ mathMethod ]( cols );
34942 this.cols = Math.max( cols, 1 );
34943 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34945 // padding positioning..
34946 var totalColWidth = this.cols * this.columnWidth;
34947 var padavail = this.containerWidth - totalColWidth;
34948 // so for 2 columns - we need 3 'pads'
34950 var padNeeded = (1+this.cols) * this.padWidth;
34952 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34954 this.columnWidth += padExtra
34955 //this.padWidth = Math.floor(padavail / ( this.cols));
34957 // adjust colum width so that padding is fixed??
34959 // we have 3 columns ... total = width * 3
34960 // we have X left over... that should be used by
34962 //if (this.expandC) {
34970 getContainerWidth : function()
34972 /* // container is parent if fit width
34973 var container = this.isFitWidth ? this.element.parentNode : this.element;
34974 // check that this.size and size are there
34975 // IE8 triggers resize on body size change, so they might not be
34977 var size = getSize( container ); //FIXME
34978 this.containerWidth = size && size.innerWidth; //FIXME
34981 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34985 _getItemLayoutPosition : function( item ) // what is item?
34987 // we resize the item to our columnWidth..
34989 item.setWidth(this.columnWidth);
34990 item.autoBoxAdjust = false;
34992 var sz = item.getSize();
34994 // how many columns does this brick span
34995 var remainder = this.containerWidth % this.columnWidth;
34997 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34998 // round if off by 1 pixel, otherwise use ceil
34999 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35000 colSpan = Math.min( colSpan, this.cols );
35002 // normally this should be '1' as we dont' currently allow multi width columns..
35004 var colGroup = this._getColGroup( colSpan );
35005 // get the minimum Y value from the columns
35006 var minimumY = Math.min.apply( Math, colGroup );
35007 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35009 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35011 // position the brick
35013 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35014 y: this.currentSize.y + minimumY + this.padHeight
35018 // apply setHeight to necessary columns
35019 var setHeight = minimumY + sz.height + this.padHeight;
35020 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35022 var setSpan = this.cols + 1 - colGroup.length;
35023 for ( var i = 0; i < setSpan; i++ ) {
35024 this.colYs[ shortColIndex + i ] = setHeight ;
35031 * @param {Number} colSpan - number of columns the element spans
35032 * @returns {Array} colGroup
35034 _getColGroup : function( colSpan )
35036 if ( colSpan < 2 ) {
35037 // if brick spans only one column, use all the column Ys
35042 // how many different places could this brick fit horizontally
35043 var groupCount = this.cols + 1 - colSpan;
35044 // for each group potential horizontal position
35045 for ( var i = 0; i < groupCount; i++ ) {
35046 // make an array of colY values for that one group
35047 var groupColYs = this.colYs.slice( i, i + colSpan );
35048 // and get the max value of the array
35049 colGroup[i] = Math.max.apply( Math, groupColYs );
35054 _manageStamp : function( stamp )
35056 var stampSize = stamp.getSize();
35057 var offset = stamp.getBox();
35058 // get the columns that this stamp affects
35059 var firstX = this.isOriginLeft ? offset.x : offset.right;
35060 var lastX = firstX + stampSize.width;
35061 var firstCol = Math.floor( firstX / this.columnWidth );
35062 firstCol = Math.max( 0, firstCol );
35064 var lastCol = Math.floor( lastX / this.columnWidth );
35065 // lastCol should not go over if multiple of columnWidth #425
35066 lastCol -= lastX % this.columnWidth ? 0 : 1;
35067 lastCol = Math.min( this.cols - 1, lastCol );
35069 // set colYs to bottom of the stamp
35070 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35073 for ( var i = firstCol; i <= lastCol; i++ ) {
35074 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35079 _getContainerSize : function()
35081 this.maxY = Math.max.apply( Math, this.colYs );
35086 if ( this.isFitWidth ) {
35087 size.width = this._getContainerFitWidth();
35093 _getContainerFitWidth : function()
35095 var unusedCols = 0;
35096 // count unused columns
35099 if ( this.colYs[i] !== 0 ) {
35104 // fit container to columns that have been used
35105 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35108 needsResizeLayout : function()
35110 var previousWidth = this.containerWidth;
35111 this.getContainerWidth();
35112 return previousWidth !== this.containerWidth;
35127 * @class Roo.bootstrap.MasonryBrick
35128 * @extends Roo.bootstrap.Component
35129 * Bootstrap MasonryBrick class
35132 * Create a new MasonryBrick
35133 * @param {Object} config The config object
35136 Roo.bootstrap.MasonryBrick = function(config){
35138 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35140 Roo.bootstrap.MasonryBrick.register(this);
35146 * When a MasonryBrick is clcik
35147 * @param {Roo.bootstrap.MasonryBrick} this
35148 * @param {Roo.EventObject} e
35154 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35157 * @cfg {String} title
35161 * @cfg {String} html
35165 * @cfg {String} bgimage
35169 * @cfg {String} videourl
35173 * @cfg {String} cls
35177 * @cfg {String} href
35181 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35186 * @cfg {String} placetitle (center|bottom)
35191 * @cfg {Boolean} isFitContainer defalut true
35193 isFitContainer : true,
35196 * @cfg {Boolean} preventDefault defalut false
35198 preventDefault : false,
35201 * @cfg {Boolean} inverse defalut false
35203 maskInverse : false,
35205 getAutoCreate : function()
35207 if(!this.isFitContainer){
35208 return this.getSplitAutoCreate();
35211 var cls = 'masonry-brick masonry-brick-full';
35213 if(this.href.length){
35214 cls += ' masonry-brick-link';
35217 if(this.bgimage.length){
35218 cls += ' masonry-brick-image';
35221 if(this.maskInverse){
35222 cls += ' mask-inverse';
35225 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35226 cls += ' enable-mask';
35230 cls += ' masonry-' + this.size + '-brick';
35233 if(this.placetitle.length){
35235 switch (this.placetitle) {
35237 cls += ' masonry-center-title';
35240 cls += ' masonry-bottom-title';
35247 if(!this.html.length && !this.bgimage.length){
35248 cls += ' masonry-center-title';
35251 if(!this.html.length && this.bgimage.length){
35252 cls += ' masonry-bottom-title';
35257 cls += ' ' + this.cls;
35261 tag: (this.href.length) ? 'a' : 'div',
35266 cls: 'masonry-brick-mask'
35270 cls: 'masonry-brick-paragraph',
35276 if(this.href.length){
35277 cfg.href = this.href;
35280 var cn = cfg.cn[1].cn;
35282 if(this.title.length){
35285 cls: 'masonry-brick-title',
35290 if(this.html.length){
35293 cls: 'masonry-brick-text',
35298 if (!this.title.length && !this.html.length) {
35299 cfg.cn[1].cls += ' hide';
35302 if(this.bgimage.length){
35305 cls: 'masonry-brick-image-view',
35310 if(this.videourl.length){
35311 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35312 // youtube support only?
35315 cls: 'masonry-brick-image-view',
35318 allowfullscreen : true
35326 getSplitAutoCreate : function()
35328 var cls = 'masonry-brick masonry-brick-split';
35330 if(this.href.length){
35331 cls += ' masonry-brick-link';
35334 if(this.bgimage.length){
35335 cls += ' masonry-brick-image';
35339 cls += ' masonry-' + this.size + '-brick';
35342 switch (this.placetitle) {
35344 cls += ' masonry-center-title';
35347 cls += ' masonry-bottom-title';
35350 if(!this.bgimage.length){
35351 cls += ' masonry-center-title';
35354 if(this.bgimage.length){
35355 cls += ' masonry-bottom-title';
35361 cls += ' ' + this.cls;
35365 tag: (this.href.length) ? 'a' : 'div',
35370 cls: 'masonry-brick-split-head',
35374 cls: 'masonry-brick-paragraph',
35381 cls: 'masonry-brick-split-body',
35387 if(this.href.length){
35388 cfg.href = this.href;
35391 if(this.title.length){
35392 cfg.cn[0].cn[0].cn.push({
35394 cls: 'masonry-brick-title',
35399 if(this.html.length){
35400 cfg.cn[1].cn.push({
35402 cls: 'masonry-brick-text',
35407 if(this.bgimage.length){
35408 cfg.cn[0].cn.push({
35410 cls: 'masonry-brick-image-view',
35415 if(this.videourl.length){
35416 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35417 // youtube support only?
35418 cfg.cn[0].cn.cn.push({
35420 cls: 'masonry-brick-image-view',
35423 allowfullscreen : true
35430 initEvents: function()
35432 switch (this.size) {
35465 this.el.on('touchstart', this.onTouchStart, this);
35466 this.el.on('touchmove', this.onTouchMove, this);
35467 this.el.on('touchend', this.onTouchEnd, this);
35468 this.el.on('contextmenu', this.onContextMenu, this);
35470 this.el.on('mouseenter' ,this.enter, this);
35471 this.el.on('mouseleave', this.leave, this);
35472 this.el.on('click', this.onClick, this);
35475 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35476 this.parent().bricks.push(this);
35481 onClick: function(e, el)
35483 var time = this.endTimer - this.startTimer;
35484 // Roo.log(e.preventDefault());
35487 e.preventDefault();
35492 if(!this.preventDefault){
35496 e.preventDefault();
35498 if (this.activeClass != '') {
35499 this.selectBrick();
35502 this.fireEvent('click', this, e);
35505 enter: function(e, el)
35507 e.preventDefault();
35509 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35513 if(this.bgimage.length && this.html.length){
35514 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35518 leave: function(e, el)
35520 e.preventDefault();
35522 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35526 if(this.bgimage.length && this.html.length){
35527 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35531 onTouchStart: function(e, el)
35533 // e.preventDefault();
35535 this.touchmoved = false;
35537 if(!this.isFitContainer){
35541 if(!this.bgimage.length || !this.html.length){
35545 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35547 this.timer = new Date().getTime();
35551 onTouchMove: function(e, el)
35553 this.touchmoved = true;
35556 onContextMenu : function(e,el)
35558 e.preventDefault();
35559 e.stopPropagation();
35563 onTouchEnd: function(e, el)
35565 // e.preventDefault();
35567 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35574 if(!this.bgimage.length || !this.html.length){
35576 if(this.href.length){
35577 window.location.href = this.href;
35583 if(!this.isFitContainer){
35587 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35589 window.location.href = this.href;
35592 //selection on single brick only
35593 selectBrick : function() {
35595 if (!this.parentId) {
35599 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35600 var index = m.selectedBrick.indexOf(this.id);
35603 m.selectedBrick.splice(index,1);
35604 this.el.removeClass(this.activeClass);
35608 for(var i = 0; i < m.selectedBrick.length; i++) {
35609 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35610 b.el.removeClass(b.activeClass);
35613 m.selectedBrick = [];
35615 m.selectedBrick.push(this.id);
35616 this.el.addClass(this.activeClass);
35620 isSelected : function(){
35621 return this.el.hasClass(this.activeClass);
35626 Roo.apply(Roo.bootstrap.MasonryBrick, {
35629 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35631 * register a Masonry Brick
35632 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35635 register : function(brick)
35637 //this.groups[brick.id] = brick;
35638 this.groups.add(brick.id, brick);
35641 * fetch a masonry brick based on the masonry brick ID
35642 * @param {string} the masonry brick to add
35643 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35646 get: function(brick_id)
35648 // if (typeof(this.groups[brick_id]) == 'undefined') {
35651 // return this.groups[brick_id] ;
35653 if(this.groups.key(brick_id)) {
35654 return this.groups.key(brick_id);
35672 * @class Roo.bootstrap.Brick
35673 * @extends Roo.bootstrap.Component
35674 * Bootstrap Brick class
35677 * Create a new Brick
35678 * @param {Object} config The config object
35681 Roo.bootstrap.Brick = function(config){
35682 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35688 * When a Brick is click
35689 * @param {Roo.bootstrap.Brick} this
35690 * @param {Roo.EventObject} e
35696 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35699 * @cfg {String} title
35703 * @cfg {String} html
35707 * @cfg {String} bgimage
35711 * @cfg {String} cls
35715 * @cfg {String} href
35719 * @cfg {String} video
35723 * @cfg {Boolean} square
35727 getAutoCreate : function()
35729 var cls = 'roo-brick';
35731 if(this.href.length){
35732 cls += ' roo-brick-link';
35735 if(this.bgimage.length){
35736 cls += ' roo-brick-image';
35739 if(!this.html.length && !this.bgimage.length){
35740 cls += ' roo-brick-center-title';
35743 if(!this.html.length && this.bgimage.length){
35744 cls += ' roo-brick-bottom-title';
35748 cls += ' ' + this.cls;
35752 tag: (this.href.length) ? 'a' : 'div',
35757 cls: 'roo-brick-paragraph',
35763 if(this.href.length){
35764 cfg.href = this.href;
35767 var cn = cfg.cn[0].cn;
35769 if(this.title.length){
35772 cls: 'roo-brick-title',
35777 if(this.html.length){
35780 cls: 'roo-brick-text',
35787 if(this.bgimage.length){
35790 cls: 'roo-brick-image-view',
35798 initEvents: function()
35800 if(this.title.length || this.html.length){
35801 this.el.on('mouseenter' ,this.enter, this);
35802 this.el.on('mouseleave', this.leave, this);
35805 Roo.EventManager.onWindowResize(this.resize, this);
35807 if(this.bgimage.length){
35808 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35809 this.imageEl.on('load', this.onImageLoad, this);
35816 onImageLoad : function()
35821 resize : function()
35823 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35825 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35827 if(this.bgimage.length){
35828 var image = this.el.select('.roo-brick-image-view', true).first();
35830 image.setWidth(paragraph.getWidth());
35833 image.setHeight(paragraph.getWidth());
35836 this.el.setHeight(image.getHeight());
35837 paragraph.setHeight(image.getHeight());
35843 enter: function(e, el)
35845 e.preventDefault();
35847 if(this.bgimage.length){
35848 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35849 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35853 leave: function(e, el)
35855 e.preventDefault();
35857 if(this.bgimage.length){
35858 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35859 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35874 * @class Roo.bootstrap.NumberField
35875 * @extends Roo.bootstrap.Input
35876 * Bootstrap NumberField class
35882 * Create a new NumberField
35883 * @param {Object} config The config object
35886 Roo.bootstrap.NumberField = function(config){
35887 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35890 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35893 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35895 allowDecimals : true,
35897 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35899 decimalSeparator : ".",
35901 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35903 decimalPrecision : 2,
35905 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35907 allowNegative : true,
35910 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35914 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35916 minValue : Number.NEGATIVE_INFINITY,
35918 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35920 maxValue : Number.MAX_VALUE,
35922 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35924 minText : "The minimum value for this field is {0}",
35926 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35928 maxText : "The maximum value for this field is {0}",
35930 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35931 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35933 nanText : "{0} is not a valid number",
35935 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35937 thousandsDelimiter : false,
35939 * @cfg {String} valueAlign alignment of value
35941 valueAlign : "left",
35943 getAutoCreate : function()
35945 var hiddenInput = {
35949 cls: 'hidden-number-input'
35953 hiddenInput.name = this.name;
35958 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35960 this.name = hiddenInput.name;
35962 if(cfg.cn.length > 0) {
35963 cfg.cn.push(hiddenInput);
35970 initEvents : function()
35972 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35974 var allowed = "0123456789";
35976 if(this.allowDecimals){
35977 allowed += this.decimalSeparator;
35980 if(this.allowNegative){
35984 if(this.thousandsDelimiter) {
35988 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35990 var keyPress = function(e){
35992 var k = e.getKey();
35994 var c = e.getCharCode();
35997 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35998 allowed.indexOf(String.fromCharCode(c)) === -1
36004 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36008 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36013 this.el.on("keypress", keyPress, this);
36016 validateValue : function(value)
36019 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36023 var num = this.parseValue(value);
36026 this.markInvalid(String.format(this.nanText, value));
36030 if(num < this.minValue){
36031 this.markInvalid(String.format(this.minText, this.minValue));
36035 if(num > this.maxValue){
36036 this.markInvalid(String.format(this.maxText, this.maxValue));
36043 getValue : function()
36045 var v = this.hiddenEl().getValue();
36047 return this.fixPrecision(this.parseValue(v));
36050 parseValue : function(value)
36052 if(this.thousandsDelimiter) {
36054 r = new RegExp(",", "g");
36055 value = value.replace(r, "");
36058 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36059 return isNaN(value) ? '' : value;
36062 fixPrecision : function(value)
36064 if(this.thousandsDelimiter) {
36066 r = new RegExp(",", "g");
36067 value = value.replace(r, "");
36070 var nan = isNaN(value);
36072 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36073 return nan ? '' : value;
36075 return parseFloat(value).toFixed(this.decimalPrecision);
36078 setValue : function(v)
36080 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36086 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36088 this.inputEl().dom.value = (v == '') ? '' :
36089 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36091 if(!this.allowZero && v === '0') {
36092 this.hiddenEl().dom.value = '';
36093 this.inputEl().dom.value = '';
36100 decimalPrecisionFcn : function(v)
36102 return Math.floor(v);
36105 beforeBlur : function()
36107 var v = this.parseValue(this.getRawValue());
36109 if(v || v === 0 || v === ''){
36114 hiddenEl : function()
36116 return this.el.select('input.hidden-number-input',true).first();
36128 * @class Roo.bootstrap.DocumentSlider
36129 * @extends Roo.bootstrap.Component
36130 * Bootstrap DocumentSlider class
36133 * Create a new DocumentViewer
36134 * @param {Object} config The config object
36137 Roo.bootstrap.DocumentSlider = function(config){
36138 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36145 * Fire after initEvent
36146 * @param {Roo.bootstrap.DocumentSlider} this
36151 * Fire after update
36152 * @param {Roo.bootstrap.DocumentSlider} this
36158 * @param {Roo.bootstrap.DocumentSlider} this
36164 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36170 getAutoCreate : function()
36174 cls : 'roo-document-slider',
36178 cls : 'roo-document-slider-header',
36182 cls : 'roo-document-slider-header-title'
36188 cls : 'roo-document-slider-body',
36192 cls : 'roo-document-slider-prev',
36196 cls : 'fa fa-chevron-left'
36202 cls : 'roo-document-slider-thumb',
36206 cls : 'roo-document-slider-image'
36212 cls : 'roo-document-slider-next',
36216 cls : 'fa fa-chevron-right'
36228 initEvents : function()
36230 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36231 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36233 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36234 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36236 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36237 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36239 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36240 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36242 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36243 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36245 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36246 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36248 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36249 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36251 this.thumbEl.on('click', this.onClick, this);
36253 this.prevIndicator.on('click', this.prev, this);
36255 this.nextIndicator.on('click', this.next, this);
36259 initial : function()
36261 if(this.files.length){
36262 this.indicator = 1;
36266 this.fireEvent('initial', this);
36269 update : function()
36271 this.imageEl.attr('src', this.files[this.indicator - 1]);
36273 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36275 this.prevIndicator.show();
36277 if(this.indicator == 1){
36278 this.prevIndicator.hide();
36281 this.nextIndicator.show();
36283 if(this.indicator == this.files.length){
36284 this.nextIndicator.hide();
36287 this.thumbEl.scrollTo('top');
36289 this.fireEvent('update', this);
36292 onClick : function(e)
36294 e.preventDefault();
36296 this.fireEvent('click', this);
36301 e.preventDefault();
36303 this.indicator = Math.max(1, this.indicator - 1);
36310 e.preventDefault();
36312 this.indicator = Math.min(this.files.length, this.indicator + 1);
36326 * @class Roo.bootstrap.RadioSet
36327 * @extends Roo.bootstrap.Input
36328 * Bootstrap RadioSet class
36329 * @cfg {String} indicatorpos (left|right) default left
36330 * @cfg {Boolean} inline (true|false) inline the element (default true)
36331 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36333 * Create a new RadioSet
36334 * @param {Object} config The config object
36337 Roo.bootstrap.RadioSet = function(config){
36339 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36343 Roo.bootstrap.RadioSet.register(this);
36348 * Fires when the element is checked or unchecked.
36349 * @param {Roo.bootstrap.RadioSet} this This radio
36350 * @param {Roo.bootstrap.Radio} item The checked item
36355 * Fires when the element is click.
36356 * @param {Roo.bootstrap.RadioSet} this This radio set
36357 * @param {Roo.bootstrap.Radio} item The checked item
36358 * @param {Roo.EventObject} e The event object
36365 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36373 indicatorpos : 'left',
36375 getAutoCreate : function()
36379 cls : 'roo-radio-set-label',
36383 html : this.fieldLabel
36387 if (Roo.bootstrap.version == 3) {
36390 if(this.indicatorpos == 'left'){
36393 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36394 tooltip : 'This field is required'
36399 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36400 tooltip : 'This field is required'
36406 cls : 'roo-radio-set-items'
36409 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36411 if (align === 'left' && this.fieldLabel.length) {
36414 cls : "roo-radio-set-right",
36420 if(this.labelWidth > 12){
36421 label.style = "width: " + this.labelWidth + 'px';
36424 if(this.labelWidth < 13 && this.labelmd == 0){
36425 this.labelmd = this.labelWidth;
36428 if(this.labellg > 0){
36429 label.cls += ' col-lg-' + this.labellg;
36430 items.cls += ' col-lg-' + (12 - this.labellg);
36433 if(this.labelmd > 0){
36434 label.cls += ' col-md-' + this.labelmd;
36435 items.cls += ' col-md-' + (12 - this.labelmd);
36438 if(this.labelsm > 0){
36439 label.cls += ' col-sm-' + this.labelsm;
36440 items.cls += ' col-sm-' + (12 - this.labelsm);
36443 if(this.labelxs > 0){
36444 label.cls += ' col-xs-' + this.labelxs;
36445 items.cls += ' col-xs-' + (12 - this.labelxs);
36451 cls : 'roo-radio-set',
36455 cls : 'roo-radio-set-input',
36458 value : this.value ? this.value : ''
36465 if(this.weight.length){
36466 cfg.cls += ' roo-radio-' + this.weight;
36470 cfg.cls += ' roo-radio-set-inline';
36474 ['xs','sm','md','lg'].map(function(size){
36475 if (settings[size]) {
36476 cfg.cls += ' col-' + size + '-' + settings[size];
36484 initEvents : function()
36486 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36487 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36489 if(!this.fieldLabel.length){
36490 this.labelEl.hide();
36493 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36494 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36496 this.indicator = this.indicatorEl();
36498 if(this.indicator){
36499 this.indicator.addClass('invisible');
36502 this.originalValue = this.getValue();
36506 inputEl: function ()
36508 return this.el.select('.roo-radio-set-input', true).first();
36511 getChildContainer : function()
36513 return this.itemsEl;
36516 register : function(item)
36518 this.radioes.push(item);
36522 validate : function()
36524 if(this.getVisibilityEl().hasClass('hidden')){
36530 Roo.each(this.radioes, function(i){
36539 if(this.allowBlank) {
36543 if(this.disabled || valid){
36548 this.markInvalid();
36553 markValid : function()
36555 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36556 this.indicatorEl().removeClass('visible');
36557 this.indicatorEl().addClass('invisible');
36561 if (Roo.bootstrap.version == 3) {
36562 this.el.removeClass([this.invalidClass, this.validClass]);
36563 this.el.addClass(this.validClass);
36565 this.el.removeClass(['is-invalid','is-valid']);
36566 this.el.addClass(['is-valid']);
36568 this.fireEvent('valid', this);
36571 markInvalid : function(msg)
36573 if(this.allowBlank || this.disabled){
36577 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36578 this.indicatorEl().removeClass('invisible');
36579 this.indicatorEl().addClass('visible');
36581 if (Roo.bootstrap.version == 3) {
36582 this.el.removeClass([this.invalidClass, this.validClass]);
36583 this.el.addClass(this.invalidClass);
36585 this.el.removeClass(['is-invalid','is-valid']);
36586 this.el.addClass(['is-invalid']);
36589 this.fireEvent('invalid', this, msg);
36593 setValue : function(v, suppressEvent)
36595 if(this.value === v){
36602 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36605 Roo.each(this.radioes, function(i){
36607 i.el.removeClass('checked');
36610 Roo.each(this.radioes, function(i){
36612 if(i.value === v || i.value.toString() === v.toString()){
36614 i.el.addClass('checked');
36616 if(suppressEvent !== true){
36617 this.fireEvent('check', this, i);
36628 clearInvalid : function(){
36630 if(!this.el || this.preventMark){
36634 this.el.removeClass([this.invalidClass]);
36636 this.fireEvent('valid', this);
36641 Roo.apply(Roo.bootstrap.RadioSet, {
36645 register : function(set)
36647 this.groups[set.name] = set;
36650 get: function(name)
36652 if (typeof(this.groups[name]) == 'undefined') {
36656 return this.groups[name] ;
36662 * Ext JS Library 1.1.1
36663 * Copyright(c) 2006-2007, Ext JS, LLC.
36665 * Originally Released Under LGPL - original licence link has changed is not relivant.
36668 * <script type="text/javascript">
36673 * @class Roo.bootstrap.SplitBar
36674 * @extends Roo.util.Observable
36675 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36679 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36680 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36681 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36682 split.minSize = 100;
36683 split.maxSize = 600;
36684 split.animate = true;
36685 split.on('moved', splitterMoved);
36688 * Create a new SplitBar
36689 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36690 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36691 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36692 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36693 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36694 position of the SplitBar).
36696 Roo.bootstrap.SplitBar = function(cfg){
36701 // dragElement : elm
36702 // resizingElement: el,
36704 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36705 // placement : Roo.bootstrap.SplitBar.LEFT ,
36706 // existingProxy ???
36709 this.el = Roo.get(cfg.dragElement, true);
36710 this.el.dom.unselectable = "on";
36712 this.resizingEl = Roo.get(cfg.resizingElement, true);
36716 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36717 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36720 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36723 * The minimum size of the resizing element. (Defaults to 0)
36729 * The maximum size of the resizing element. (Defaults to 2000)
36732 this.maxSize = 2000;
36735 * Whether to animate the transition to the new size
36738 this.animate = false;
36741 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36744 this.useShim = false;
36749 if(!cfg.existingProxy){
36751 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36753 this.proxy = Roo.get(cfg.existingProxy).dom;
36756 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36759 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36762 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36765 this.dragSpecs = {};
36768 * @private The adapter to use to positon and resize elements
36770 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36771 this.adapter.init(this);
36773 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36775 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36776 this.el.addClass("roo-splitbar-h");
36779 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36780 this.el.addClass("roo-splitbar-v");
36786 * Fires when the splitter is moved (alias for {@link #event-moved})
36787 * @param {Roo.bootstrap.SplitBar} this
36788 * @param {Number} newSize the new width or height
36793 * Fires when the splitter is moved
36794 * @param {Roo.bootstrap.SplitBar} this
36795 * @param {Number} newSize the new width or height
36799 * @event beforeresize
36800 * Fires before the splitter is dragged
36801 * @param {Roo.bootstrap.SplitBar} this
36803 "beforeresize" : true,
36805 "beforeapply" : true
36808 Roo.util.Observable.call(this);
36811 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36812 onStartProxyDrag : function(x, y){
36813 this.fireEvent("beforeresize", this);
36815 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36817 o.enableDisplayMode("block");
36818 // all splitbars share the same overlay
36819 Roo.bootstrap.SplitBar.prototype.overlay = o;
36821 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36822 this.overlay.show();
36823 Roo.get(this.proxy).setDisplayed("block");
36824 var size = this.adapter.getElementSize(this);
36825 this.activeMinSize = this.getMinimumSize();;
36826 this.activeMaxSize = this.getMaximumSize();;
36827 var c1 = size - this.activeMinSize;
36828 var c2 = Math.max(this.activeMaxSize - size, 0);
36829 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36830 this.dd.resetConstraints();
36831 this.dd.setXConstraint(
36832 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36833 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36835 this.dd.setYConstraint(0, 0);
36837 this.dd.resetConstraints();
36838 this.dd.setXConstraint(0, 0);
36839 this.dd.setYConstraint(
36840 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36841 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36844 this.dragSpecs.startSize = size;
36845 this.dragSpecs.startPoint = [x, y];
36846 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36850 * @private Called after the drag operation by the DDProxy
36852 onEndProxyDrag : function(e){
36853 Roo.get(this.proxy).setDisplayed(false);
36854 var endPoint = Roo.lib.Event.getXY(e);
36856 this.overlay.hide();
36859 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36860 newSize = this.dragSpecs.startSize +
36861 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36862 endPoint[0] - this.dragSpecs.startPoint[0] :
36863 this.dragSpecs.startPoint[0] - endPoint[0]
36866 newSize = this.dragSpecs.startSize +
36867 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36868 endPoint[1] - this.dragSpecs.startPoint[1] :
36869 this.dragSpecs.startPoint[1] - endPoint[1]
36872 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36873 if(newSize != this.dragSpecs.startSize){
36874 if(this.fireEvent('beforeapply', this, newSize) !== false){
36875 this.adapter.setElementSize(this, newSize);
36876 this.fireEvent("moved", this, newSize);
36877 this.fireEvent("resize", this, newSize);
36883 * Get the adapter this SplitBar uses
36884 * @return The adapter object
36886 getAdapter : function(){
36887 return this.adapter;
36891 * Set the adapter this SplitBar uses
36892 * @param {Object} adapter A SplitBar adapter object
36894 setAdapter : function(adapter){
36895 this.adapter = adapter;
36896 this.adapter.init(this);
36900 * Gets the minimum size for the resizing element
36901 * @return {Number} The minimum size
36903 getMinimumSize : function(){
36904 return this.minSize;
36908 * Sets the minimum size for the resizing element
36909 * @param {Number} minSize The minimum size
36911 setMinimumSize : function(minSize){
36912 this.minSize = minSize;
36916 * Gets the maximum size for the resizing element
36917 * @return {Number} The maximum size
36919 getMaximumSize : function(){
36920 return this.maxSize;
36924 * Sets the maximum size for the resizing element
36925 * @param {Number} maxSize The maximum size
36927 setMaximumSize : function(maxSize){
36928 this.maxSize = maxSize;
36932 * Sets the initialize size for the resizing element
36933 * @param {Number} size The initial size
36935 setCurrentSize : function(size){
36936 var oldAnimate = this.animate;
36937 this.animate = false;
36938 this.adapter.setElementSize(this, size);
36939 this.animate = oldAnimate;
36943 * Destroy this splitbar.
36944 * @param {Boolean} removeEl True to remove the element
36946 destroy : function(removeEl){
36948 this.shim.remove();
36951 this.proxy.parentNode.removeChild(this.proxy);
36959 * @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.
36961 Roo.bootstrap.SplitBar.createProxy = function(dir){
36962 var proxy = new Roo.Element(document.createElement("div"));
36963 proxy.unselectable();
36964 var cls = 'roo-splitbar-proxy';
36965 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36966 document.body.appendChild(proxy.dom);
36971 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36972 * Default Adapter. It assumes the splitter and resizing element are not positioned
36973 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36975 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36978 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36979 // do nothing for now
36980 init : function(s){
36984 * Called before drag operations to get the current size of the resizing element.
36985 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36987 getElementSize : function(s){
36988 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36989 return s.resizingEl.getWidth();
36991 return s.resizingEl.getHeight();
36996 * Called after drag operations to set the size of the resizing element.
36997 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36998 * @param {Number} newSize The new size to set
36999 * @param {Function} onComplete A function to be invoked when resizing is complete
37001 setElementSize : function(s, newSize, onComplete){
37002 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37004 s.resizingEl.setWidth(newSize);
37006 onComplete(s, newSize);
37009 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37014 s.resizingEl.setHeight(newSize);
37016 onComplete(s, newSize);
37019 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37026 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37027 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37028 * Adapter that moves the splitter element to align with the resized sizing element.
37029 * Used with an absolute positioned SplitBar.
37030 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37031 * document.body, make sure you assign an id to the body element.
37033 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37034 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37035 this.container = Roo.get(container);
37038 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37039 init : function(s){
37040 this.basic.init(s);
37043 getElementSize : function(s){
37044 return this.basic.getElementSize(s);
37047 setElementSize : function(s, newSize, onComplete){
37048 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37051 moveSplitter : function(s){
37052 var yes = Roo.bootstrap.SplitBar;
37053 switch(s.placement){
37055 s.el.setX(s.resizingEl.getRight());
37058 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37061 s.el.setY(s.resizingEl.getBottom());
37064 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37071 * Orientation constant - Create a vertical SplitBar
37075 Roo.bootstrap.SplitBar.VERTICAL = 1;
37078 * Orientation constant - Create a horizontal SplitBar
37082 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37085 * Placement constant - The resizing element is to the left of the splitter element
37089 Roo.bootstrap.SplitBar.LEFT = 1;
37092 * Placement constant - The resizing element is to the right of the splitter element
37096 Roo.bootstrap.SplitBar.RIGHT = 2;
37099 * Placement constant - The resizing element is positioned above the splitter element
37103 Roo.bootstrap.SplitBar.TOP = 3;
37106 * Placement constant - The resizing element is positioned under splitter element
37110 Roo.bootstrap.SplitBar.BOTTOM = 4;
37111 Roo.namespace("Roo.bootstrap.layout");/*
37113 * Ext JS Library 1.1.1
37114 * Copyright(c) 2006-2007, Ext JS, LLC.
37116 * Originally Released Under LGPL - original licence link has changed is not relivant.
37119 * <script type="text/javascript">
37123 * @class Roo.bootstrap.layout.Manager
37124 * @extends Roo.bootstrap.Component
37125 * Base class for layout managers.
37127 Roo.bootstrap.layout.Manager = function(config)
37129 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37135 /** false to disable window resize monitoring @type Boolean */
37136 this.monitorWindowResize = true;
37141 * Fires when a layout is performed.
37142 * @param {Roo.LayoutManager} this
37146 * @event regionresized
37147 * Fires when the user resizes a region.
37148 * @param {Roo.LayoutRegion} region The resized region
37149 * @param {Number} newSize The new size (width for east/west, height for north/south)
37151 "regionresized" : true,
37153 * @event regioncollapsed
37154 * Fires when a region is collapsed.
37155 * @param {Roo.LayoutRegion} region The collapsed region
37157 "regioncollapsed" : true,
37159 * @event regionexpanded
37160 * Fires when a region is expanded.
37161 * @param {Roo.LayoutRegion} region The expanded region
37163 "regionexpanded" : true
37165 this.updating = false;
37168 this.el = Roo.get(config.el);
37174 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37179 monitorWindowResize : true,
37185 onRender : function(ct, position)
37188 this.el = Roo.get(ct);
37191 //this.fireEvent('render',this);
37195 initEvents: function()
37199 // ie scrollbar fix
37200 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37201 document.body.scroll = "no";
37202 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37203 this.el.position('relative');
37205 this.id = this.el.id;
37206 this.el.addClass("roo-layout-container");
37207 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37208 if(this.el.dom != document.body ) {
37209 this.el.on('resize', this.layout,this);
37210 this.el.on('show', this.layout,this);
37216 * Returns true if this layout is currently being updated
37217 * @return {Boolean}
37219 isUpdating : function(){
37220 return this.updating;
37224 * Suspend the LayoutManager from doing auto-layouts while
37225 * making multiple add or remove calls
37227 beginUpdate : function(){
37228 this.updating = true;
37232 * Restore auto-layouts and optionally disable the manager from performing a layout
37233 * @param {Boolean} noLayout true to disable a layout update
37235 endUpdate : function(noLayout){
37236 this.updating = false;
37242 layout: function(){
37246 onRegionResized : function(region, newSize){
37247 this.fireEvent("regionresized", region, newSize);
37251 onRegionCollapsed : function(region){
37252 this.fireEvent("regioncollapsed", region);
37255 onRegionExpanded : function(region){
37256 this.fireEvent("regionexpanded", region);
37260 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37261 * performs box-model adjustments.
37262 * @return {Object} The size as an object {width: (the width), height: (the height)}
37264 getViewSize : function()
37267 if(this.el.dom != document.body){
37268 size = this.el.getSize();
37270 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37272 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37273 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37278 * Returns the Element this layout is bound to.
37279 * @return {Roo.Element}
37281 getEl : function(){
37286 * Returns the specified region.
37287 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37288 * @return {Roo.LayoutRegion}
37290 getRegion : function(target){
37291 return this.regions[target.toLowerCase()];
37294 onWindowResize : function(){
37295 if(this.monitorWindowResize){
37302 * Ext JS Library 1.1.1
37303 * Copyright(c) 2006-2007, Ext JS, LLC.
37305 * Originally Released Under LGPL - original licence link has changed is not relivant.
37308 * <script type="text/javascript">
37311 * @class Roo.bootstrap.layout.Border
37312 * @extends Roo.bootstrap.layout.Manager
37313 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37314 * please see: examples/bootstrap/nested.html<br><br>
37316 <b>The container the layout is rendered into can be either the body element or any other element.
37317 If it is not the body element, the container needs to either be an absolute positioned element,
37318 or you will need to add "position:relative" to the css of the container. You will also need to specify
37319 the container size if it is not the body element.</b>
37322 * Create a new Border
37323 * @param {Object} config Configuration options
37325 Roo.bootstrap.layout.Border = function(config){
37326 config = config || {};
37327 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37331 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37332 if(config[region]){
37333 config[region].region = region;
37334 this.addRegion(config[region]);
37340 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37342 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37344 parent : false, // this might point to a 'nest' or a ???
37347 * Creates and adds a new region if it doesn't already exist.
37348 * @param {String} target The target region key (north, south, east, west or center).
37349 * @param {Object} config The regions config object
37350 * @return {BorderLayoutRegion} The new region
37352 addRegion : function(config)
37354 if(!this.regions[config.region]){
37355 var r = this.factory(config);
37356 this.bindRegion(r);
37358 return this.regions[config.region];
37362 bindRegion : function(r){
37363 this.regions[r.config.region] = r;
37365 r.on("visibilitychange", this.layout, this);
37366 r.on("paneladded", this.layout, this);
37367 r.on("panelremoved", this.layout, this);
37368 r.on("invalidated", this.layout, this);
37369 r.on("resized", this.onRegionResized, this);
37370 r.on("collapsed", this.onRegionCollapsed, this);
37371 r.on("expanded", this.onRegionExpanded, this);
37375 * Performs a layout update.
37377 layout : function()
37379 if(this.updating) {
37383 // render all the rebions if they have not been done alreayd?
37384 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37385 if(this.regions[region] && !this.regions[region].bodyEl){
37386 this.regions[region].onRender(this.el)
37390 var size = this.getViewSize();
37391 var w = size.width;
37392 var h = size.height;
37397 //var x = 0, y = 0;
37399 var rs = this.regions;
37400 var north = rs["north"];
37401 var south = rs["south"];
37402 var west = rs["west"];
37403 var east = rs["east"];
37404 var center = rs["center"];
37405 //if(this.hideOnLayout){ // not supported anymore
37406 //c.el.setStyle("display", "none");
37408 if(north && north.isVisible()){
37409 var b = north.getBox();
37410 var m = north.getMargins();
37411 b.width = w - (m.left+m.right);
37414 centerY = b.height + b.y + m.bottom;
37415 centerH -= centerY;
37416 north.updateBox(this.safeBox(b));
37418 if(south && south.isVisible()){
37419 var b = south.getBox();
37420 var m = south.getMargins();
37421 b.width = w - (m.left+m.right);
37423 var totalHeight = (b.height + m.top + m.bottom);
37424 b.y = h - totalHeight + m.top;
37425 centerH -= totalHeight;
37426 south.updateBox(this.safeBox(b));
37428 if(west && west.isVisible()){
37429 var b = west.getBox();
37430 var m = west.getMargins();
37431 b.height = centerH - (m.top+m.bottom);
37433 b.y = centerY + m.top;
37434 var totalWidth = (b.width + m.left + m.right);
37435 centerX += totalWidth;
37436 centerW -= totalWidth;
37437 west.updateBox(this.safeBox(b));
37439 if(east && east.isVisible()){
37440 var b = east.getBox();
37441 var m = east.getMargins();
37442 b.height = centerH - (m.top+m.bottom);
37443 var totalWidth = (b.width + m.left + m.right);
37444 b.x = w - totalWidth + m.left;
37445 b.y = centerY + m.top;
37446 centerW -= totalWidth;
37447 east.updateBox(this.safeBox(b));
37450 var m = center.getMargins();
37452 x: centerX + m.left,
37453 y: centerY + m.top,
37454 width: centerW - (m.left+m.right),
37455 height: centerH - (m.top+m.bottom)
37457 //if(this.hideOnLayout){
37458 //center.el.setStyle("display", "block");
37460 center.updateBox(this.safeBox(centerBox));
37463 this.fireEvent("layout", this);
37467 safeBox : function(box){
37468 box.width = Math.max(0, box.width);
37469 box.height = Math.max(0, box.height);
37474 * Adds a ContentPanel (or subclass) to this layout.
37475 * @param {String} target The target region key (north, south, east, west or center).
37476 * @param {Roo.ContentPanel} panel The panel to add
37477 * @return {Roo.ContentPanel} The added panel
37479 add : function(target, panel){
37481 target = target.toLowerCase();
37482 return this.regions[target].add(panel);
37486 * Remove a ContentPanel (or subclass) to this layout.
37487 * @param {String} target The target region key (north, south, east, west or center).
37488 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37489 * @return {Roo.ContentPanel} The removed panel
37491 remove : function(target, panel){
37492 target = target.toLowerCase();
37493 return this.regions[target].remove(panel);
37497 * Searches all regions for a panel with the specified id
37498 * @param {String} panelId
37499 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37501 findPanel : function(panelId){
37502 var rs = this.regions;
37503 for(var target in rs){
37504 if(typeof rs[target] != "function"){
37505 var p = rs[target].getPanel(panelId);
37515 * Searches all regions for a panel with the specified id and activates (shows) it.
37516 * @param {String/ContentPanel} panelId The panels id or the panel itself
37517 * @return {Roo.ContentPanel} The shown panel or null
37519 showPanel : function(panelId) {
37520 var rs = this.regions;
37521 for(var target in rs){
37522 var r = rs[target];
37523 if(typeof r != "function"){
37524 if(r.hasPanel(panelId)){
37525 return r.showPanel(panelId);
37533 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37534 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37537 restoreState : function(provider){
37539 provider = Roo.state.Manager;
37541 var sm = new Roo.LayoutStateManager();
37542 sm.init(this, provider);
37548 * Adds a xtype elements to the layout.
37552 xtype : 'ContentPanel',
37559 xtype : 'NestedLayoutPanel',
37565 items : [ ... list of content panels or nested layout panels.. ]
37569 * @param {Object} cfg Xtype definition of item to add.
37571 addxtype : function(cfg)
37573 // basically accepts a pannel...
37574 // can accept a layout region..!?!?
37575 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37578 // theory? children can only be panels??
37580 //if (!cfg.xtype.match(/Panel$/)) {
37585 if (typeof(cfg.region) == 'undefined') {
37586 Roo.log("Failed to add Panel, region was not set");
37590 var region = cfg.region;
37596 xitems = cfg.items;
37601 if ( region == 'center') {
37602 Roo.log("Center: " + cfg.title);
37608 case 'Content': // ContentPanel (el, cfg)
37609 case 'Scroll': // ContentPanel (el, cfg)
37611 cfg.autoCreate = cfg.autoCreate || true;
37612 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37614 // var el = this.el.createChild();
37615 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37618 this.add(region, ret);
37622 case 'TreePanel': // our new panel!
37623 cfg.el = this.el.createChild();
37624 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37625 this.add(region, ret);
37630 // create a new Layout (which is a Border Layout...
37632 var clayout = cfg.layout;
37633 clayout.el = this.el.createChild();
37634 clayout.items = clayout.items || [];
37638 // replace this exitems with the clayout ones..
37639 xitems = clayout.items;
37641 // force background off if it's in center...
37642 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37643 cfg.background = false;
37645 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37648 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37649 //console.log('adding nested layout panel ' + cfg.toSource());
37650 this.add(region, ret);
37651 nb = {}; /// find first...
37656 // needs grid and region
37658 //var el = this.getRegion(region).el.createChild();
37660 *var el = this.el.createChild();
37661 // create the grid first...
37662 cfg.grid.container = el;
37663 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37666 if (region == 'center' && this.active ) {
37667 cfg.background = false;
37670 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37672 this.add(region, ret);
37674 if (cfg.background) {
37675 // render grid on panel activation (if panel background)
37676 ret.on('activate', function(gp) {
37677 if (!gp.grid.rendered) {
37678 // gp.grid.render(el);
37682 // cfg.grid.render(el);
37688 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37689 // it was the old xcomponent building that caused this before.
37690 // espeically if border is the top element in the tree.
37700 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37702 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37703 this.add(region, ret);
37707 throw "Can not add '" + cfg.xtype + "' to Border";
37713 this.beginUpdate();
37717 Roo.each(xitems, function(i) {
37718 region = nb && i.region ? i.region : false;
37720 var add = ret.addxtype(i);
37723 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37724 if (!i.background) {
37725 abn[region] = nb[region] ;
37732 // make the last non-background panel active..
37733 //if (nb) { Roo.log(abn); }
37736 for(var r in abn) {
37737 region = this.getRegion(r);
37739 // tried using nb[r], but it does not work..
37741 region.showPanel(abn[r]);
37752 factory : function(cfg)
37755 var validRegions = Roo.bootstrap.layout.Border.regions;
37757 var target = cfg.region;
37760 var r = Roo.bootstrap.layout;
37764 return new r.North(cfg);
37766 return new r.South(cfg);
37768 return new r.East(cfg);
37770 return new r.West(cfg);
37772 return new r.Center(cfg);
37774 throw 'Layout region "'+target+'" not supported.';
37781 * Ext JS Library 1.1.1
37782 * Copyright(c) 2006-2007, Ext JS, LLC.
37784 * Originally Released Under LGPL - original licence link has changed is not relivant.
37787 * <script type="text/javascript">
37791 * @class Roo.bootstrap.layout.Basic
37792 * @extends Roo.util.Observable
37793 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37794 * and does not have a titlebar, tabs or any other features. All it does is size and position
37795 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37796 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37797 * @cfg {string} region the region that it inhabits..
37798 * @cfg {bool} skipConfig skip config?
37802 Roo.bootstrap.layout.Basic = function(config){
37804 this.mgr = config.mgr;
37806 this.position = config.region;
37808 var skipConfig = config.skipConfig;
37812 * @scope Roo.BasicLayoutRegion
37816 * @event beforeremove
37817 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37818 * @param {Roo.LayoutRegion} this
37819 * @param {Roo.ContentPanel} panel The panel
37820 * @param {Object} e The cancel event object
37822 "beforeremove" : true,
37824 * @event invalidated
37825 * Fires when the layout for this region is changed.
37826 * @param {Roo.LayoutRegion} this
37828 "invalidated" : true,
37830 * @event visibilitychange
37831 * Fires when this region is shown or hidden
37832 * @param {Roo.LayoutRegion} this
37833 * @param {Boolean} visibility true or false
37835 "visibilitychange" : true,
37837 * @event paneladded
37838 * Fires when a panel is added.
37839 * @param {Roo.LayoutRegion} this
37840 * @param {Roo.ContentPanel} panel The panel
37842 "paneladded" : true,
37844 * @event panelremoved
37845 * Fires when a panel is removed.
37846 * @param {Roo.LayoutRegion} this
37847 * @param {Roo.ContentPanel} panel The panel
37849 "panelremoved" : true,
37851 * @event beforecollapse
37852 * Fires when this region before collapse.
37853 * @param {Roo.LayoutRegion} this
37855 "beforecollapse" : true,
37858 * Fires when this region is collapsed.
37859 * @param {Roo.LayoutRegion} this
37861 "collapsed" : true,
37864 * Fires when this region is expanded.
37865 * @param {Roo.LayoutRegion} this
37870 * Fires when this region is slid into view.
37871 * @param {Roo.LayoutRegion} this
37873 "slideshow" : true,
37876 * Fires when this region slides out of view.
37877 * @param {Roo.LayoutRegion} this
37879 "slidehide" : true,
37881 * @event panelactivated
37882 * Fires when a panel is activated.
37883 * @param {Roo.LayoutRegion} this
37884 * @param {Roo.ContentPanel} panel The activated panel
37886 "panelactivated" : true,
37889 * Fires when the user resizes this region.
37890 * @param {Roo.LayoutRegion} this
37891 * @param {Number} newSize The new size (width for east/west, height for north/south)
37895 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37896 this.panels = new Roo.util.MixedCollection();
37897 this.panels.getKey = this.getPanelId.createDelegate(this);
37899 this.activePanel = null;
37900 // ensure listeners are added...
37902 if (config.listeners || config.events) {
37903 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37904 listeners : config.listeners || {},
37905 events : config.events || {}
37909 if(skipConfig !== true){
37910 this.applyConfig(config);
37914 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37916 getPanelId : function(p){
37920 applyConfig : function(config){
37921 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37922 this.config = config;
37927 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37928 * the width, for horizontal (north, south) the height.
37929 * @param {Number} newSize The new width or height
37931 resizeTo : function(newSize){
37932 var el = this.el ? this.el :
37933 (this.activePanel ? this.activePanel.getEl() : null);
37935 switch(this.position){
37938 el.setWidth(newSize);
37939 this.fireEvent("resized", this, newSize);
37943 el.setHeight(newSize);
37944 this.fireEvent("resized", this, newSize);
37950 getBox : function(){
37951 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37954 getMargins : function(){
37955 return this.margins;
37958 updateBox : function(box){
37960 var el = this.activePanel.getEl();
37961 el.dom.style.left = box.x + "px";
37962 el.dom.style.top = box.y + "px";
37963 this.activePanel.setSize(box.width, box.height);
37967 * Returns the container element for this region.
37968 * @return {Roo.Element}
37970 getEl : function(){
37971 return this.activePanel;
37975 * Returns true if this region is currently visible.
37976 * @return {Boolean}
37978 isVisible : function(){
37979 return this.activePanel ? true : false;
37982 setActivePanel : function(panel){
37983 panel = this.getPanel(panel);
37984 if(this.activePanel && this.activePanel != panel){
37985 this.activePanel.setActiveState(false);
37986 this.activePanel.getEl().setLeftTop(-10000,-10000);
37988 this.activePanel = panel;
37989 panel.setActiveState(true);
37991 panel.setSize(this.box.width, this.box.height);
37993 this.fireEvent("panelactivated", this, panel);
37994 this.fireEvent("invalidated");
37998 * Show the specified panel.
37999 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38000 * @return {Roo.ContentPanel} The shown panel or null
38002 showPanel : function(panel){
38003 panel = this.getPanel(panel);
38005 this.setActivePanel(panel);
38011 * Get the active panel for this region.
38012 * @return {Roo.ContentPanel} The active panel or null
38014 getActivePanel : function(){
38015 return this.activePanel;
38019 * Add the passed ContentPanel(s)
38020 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38021 * @return {Roo.ContentPanel} The panel added (if only one was added)
38023 add : function(panel){
38024 if(arguments.length > 1){
38025 for(var i = 0, len = arguments.length; i < len; i++) {
38026 this.add(arguments[i]);
38030 if(this.hasPanel(panel)){
38031 this.showPanel(panel);
38034 var el = panel.getEl();
38035 if(el.dom.parentNode != this.mgr.el.dom){
38036 this.mgr.el.dom.appendChild(el.dom);
38038 if(panel.setRegion){
38039 panel.setRegion(this);
38041 this.panels.add(panel);
38042 el.setStyle("position", "absolute");
38043 if(!panel.background){
38044 this.setActivePanel(panel);
38045 if(this.config.initialSize && this.panels.getCount()==1){
38046 this.resizeTo(this.config.initialSize);
38049 this.fireEvent("paneladded", this, panel);
38054 * Returns true if the panel is in this region.
38055 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38056 * @return {Boolean}
38058 hasPanel : function(panel){
38059 if(typeof panel == "object"){ // must be panel obj
38060 panel = panel.getId();
38062 return this.getPanel(panel) ? true : false;
38066 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38067 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38068 * @param {Boolean} preservePanel Overrides the config preservePanel option
38069 * @return {Roo.ContentPanel} The panel that was removed
38071 remove : function(panel, preservePanel){
38072 panel = this.getPanel(panel);
38077 this.fireEvent("beforeremove", this, panel, e);
38078 if(e.cancel === true){
38081 var panelId = panel.getId();
38082 this.panels.removeKey(panelId);
38087 * Returns the panel specified or null if it's not in this region.
38088 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38089 * @return {Roo.ContentPanel}
38091 getPanel : function(id){
38092 if(typeof id == "object"){ // must be panel obj
38095 return this.panels.get(id);
38099 * Returns this regions position (north/south/east/west/center).
38102 getPosition: function(){
38103 return this.position;
38107 * Ext JS Library 1.1.1
38108 * Copyright(c) 2006-2007, Ext JS, LLC.
38110 * Originally Released Under LGPL - original licence link has changed is not relivant.
38113 * <script type="text/javascript">
38117 * @class Roo.bootstrap.layout.Region
38118 * @extends Roo.bootstrap.layout.Basic
38119 * This class represents a region in a layout manager.
38121 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38122 * @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})
38123 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38124 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38125 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38126 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38127 * @cfg {String} title The title for the region (overrides panel titles)
38128 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38129 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38130 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38131 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38132 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38133 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38134 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38135 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38136 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38137 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38139 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38140 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38141 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38142 * @cfg {Number} width For East/West panels
38143 * @cfg {Number} height For North/South panels
38144 * @cfg {Boolean} split To show the splitter
38145 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38147 * @cfg {string} cls Extra CSS classes to add to region
38149 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38150 * @cfg {string} region the region that it inhabits..
38153 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38154 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38156 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38157 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38158 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38160 Roo.bootstrap.layout.Region = function(config)
38162 this.applyConfig(config);
38164 var mgr = config.mgr;
38165 var pos = config.region;
38166 config.skipConfig = true;
38167 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38170 this.onRender(mgr.el);
38173 this.visible = true;
38174 this.collapsed = false;
38175 this.unrendered_panels = [];
38178 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38180 position: '', // set by wrapper (eg. north/south etc..)
38181 unrendered_panels : null, // unrendered panels.
38183 tabPosition : false,
38185 mgr: false, // points to 'Border'
38188 createBody : function(){
38189 /** This region's body element
38190 * @type Roo.Element */
38191 this.bodyEl = this.el.createChild({
38193 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38197 onRender: function(ctr, pos)
38199 var dh = Roo.DomHelper;
38200 /** This region's container element
38201 * @type Roo.Element */
38202 this.el = dh.append(ctr.dom, {
38204 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38206 /** This region's title element
38207 * @type Roo.Element */
38209 this.titleEl = dh.append(this.el.dom, {
38211 unselectable: "on",
38212 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38214 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38215 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38219 this.titleEl.enableDisplayMode();
38220 /** This region's title text element
38221 * @type HTMLElement */
38222 this.titleTextEl = this.titleEl.dom.firstChild;
38223 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38225 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38226 this.closeBtn.enableDisplayMode();
38227 this.closeBtn.on("click", this.closeClicked, this);
38228 this.closeBtn.hide();
38230 this.createBody(this.config);
38231 if(this.config.hideWhenEmpty){
38233 this.on("paneladded", this.validateVisibility, this);
38234 this.on("panelremoved", this.validateVisibility, this);
38236 if(this.autoScroll){
38237 this.bodyEl.setStyle("overflow", "auto");
38239 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38241 //if(c.titlebar !== false){
38242 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38243 this.titleEl.hide();
38245 this.titleEl.show();
38246 if(this.config.title){
38247 this.titleTextEl.innerHTML = this.config.title;
38251 if(this.config.collapsed){
38252 this.collapse(true);
38254 if(this.config.hidden){
38258 if (this.unrendered_panels && this.unrendered_panels.length) {
38259 for (var i =0;i< this.unrendered_panels.length; i++) {
38260 this.add(this.unrendered_panels[i]);
38262 this.unrendered_panels = null;
38268 applyConfig : function(c)
38271 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38272 var dh = Roo.DomHelper;
38273 if(c.titlebar !== false){
38274 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38275 this.collapseBtn.on("click", this.collapse, this);
38276 this.collapseBtn.enableDisplayMode();
38278 if(c.showPin === true || this.showPin){
38279 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38280 this.stickBtn.enableDisplayMode();
38281 this.stickBtn.on("click", this.expand, this);
38282 this.stickBtn.hide();
38287 /** This region's collapsed element
38288 * @type Roo.Element */
38291 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38292 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38295 if(c.floatable !== false){
38296 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38297 this.collapsedEl.on("click", this.collapseClick, this);
38300 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38301 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38302 id: "message", unselectable: "on", style:{"float":"left"}});
38303 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38305 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38306 this.expandBtn.on("click", this.expand, this);
38310 if(this.collapseBtn){
38311 this.collapseBtn.setVisible(c.collapsible == true);
38314 this.cmargins = c.cmargins || this.cmargins ||
38315 (this.position == "west" || this.position == "east" ?
38316 {top: 0, left: 2, right:2, bottom: 0} :
38317 {top: 2, left: 0, right:0, bottom: 2});
38319 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38322 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38324 this.autoScroll = c.autoScroll || false;
38329 this.duration = c.duration || .30;
38330 this.slideDuration = c.slideDuration || .45;
38335 * Returns true if this region is currently visible.
38336 * @return {Boolean}
38338 isVisible : function(){
38339 return this.visible;
38343 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38344 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38346 //setCollapsedTitle : function(title){
38347 // title = title || " ";
38348 // if(this.collapsedTitleTextEl){
38349 // this.collapsedTitleTextEl.innerHTML = title;
38353 getBox : function(){
38355 // if(!this.collapsed){
38356 b = this.el.getBox(false, true);
38358 // b = this.collapsedEl.getBox(false, true);
38363 getMargins : function(){
38364 return this.margins;
38365 //return this.collapsed ? this.cmargins : this.margins;
38368 highlight : function(){
38369 this.el.addClass("x-layout-panel-dragover");
38372 unhighlight : function(){
38373 this.el.removeClass("x-layout-panel-dragover");
38376 updateBox : function(box)
38378 if (!this.bodyEl) {
38379 return; // not rendered yet..
38383 if(!this.collapsed){
38384 this.el.dom.style.left = box.x + "px";
38385 this.el.dom.style.top = box.y + "px";
38386 this.updateBody(box.width, box.height);
38388 this.collapsedEl.dom.style.left = box.x + "px";
38389 this.collapsedEl.dom.style.top = box.y + "px";
38390 this.collapsedEl.setSize(box.width, box.height);
38393 this.tabs.autoSizeTabs();
38397 updateBody : function(w, h)
38400 this.el.setWidth(w);
38401 w -= this.el.getBorderWidth("rl");
38402 if(this.config.adjustments){
38403 w += this.config.adjustments[0];
38406 if(h !== null && h > 0){
38407 this.el.setHeight(h);
38408 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38409 h -= this.el.getBorderWidth("tb");
38410 if(this.config.adjustments){
38411 h += this.config.adjustments[1];
38413 this.bodyEl.setHeight(h);
38415 h = this.tabs.syncHeight(h);
38418 if(this.panelSize){
38419 w = w !== null ? w : this.panelSize.width;
38420 h = h !== null ? h : this.panelSize.height;
38422 if(this.activePanel){
38423 var el = this.activePanel.getEl();
38424 w = w !== null ? w : el.getWidth();
38425 h = h !== null ? h : el.getHeight();
38426 this.panelSize = {width: w, height: h};
38427 this.activePanel.setSize(w, h);
38429 if(Roo.isIE && this.tabs){
38430 this.tabs.el.repaint();
38435 * Returns the container element for this region.
38436 * @return {Roo.Element}
38438 getEl : function(){
38443 * Hides this region.
38446 //if(!this.collapsed){
38447 this.el.dom.style.left = "-2000px";
38450 // this.collapsedEl.dom.style.left = "-2000px";
38451 // this.collapsedEl.hide();
38453 this.visible = false;
38454 this.fireEvent("visibilitychange", this, false);
38458 * Shows this region if it was previously hidden.
38461 //if(!this.collapsed){
38464 // this.collapsedEl.show();
38466 this.visible = true;
38467 this.fireEvent("visibilitychange", this, true);
38470 closeClicked : function(){
38471 if(this.activePanel){
38472 this.remove(this.activePanel);
38476 collapseClick : function(e){
38478 e.stopPropagation();
38481 e.stopPropagation();
38487 * Collapses this region.
38488 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38491 collapse : function(skipAnim, skipCheck = false){
38492 if(this.collapsed) {
38496 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38498 this.collapsed = true;
38500 this.split.el.hide();
38502 if(this.config.animate && skipAnim !== true){
38503 this.fireEvent("invalidated", this);
38504 this.animateCollapse();
38506 this.el.setLocation(-20000,-20000);
38508 this.collapsedEl.show();
38509 this.fireEvent("collapsed", this);
38510 this.fireEvent("invalidated", this);
38516 animateCollapse : function(){
38521 * Expands this region if it was previously collapsed.
38522 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38523 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38526 expand : function(e, skipAnim){
38528 e.stopPropagation();
38530 if(!this.collapsed || this.el.hasActiveFx()) {
38534 this.afterSlideIn();
38537 this.collapsed = false;
38538 if(this.config.animate && skipAnim !== true){
38539 this.animateExpand();
38543 this.split.el.show();
38545 this.collapsedEl.setLocation(-2000,-2000);
38546 this.collapsedEl.hide();
38547 this.fireEvent("invalidated", this);
38548 this.fireEvent("expanded", this);
38552 animateExpand : function(){
38556 initTabs : function()
38558 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38560 var ts = new Roo.bootstrap.panel.Tabs({
38561 el: this.bodyEl.dom,
38563 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38564 disableTooltips: this.config.disableTabTips,
38565 toolbar : this.config.toolbar
38568 if(this.config.hideTabs){
38569 ts.stripWrap.setDisplayed(false);
38572 ts.resizeTabs = this.config.resizeTabs === true;
38573 ts.minTabWidth = this.config.minTabWidth || 40;
38574 ts.maxTabWidth = this.config.maxTabWidth || 250;
38575 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38576 ts.monitorResize = false;
38577 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38578 ts.bodyEl.addClass('roo-layout-tabs-body');
38579 this.panels.each(this.initPanelAsTab, this);
38582 initPanelAsTab : function(panel){
38583 var ti = this.tabs.addTab(
38587 this.config.closeOnTab && panel.isClosable(),
38590 if(panel.tabTip !== undefined){
38591 ti.setTooltip(panel.tabTip);
38593 ti.on("activate", function(){
38594 this.setActivePanel(panel);
38597 if(this.config.closeOnTab){
38598 ti.on("beforeclose", function(t, e){
38600 this.remove(panel);
38604 panel.tabItem = ti;
38609 updatePanelTitle : function(panel, title)
38611 if(this.activePanel == panel){
38612 this.updateTitle(title);
38615 var ti = this.tabs.getTab(panel.getEl().id);
38617 if(panel.tabTip !== undefined){
38618 ti.setTooltip(panel.tabTip);
38623 updateTitle : function(title){
38624 if(this.titleTextEl && !this.config.title){
38625 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38629 setActivePanel : function(panel)
38631 panel = this.getPanel(panel);
38632 if(this.activePanel && this.activePanel != panel){
38633 if(this.activePanel.setActiveState(false) === false){
38637 this.activePanel = panel;
38638 panel.setActiveState(true);
38639 if(this.panelSize){
38640 panel.setSize(this.panelSize.width, this.panelSize.height);
38643 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38645 this.updateTitle(panel.getTitle());
38647 this.fireEvent("invalidated", this);
38649 this.fireEvent("panelactivated", this, panel);
38653 * Shows the specified panel.
38654 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38655 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38657 showPanel : function(panel)
38659 panel = this.getPanel(panel);
38662 var tab = this.tabs.getTab(panel.getEl().id);
38663 if(tab.isHidden()){
38664 this.tabs.unhideTab(tab.id);
38668 this.setActivePanel(panel);
38675 * Get the active panel for this region.
38676 * @return {Roo.ContentPanel} The active panel or null
38678 getActivePanel : function(){
38679 return this.activePanel;
38682 validateVisibility : function(){
38683 if(this.panels.getCount() < 1){
38684 this.updateTitle(" ");
38685 this.closeBtn.hide();
38688 if(!this.isVisible()){
38695 * Adds the passed ContentPanel(s) to this region.
38696 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38697 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38699 add : function(panel)
38701 if(arguments.length > 1){
38702 for(var i = 0, len = arguments.length; i < len; i++) {
38703 this.add(arguments[i]);
38708 // if we have not been rendered yet, then we can not really do much of this..
38709 if (!this.bodyEl) {
38710 this.unrendered_panels.push(panel);
38717 if(this.hasPanel(panel)){
38718 this.showPanel(panel);
38721 panel.setRegion(this);
38722 this.panels.add(panel);
38723 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38724 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38725 // and hide them... ???
38726 this.bodyEl.dom.appendChild(panel.getEl().dom);
38727 if(panel.background !== true){
38728 this.setActivePanel(panel);
38730 this.fireEvent("paneladded", this, panel);
38737 this.initPanelAsTab(panel);
38741 if(panel.background !== true){
38742 this.tabs.activate(panel.getEl().id);
38744 this.fireEvent("paneladded", this, panel);
38749 * Hides the tab for the specified panel.
38750 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38752 hidePanel : function(panel){
38753 if(this.tabs && (panel = this.getPanel(panel))){
38754 this.tabs.hideTab(panel.getEl().id);
38759 * Unhides the tab for a previously hidden panel.
38760 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38762 unhidePanel : function(panel){
38763 if(this.tabs && (panel = this.getPanel(panel))){
38764 this.tabs.unhideTab(panel.getEl().id);
38768 clearPanels : function(){
38769 while(this.panels.getCount() > 0){
38770 this.remove(this.panels.first());
38775 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38776 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38777 * @param {Boolean} preservePanel Overrides the config preservePanel option
38778 * @return {Roo.ContentPanel} The panel that was removed
38780 remove : function(panel, preservePanel)
38782 panel = this.getPanel(panel);
38787 this.fireEvent("beforeremove", this, panel, e);
38788 if(e.cancel === true){
38791 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38792 var panelId = panel.getId();
38793 this.panels.removeKey(panelId);
38795 document.body.appendChild(panel.getEl().dom);
38798 this.tabs.removeTab(panel.getEl().id);
38799 }else if (!preservePanel){
38800 this.bodyEl.dom.removeChild(panel.getEl().dom);
38802 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38803 var p = this.panels.first();
38804 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38805 tempEl.appendChild(p.getEl().dom);
38806 this.bodyEl.update("");
38807 this.bodyEl.dom.appendChild(p.getEl().dom);
38809 this.updateTitle(p.getTitle());
38811 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38812 this.setActivePanel(p);
38814 panel.setRegion(null);
38815 if(this.activePanel == panel){
38816 this.activePanel = null;
38818 if(this.config.autoDestroy !== false && preservePanel !== true){
38819 try{panel.destroy();}catch(e){}
38821 this.fireEvent("panelremoved", this, panel);
38826 * Returns the TabPanel component used by this region
38827 * @return {Roo.TabPanel}
38829 getTabs : function(){
38833 createTool : function(parentEl, className){
38834 var btn = Roo.DomHelper.append(parentEl, {
38836 cls: "x-layout-tools-button",
38839 cls: "roo-layout-tools-button-inner " + className,
38843 btn.addClassOnOver("roo-layout-tools-button-over");
38848 * Ext JS Library 1.1.1
38849 * Copyright(c) 2006-2007, Ext JS, LLC.
38851 * Originally Released Under LGPL - original licence link has changed is not relivant.
38854 * <script type="text/javascript">
38860 * @class Roo.SplitLayoutRegion
38861 * @extends Roo.LayoutRegion
38862 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38864 Roo.bootstrap.layout.Split = function(config){
38865 this.cursor = config.cursor;
38866 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38869 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38871 splitTip : "Drag to resize.",
38872 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38873 useSplitTips : false,
38875 applyConfig : function(config){
38876 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38879 onRender : function(ctr,pos) {
38881 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38882 if(!this.config.split){
38887 var splitEl = Roo.DomHelper.append(ctr.dom, {
38889 id: this.el.id + "-split",
38890 cls: "roo-layout-split roo-layout-split-"+this.position,
38893 /** The SplitBar for this region
38894 * @type Roo.SplitBar */
38895 // does not exist yet...
38896 Roo.log([this.position, this.orientation]);
38898 this.split = new Roo.bootstrap.SplitBar({
38899 dragElement : splitEl,
38900 resizingElement: this.el,
38901 orientation : this.orientation
38904 this.split.on("moved", this.onSplitMove, this);
38905 this.split.useShim = this.config.useShim === true;
38906 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38907 if(this.useSplitTips){
38908 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38910 //if(config.collapsible){
38911 // this.split.el.on("dblclick", this.collapse, this);
38914 if(typeof this.config.minSize != "undefined"){
38915 this.split.minSize = this.config.minSize;
38917 if(typeof this.config.maxSize != "undefined"){
38918 this.split.maxSize = this.config.maxSize;
38920 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38921 this.hideSplitter();
38926 getHMaxSize : function(){
38927 var cmax = this.config.maxSize || 10000;
38928 var center = this.mgr.getRegion("center");
38929 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38932 getVMaxSize : function(){
38933 var cmax = this.config.maxSize || 10000;
38934 var center = this.mgr.getRegion("center");
38935 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38938 onSplitMove : function(split, newSize){
38939 this.fireEvent("resized", this, newSize);
38943 * Returns the {@link Roo.SplitBar} for this region.
38944 * @return {Roo.SplitBar}
38946 getSplitBar : function(){
38951 this.hideSplitter();
38952 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38955 hideSplitter : function(){
38957 this.split.el.setLocation(-2000,-2000);
38958 this.split.el.hide();
38964 this.split.el.show();
38966 Roo.bootstrap.layout.Split.superclass.show.call(this);
38969 beforeSlide: function(){
38970 if(Roo.isGecko){// firefox overflow auto bug workaround
38971 this.bodyEl.clip();
38973 this.tabs.bodyEl.clip();
38975 if(this.activePanel){
38976 this.activePanel.getEl().clip();
38978 if(this.activePanel.beforeSlide){
38979 this.activePanel.beforeSlide();
38985 afterSlide : function(){
38986 if(Roo.isGecko){// firefox overflow auto bug workaround
38987 this.bodyEl.unclip();
38989 this.tabs.bodyEl.unclip();
38991 if(this.activePanel){
38992 this.activePanel.getEl().unclip();
38993 if(this.activePanel.afterSlide){
38994 this.activePanel.afterSlide();
39000 initAutoHide : function(){
39001 if(this.autoHide !== false){
39002 if(!this.autoHideHd){
39003 var st = new Roo.util.DelayedTask(this.slideIn, this);
39004 this.autoHideHd = {
39005 "mouseout": function(e){
39006 if(!e.within(this.el, true)){
39010 "mouseover" : function(e){
39016 this.el.on(this.autoHideHd);
39020 clearAutoHide : function(){
39021 if(this.autoHide !== false){
39022 this.el.un("mouseout", this.autoHideHd.mouseout);
39023 this.el.un("mouseover", this.autoHideHd.mouseover);
39027 clearMonitor : function(){
39028 Roo.get(document).un("click", this.slideInIf, this);
39031 // these names are backwards but not changed for compat
39032 slideOut : function(){
39033 if(this.isSlid || this.el.hasActiveFx()){
39036 this.isSlid = true;
39037 if(this.collapseBtn){
39038 this.collapseBtn.hide();
39040 this.closeBtnState = this.closeBtn.getStyle('display');
39041 this.closeBtn.hide();
39043 this.stickBtn.show();
39046 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39047 this.beforeSlide();
39048 this.el.setStyle("z-index", 10001);
39049 this.el.slideIn(this.getSlideAnchor(), {
39050 callback: function(){
39052 this.initAutoHide();
39053 Roo.get(document).on("click", this.slideInIf, this);
39054 this.fireEvent("slideshow", this);
39061 afterSlideIn : function(){
39062 this.clearAutoHide();
39063 this.isSlid = false;
39064 this.clearMonitor();
39065 this.el.setStyle("z-index", "");
39066 if(this.collapseBtn){
39067 this.collapseBtn.show();
39069 this.closeBtn.setStyle('display', this.closeBtnState);
39071 this.stickBtn.hide();
39073 this.fireEvent("slidehide", this);
39076 slideIn : function(cb){
39077 if(!this.isSlid || this.el.hasActiveFx()){
39081 this.isSlid = false;
39082 this.beforeSlide();
39083 this.el.slideOut(this.getSlideAnchor(), {
39084 callback: function(){
39085 this.el.setLeftTop(-10000, -10000);
39087 this.afterSlideIn();
39095 slideInIf : function(e){
39096 if(!e.within(this.el)){
39101 animateCollapse : function(){
39102 this.beforeSlide();
39103 this.el.setStyle("z-index", 20000);
39104 var anchor = this.getSlideAnchor();
39105 this.el.slideOut(anchor, {
39106 callback : function(){
39107 this.el.setStyle("z-index", "");
39108 this.collapsedEl.slideIn(anchor, {duration:.3});
39110 this.el.setLocation(-10000,-10000);
39112 this.fireEvent("collapsed", this);
39119 animateExpand : function(){
39120 this.beforeSlide();
39121 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39122 this.el.setStyle("z-index", 20000);
39123 this.collapsedEl.hide({
39126 this.el.slideIn(this.getSlideAnchor(), {
39127 callback : function(){
39128 this.el.setStyle("z-index", "");
39131 this.split.el.show();
39133 this.fireEvent("invalidated", this);
39134 this.fireEvent("expanded", this);
39162 getAnchor : function(){
39163 return this.anchors[this.position];
39166 getCollapseAnchor : function(){
39167 return this.canchors[this.position];
39170 getSlideAnchor : function(){
39171 return this.sanchors[this.position];
39174 getAlignAdj : function(){
39175 var cm = this.cmargins;
39176 switch(this.position){
39192 getExpandAdj : function(){
39193 var c = this.collapsedEl, cm = this.cmargins;
39194 switch(this.position){
39196 return [-(cm.right+c.getWidth()+cm.left), 0];
39199 return [cm.right+c.getWidth()+cm.left, 0];
39202 return [0, -(cm.top+cm.bottom+c.getHeight())];
39205 return [0, cm.top+cm.bottom+c.getHeight()];
39211 * Ext JS Library 1.1.1
39212 * Copyright(c) 2006-2007, Ext JS, LLC.
39214 * Originally Released Under LGPL - original licence link has changed is not relivant.
39217 * <script type="text/javascript">
39220 * These classes are private internal classes
39222 Roo.bootstrap.layout.Center = function(config){
39223 config.region = "center";
39224 Roo.bootstrap.layout.Region.call(this, config);
39225 this.visible = true;
39226 this.minWidth = config.minWidth || 20;
39227 this.minHeight = config.minHeight || 20;
39230 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39232 // center panel can't be hidden
39236 // center panel can't be hidden
39239 getMinWidth: function(){
39240 return this.minWidth;
39243 getMinHeight: function(){
39244 return this.minHeight;
39258 Roo.bootstrap.layout.North = function(config)
39260 config.region = 'north';
39261 config.cursor = 'n-resize';
39263 Roo.bootstrap.layout.Split.call(this, config);
39267 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39268 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39269 this.split.el.addClass("roo-layout-split-v");
39271 var size = config.initialSize || config.height;
39272 if(typeof size != "undefined"){
39273 this.el.setHeight(size);
39276 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39278 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39282 getBox : function(){
39283 if(this.collapsed){
39284 return this.collapsedEl.getBox();
39286 var box = this.el.getBox();
39288 box.height += this.split.el.getHeight();
39293 updateBox : function(box){
39294 if(this.split && !this.collapsed){
39295 box.height -= this.split.el.getHeight();
39296 this.split.el.setLeft(box.x);
39297 this.split.el.setTop(box.y+box.height);
39298 this.split.el.setWidth(box.width);
39300 if(this.collapsed){
39301 this.updateBody(box.width, null);
39303 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39311 Roo.bootstrap.layout.South = function(config){
39312 config.region = 'south';
39313 config.cursor = 's-resize';
39314 Roo.bootstrap.layout.Split.call(this, config);
39316 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39317 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39318 this.split.el.addClass("roo-layout-split-v");
39320 var size = config.initialSize || config.height;
39321 if(typeof size != "undefined"){
39322 this.el.setHeight(size);
39326 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39327 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39328 getBox : function(){
39329 if(this.collapsed){
39330 return this.collapsedEl.getBox();
39332 var box = this.el.getBox();
39334 var sh = this.split.el.getHeight();
39341 updateBox : function(box){
39342 if(this.split && !this.collapsed){
39343 var sh = this.split.el.getHeight();
39346 this.split.el.setLeft(box.x);
39347 this.split.el.setTop(box.y-sh);
39348 this.split.el.setWidth(box.width);
39350 if(this.collapsed){
39351 this.updateBody(box.width, null);
39353 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39357 Roo.bootstrap.layout.East = function(config){
39358 config.region = "east";
39359 config.cursor = "e-resize";
39360 Roo.bootstrap.layout.Split.call(this, config);
39362 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39363 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39364 this.split.el.addClass("roo-layout-split-h");
39366 var size = config.initialSize || config.width;
39367 if(typeof size != "undefined"){
39368 this.el.setWidth(size);
39371 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39372 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39373 getBox : function(){
39374 if(this.collapsed){
39375 return this.collapsedEl.getBox();
39377 var box = this.el.getBox();
39379 var sw = this.split.el.getWidth();
39386 updateBox : function(box){
39387 if(this.split && !this.collapsed){
39388 var sw = this.split.el.getWidth();
39390 this.split.el.setLeft(box.x);
39391 this.split.el.setTop(box.y);
39392 this.split.el.setHeight(box.height);
39395 if(this.collapsed){
39396 this.updateBody(null, box.height);
39398 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39402 Roo.bootstrap.layout.West = function(config){
39403 config.region = "west";
39404 config.cursor = "w-resize";
39406 Roo.bootstrap.layout.Split.call(this, config);
39408 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39409 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39410 this.split.el.addClass("roo-layout-split-h");
39414 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39415 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39417 onRender: function(ctr, pos)
39419 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39420 var size = this.config.initialSize || this.config.width;
39421 if(typeof size != "undefined"){
39422 this.el.setWidth(size);
39426 getBox : function(){
39427 if(this.collapsed){
39428 return this.collapsedEl.getBox();
39430 var box = this.el.getBox();
39432 box.width += this.split.el.getWidth();
39437 updateBox : function(box){
39438 if(this.split && !this.collapsed){
39439 var sw = this.split.el.getWidth();
39441 this.split.el.setLeft(box.x+box.width);
39442 this.split.el.setTop(box.y);
39443 this.split.el.setHeight(box.height);
39445 if(this.collapsed){
39446 this.updateBody(null, box.height);
39448 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39450 });Roo.namespace("Roo.bootstrap.panel");/*
39452 * Ext JS Library 1.1.1
39453 * Copyright(c) 2006-2007, Ext JS, LLC.
39455 * Originally Released Under LGPL - original licence link has changed is not relivant.
39458 * <script type="text/javascript">
39461 * @class Roo.ContentPanel
39462 * @extends Roo.util.Observable
39463 * A basic ContentPanel element.
39464 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39465 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39466 * @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
39467 * @cfg {Boolean} closable True if the panel can be closed/removed
39468 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39469 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39470 * @cfg {Toolbar} toolbar A toolbar for this panel
39471 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39472 * @cfg {String} title The title for this panel
39473 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39474 * @cfg {String} url Calls {@link #setUrl} with this value
39475 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39476 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39477 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39478 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39479 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39480 * @cfg {Boolean} badges render the badges
39481 * @cfg {String} cls extra classes to use
39482 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39485 * Create a new ContentPanel.
39486 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39487 * @param {String/Object} config A string to set only the title or a config object
39488 * @param {String} content (optional) Set the HTML content for this panel
39489 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39491 Roo.bootstrap.panel.Content = function( config){
39493 this.tpl = config.tpl || false;
39495 var el = config.el;
39496 var content = config.content;
39498 if(config.autoCreate){ // xtype is available if this is called from factory
39501 this.el = Roo.get(el);
39502 if(!this.el && config && config.autoCreate){
39503 if(typeof config.autoCreate == "object"){
39504 if(!config.autoCreate.id){
39505 config.autoCreate.id = config.id||el;
39507 this.el = Roo.DomHelper.append(document.body,
39508 config.autoCreate, true);
39512 cls: (config.cls || '') +
39513 (config.background ? ' bg-' + config.background : '') +
39514 " roo-layout-inactive-content",
39517 if (config.iframe) {
39521 style : 'border: 0px',
39522 src : 'about:blank'
39528 elcfg.html = config.html;
39532 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39533 if (config.iframe) {
39534 this.iframeEl = this.el.select('iframe',true).first();
39539 this.closable = false;
39540 this.loaded = false;
39541 this.active = false;
39544 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39546 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39548 this.wrapEl = this.el; //this.el.wrap();
39550 if (config.toolbar.items) {
39551 ti = config.toolbar.items ;
39552 delete config.toolbar.items ;
39556 this.toolbar.render(this.wrapEl, 'before');
39557 for(var i =0;i < ti.length;i++) {
39558 // Roo.log(['add child', items[i]]);
39559 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39561 this.toolbar.items = nitems;
39562 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39563 delete config.toolbar;
39567 // xtype created footer. - not sure if will work as we normally have to render first..
39568 if (this.footer && !this.footer.el && this.footer.xtype) {
39569 if (!this.wrapEl) {
39570 this.wrapEl = this.el.wrap();
39573 this.footer.container = this.wrapEl.createChild();
39575 this.footer = Roo.factory(this.footer, Roo);
39580 if(typeof config == "string"){
39581 this.title = config;
39583 Roo.apply(this, config);
39587 this.resizeEl = Roo.get(this.resizeEl, true);
39589 this.resizeEl = this.el;
39591 // handle view.xtype
39599 * Fires when this panel is activated.
39600 * @param {Roo.ContentPanel} this
39604 * @event deactivate
39605 * Fires when this panel is activated.
39606 * @param {Roo.ContentPanel} this
39608 "deactivate" : true,
39612 * Fires when this panel is resized if fitToFrame is true.
39613 * @param {Roo.ContentPanel} this
39614 * @param {Number} width The width after any component adjustments
39615 * @param {Number} height The height after any component adjustments
39621 * Fires when this tab is created
39622 * @param {Roo.ContentPanel} this
39633 if(this.autoScroll && !this.iframe){
39634 this.resizeEl.setStyle("overflow", "auto");
39636 // fix randome scrolling
39637 //this.el.on('scroll', function() {
39638 // Roo.log('fix random scolling');
39639 // this.scrollTo('top',0);
39642 content = content || this.content;
39644 this.setContent(content);
39646 if(config && config.url){
39647 this.setUrl(this.url, this.params, this.loadOnce);
39652 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39654 if (this.view && typeof(this.view.xtype) != 'undefined') {
39655 this.view.el = this.el.appendChild(document.createElement("div"));
39656 this.view = Roo.factory(this.view);
39657 this.view.render && this.view.render(false, '');
39661 this.fireEvent('render', this);
39664 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39674 setRegion : function(region){
39675 this.region = region;
39676 this.setActiveClass(region && !this.background);
39680 setActiveClass: function(state)
39683 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39684 this.el.setStyle('position','relative');
39686 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39687 this.el.setStyle('position', 'absolute');
39692 * Returns the toolbar for this Panel if one was configured.
39693 * @return {Roo.Toolbar}
39695 getToolbar : function(){
39696 return this.toolbar;
39699 setActiveState : function(active)
39701 this.active = active;
39702 this.setActiveClass(active);
39704 if(this.fireEvent("deactivate", this) === false){
39709 this.fireEvent("activate", this);
39713 * Updates this panel's element (not for iframe)
39714 * @param {String} content The new content
39715 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39717 setContent : function(content, loadScripts){
39722 this.el.update(content, loadScripts);
39725 ignoreResize : function(w, h){
39726 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39729 this.lastSize = {width: w, height: h};
39734 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39735 * @return {Roo.UpdateManager} The UpdateManager
39737 getUpdateManager : function(){
39741 return this.el.getUpdateManager();
39744 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39745 * Does not work with IFRAME contents
39746 * @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:
39749 url: "your-url.php",
39750 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39751 callback: yourFunction,
39752 scope: yourObject, //(optional scope)
39755 text: "Loading...",
39761 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39762 * 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.
39763 * @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}
39764 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39765 * @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.
39766 * @return {Roo.ContentPanel} this
39774 var um = this.el.getUpdateManager();
39775 um.update.apply(um, arguments);
39781 * 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.
39782 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39783 * @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)
39784 * @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)
39785 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39787 setUrl : function(url, params, loadOnce){
39789 this.iframeEl.dom.src = url;
39793 if(this.refreshDelegate){
39794 this.removeListener("activate", this.refreshDelegate);
39796 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39797 this.on("activate", this.refreshDelegate);
39798 return this.el.getUpdateManager();
39801 _handleRefresh : function(url, params, loadOnce){
39802 if(!loadOnce || !this.loaded){
39803 var updater = this.el.getUpdateManager();
39804 updater.update(url, params, this._setLoaded.createDelegate(this));
39808 _setLoaded : function(){
39809 this.loaded = true;
39813 * Returns this panel's id
39816 getId : function(){
39821 * Returns this panel's element - used by regiosn to add.
39822 * @return {Roo.Element}
39824 getEl : function(){
39825 return this.wrapEl || this.el;
39830 adjustForComponents : function(width, height)
39832 //Roo.log('adjustForComponents ');
39833 if(this.resizeEl != this.el){
39834 width -= this.el.getFrameWidth('lr');
39835 height -= this.el.getFrameWidth('tb');
39838 var te = this.toolbar.getEl();
39839 te.setWidth(width);
39840 height -= te.getHeight();
39843 var te = this.footer.getEl();
39844 te.setWidth(width);
39845 height -= te.getHeight();
39849 if(this.adjustments){
39850 width += this.adjustments[0];
39851 height += this.adjustments[1];
39853 return {"width": width, "height": height};
39856 setSize : function(width, height){
39857 if(this.fitToFrame && !this.ignoreResize(width, height)){
39858 if(this.fitContainer && this.resizeEl != this.el){
39859 this.el.setSize(width, height);
39861 var size = this.adjustForComponents(width, height);
39863 this.iframeEl.setSize(width,height);
39866 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39867 this.fireEvent('resize', this, size.width, size.height);
39874 * Returns this panel's title
39877 getTitle : function(){
39879 if (typeof(this.title) != 'object') {
39884 for (var k in this.title) {
39885 if (!this.title.hasOwnProperty(k)) {
39889 if (k.indexOf('-') >= 0) {
39890 var s = k.split('-');
39891 for (var i = 0; i<s.length; i++) {
39892 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39895 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39902 * Set this panel's title
39903 * @param {String} title
39905 setTitle : function(title){
39906 this.title = title;
39908 this.region.updatePanelTitle(this, title);
39913 * Returns true is this panel was configured to be closable
39914 * @return {Boolean}
39916 isClosable : function(){
39917 return this.closable;
39920 beforeSlide : function(){
39922 this.resizeEl.clip();
39925 afterSlide : function(){
39927 this.resizeEl.unclip();
39931 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39932 * Will fail silently if the {@link #setUrl} method has not been called.
39933 * This does not activate the panel, just updates its content.
39935 refresh : function(){
39936 if(this.refreshDelegate){
39937 this.loaded = false;
39938 this.refreshDelegate();
39943 * Destroys this panel
39945 destroy : function(){
39946 this.el.removeAllListeners();
39947 var tempEl = document.createElement("span");
39948 tempEl.appendChild(this.el.dom);
39949 tempEl.innerHTML = "";
39955 * form - if the content panel contains a form - this is a reference to it.
39956 * @type {Roo.form.Form}
39960 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39961 * This contains a reference to it.
39967 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39977 * @param {Object} cfg Xtype definition of item to add.
39981 getChildContainer: function () {
39982 return this.getEl();
39987 var ret = new Roo.factory(cfg);
39992 if (cfg.xtype.match(/^Form$/)) {
39995 //if (this.footer) {
39996 // el = this.footer.container.insertSibling(false, 'before');
39998 el = this.el.createChild();
40001 this.form = new Roo.form.Form(cfg);
40004 if ( this.form.allItems.length) {
40005 this.form.render(el.dom);
40009 // should only have one of theses..
40010 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40011 // views.. should not be just added - used named prop 'view''
40013 cfg.el = this.el.appendChild(document.createElement("div"));
40016 var ret = new Roo.factory(cfg);
40018 ret.render && ret.render(false, ''); // render blank..
40028 * @class Roo.bootstrap.panel.Grid
40029 * @extends Roo.bootstrap.panel.Content
40031 * Create a new GridPanel.
40032 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40033 * @param {Object} config A the config object
40039 Roo.bootstrap.panel.Grid = function(config)
40043 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40044 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40046 config.el = this.wrapper;
40047 //this.el = this.wrapper;
40049 if (config.container) {
40050 // ctor'ed from a Border/panel.grid
40053 this.wrapper.setStyle("overflow", "hidden");
40054 this.wrapper.addClass('roo-grid-container');
40059 if(config.toolbar){
40060 var tool_el = this.wrapper.createChild();
40061 this.toolbar = Roo.factory(config.toolbar);
40063 if (config.toolbar.items) {
40064 ti = config.toolbar.items ;
40065 delete config.toolbar.items ;
40069 this.toolbar.render(tool_el);
40070 for(var i =0;i < ti.length;i++) {
40071 // Roo.log(['add child', items[i]]);
40072 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40074 this.toolbar.items = nitems;
40076 delete config.toolbar;
40079 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40080 config.grid.scrollBody = true;;
40081 config.grid.monitorWindowResize = false; // turn off autosizing
40082 config.grid.autoHeight = false;
40083 config.grid.autoWidth = false;
40085 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40087 if (config.background) {
40088 // render grid on panel activation (if panel background)
40089 this.on('activate', function(gp) {
40090 if (!gp.grid.rendered) {
40091 gp.grid.render(this.wrapper);
40092 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40097 this.grid.render(this.wrapper);
40098 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40101 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40102 // ??? needed ??? config.el = this.wrapper;
40107 // xtype created footer. - not sure if will work as we normally have to render first..
40108 if (this.footer && !this.footer.el && this.footer.xtype) {
40110 var ctr = this.grid.getView().getFooterPanel(true);
40111 this.footer.dataSource = this.grid.dataSource;
40112 this.footer = Roo.factory(this.footer, Roo);
40113 this.footer.render(ctr);
40123 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40124 getId : function(){
40125 return this.grid.id;
40129 * Returns the grid for this panel
40130 * @return {Roo.bootstrap.Table}
40132 getGrid : function(){
40136 setSize : function(width, height){
40137 if(!this.ignoreResize(width, height)){
40138 var grid = this.grid;
40139 var size = this.adjustForComponents(width, height);
40140 // tfoot is not a footer?
40143 var gridel = grid.getGridEl();
40144 gridel.setSize(size.width, size.height);
40146 var tbd = grid.getGridEl().select('tbody', true).first();
40147 var thd = grid.getGridEl().select('thead',true).first();
40148 var tbf= grid.getGridEl().select('tfoot', true).first();
40151 size.height -= thd.getHeight();
40154 size.height -= thd.getHeight();
40157 tbd.setSize(size.width, size.height );
40158 // this is for the account management tab -seems to work there.
40159 var thd = grid.getGridEl().select('thead',true).first();
40161 // tbd.setSize(size.width, size.height - thd.getHeight());
40170 beforeSlide : function(){
40171 this.grid.getView().scroller.clip();
40174 afterSlide : function(){
40175 this.grid.getView().scroller.unclip();
40178 destroy : function(){
40179 this.grid.destroy();
40181 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40186 * @class Roo.bootstrap.panel.Nest
40187 * @extends Roo.bootstrap.panel.Content
40189 * Create a new Panel, that can contain a layout.Border.
40192 * @param {Roo.BorderLayout} layout The layout for this panel
40193 * @param {String/Object} config A string to set only the title or a config object
40195 Roo.bootstrap.panel.Nest = function(config)
40197 // construct with only one argument..
40198 /* FIXME - implement nicer consturctors
40199 if (layout.layout) {
40201 layout = config.layout;
40202 delete config.layout;
40204 if (layout.xtype && !layout.getEl) {
40205 // then layout needs constructing..
40206 layout = Roo.factory(layout, Roo);
40210 config.el = config.layout.getEl();
40212 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40214 config.layout.monitorWindowResize = false; // turn off autosizing
40215 this.layout = config.layout;
40216 this.layout.getEl().addClass("roo-layout-nested-layout");
40217 this.layout.parent = this;
40224 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40226 setSize : function(width, height){
40227 if(!this.ignoreResize(width, height)){
40228 var size = this.adjustForComponents(width, height);
40229 var el = this.layout.getEl();
40230 if (size.height < 1) {
40231 el.setWidth(size.width);
40233 el.setSize(size.width, size.height);
40235 var touch = el.dom.offsetWidth;
40236 this.layout.layout();
40237 // ie requires a double layout on the first pass
40238 if(Roo.isIE && !this.initialized){
40239 this.initialized = true;
40240 this.layout.layout();
40245 // activate all subpanels if not currently active..
40247 setActiveState : function(active){
40248 this.active = active;
40249 this.setActiveClass(active);
40252 this.fireEvent("deactivate", this);
40256 this.fireEvent("activate", this);
40257 // not sure if this should happen before or after..
40258 if (!this.layout) {
40259 return; // should not happen..
40262 for (var r in this.layout.regions) {
40263 reg = this.layout.getRegion(r);
40264 if (reg.getActivePanel()) {
40265 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40266 reg.setActivePanel(reg.getActivePanel());
40269 if (!reg.panels.length) {
40272 reg.showPanel(reg.getPanel(0));
40281 * Returns the nested BorderLayout for this panel
40282 * @return {Roo.BorderLayout}
40284 getLayout : function(){
40285 return this.layout;
40289 * Adds a xtype elements to the layout of the nested panel
40293 xtype : 'ContentPanel',
40300 xtype : 'NestedLayoutPanel',
40306 items : [ ... list of content panels or nested layout panels.. ]
40310 * @param {Object} cfg Xtype definition of item to add.
40312 addxtype : function(cfg) {
40313 return this.layout.addxtype(cfg);
40318 * Ext JS Library 1.1.1
40319 * Copyright(c) 2006-2007, Ext JS, LLC.
40321 * Originally Released Under LGPL - original licence link has changed is not relivant.
40324 * <script type="text/javascript">
40327 * @class Roo.TabPanel
40328 * @extends Roo.util.Observable
40329 * A lightweight tab container.
40333 // basic tabs 1, built from existing content
40334 var tabs = new Roo.TabPanel("tabs1");
40335 tabs.addTab("script", "View Script");
40336 tabs.addTab("markup", "View Markup");
40337 tabs.activate("script");
40339 // more advanced tabs, built from javascript
40340 var jtabs = new Roo.TabPanel("jtabs");
40341 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40343 // set up the UpdateManager
40344 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40345 var updater = tab2.getUpdateManager();
40346 updater.setDefaultUrl("ajax1.htm");
40347 tab2.on('activate', updater.refresh, updater, true);
40349 // Use setUrl for Ajax loading
40350 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40351 tab3.setUrl("ajax2.htm", null, true);
40354 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40357 jtabs.activate("jtabs-1");
40360 * Create a new TabPanel.
40361 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40362 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40364 Roo.bootstrap.panel.Tabs = function(config){
40366 * The container element for this TabPanel.
40367 * @type Roo.Element
40369 this.el = Roo.get(config.el);
40372 if(typeof config == "boolean"){
40373 this.tabPosition = config ? "bottom" : "top";
40375 Roo.apply(this, config);
40379 if(this.tabPosition == "bottom"){
40380 // if tabs are at the bottom = create the body first.
40381 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40382 this.el.addClass("roo-tabs-bottom");
40384 // next create the tabs holders
40386 if (this.tabPosition == "west"){
40388 var reg = this.region; // fake it..
40390 if (!reg.mgr.parent) {
40393 reg = reg.mgr.parent.region;
40395 Roo.log("got nest?");
40397 if (reg.mgr.getRegion('west')) {
40398 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40399 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40400 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40401 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40402 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40410 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40411 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40412 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40413 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40418 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40421 // finally - if tabs are at the top, then create the body last..
40422 if(this.tabPosition != "bottom"){
40423 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40424 * @type Roo.Element
40426 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40427 this.el.addClass("roo-tabs-top");
40431 this.bodyEl.setStyle("position", "relative");
40433 this.active = null;
40434 this.activateDelegate = this.activate.createDelegate(this);
40439 * Fires when the active tab changes
40440 * @param {Roo.TabPanel} this
40441 * @param {Roo.TabPanelItem} activePanel The new active tab
40445 * @event beforetabchange
40446 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40447 * @param {Roo.TabPanel} this
40448 * @param {Object} e Set cancel to true on this object to cancel the tab change
40449 * @param {Roo.TabPanelItem} tab The tab being changed to
40451 "beforetabchange" : true
40454 Roo.EventManager.onWindowResize(this.onResize, this);
40455 this.cpad = this.el.getPadding("lr");
40456 this.hiddenCount = 0;
40459 // toolbar on the tabbar support...
40460 if (this.toolbar) {
40461 alert("no toolbar support yet");
40462 this.toolbar = false;
40464 var tcfg = this.toolbar;
40465 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40466 this.toolbar = new Roo.Toolbar(tcfg);
40467 if (Roo.isSafari) {
40468 var tbl = tcfg.container.child('table', true);
40469 tbl.setAttribute('width', '100%');
40477 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40480 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40482 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40484 tabPosition : "top",
40486 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40488 currentTabWidth : 0,
40490 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40494 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40498 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40500 preferredTabWidth : 175,
40502 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40504 resizeTabs : false,
40506 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40508 monitorResize : true,
40510 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40512 toolbar : false, // set by caller..
40514 region : false, /// set by caller
40516 disableTooltips : true, // not used yet...
40519 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40520 * @param {String} id The id of the div to use <b>or create</b>
40521 * @param {String} text The text for the tab
40522 * @param {String} content (optional) Content to put in the TabPanelItem body
40523 * @param {Boolean} closable (optional) True to create a close icon on the tab
40524 * @return {Roo.TabPanelItem} The created TabPanelItem
40526 addTab : function(id, text, content, closable, tpl)
40528 var item = new Roo.bootstrap.panel.TabItem({
40532 closable : closable,
40535 this.addTabItem(item);
40537 item.setContent(content);
40543 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40544 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40545 * @return {Roo.TabPanelItem}
40547 getTab : function(id){
40548 return this.items[id];
40552 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40553 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40555 hideTab : function(id){
40556 var t = this.items[id];
40559 this.hiddenCount++;
40560 this.autoSizeTabs();
40565 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40566 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40568 unhideTab : function(id){
40569 var t = this.items[id];
40571 t.setHidden(false);
40572 this.hiddenCount--;
40573 this.autoSizeTabs();
40578 * Adds an existing {@link Roo.TabPanelItem}.
40579 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40581 addTabItem : function(item)
40583 this.items[item.id] = item;
40584 this.items.push(item);
40585 this.autoSizeTabs();
40586 // if(this.resizeTabs){
40587 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40588 // this.autoSizeTabs();
40590 // item.autoSize();
40595 * Removes a {@link Roo.TabPanelItem}.
40596 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40598 removeTab : function(id){
40599 var items = this.items;
40600 var tab = items[id];
40601 if(!tab) { return; }
40602 var index = items.indexOf(tab);
40603 if(this.active == tab && items.length > 1){
40604 var newTab = this.getNextAvailable(index);
40609 this.stripEl.dom.removeChild(tab.pnode.dom);
40610 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40611 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40613 items.splice(index, 1);
40614 delete this.items[tab.id];
40615 tab.fireEvent("close", tab);
40616 tab.purgeListeners();
40617 this.autoSizeTabs();
40620 getNextAvailable : function(start){
40621 var items = this.items;
40623 // look for a next tab that will slide over to
40624 // replace the one being removed
40625 while(index < items.length){
40626 var item = items[++index];
40627 if(item && !item.isHidden()){
40631 // if one isn't found select the previous tab (on the left)
40634 var item = items[--index];
40635 if(item && !item.isHidden()){
40643 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40644 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40646 disableTab : function(id){
40647 var tab = this.items[id];
40648 if(tab && this.active != tab){
40654 * Enables a {@link Roo.TabPanelItem} that is disabled.
40655 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40657 enableTab : function(id){
40658 var tab = this.items[id];
40663 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40664 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40665 * @return {Roo.TabPanelItem} The TabPanelItem.
40667 activate : function(id)
40669 //Roo.log('activite:' + id);
40671 var tab = this.items[id];
40675 if(tab == this.active || tab.disabled){
40679 this.fireEvent("beforetabchange", this, e, tab);
40680 if(e.cancel !== true && !tab.disabled){
40682 this.active.hide();
40684 this.active = this.items[id];
40685 this.active.show();
40686 this.fireEvent("tabchange", this, this.active);
40692 * Gets the active {@link Roo.TabPanelItem}.
40693 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40695 getActiveTab : function(){
40696 return this.active;
40700 * Updates the tab body element to fit the height of the container element
40701 * for overflow scrolling
40702 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40704 syncHeight : function(targetHeight){
40705 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40706 var bm = this.bodyEl.getMargins();
40707 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40708 this.bodyEl.setHeight(newHeight);
40712 onResize : function(){
40713 if(this.monitorResize){
40714 this.autoSizeTabs();
40719 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40721 beginUpdate : function(){
40722 this.updating = true;
40726 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40728 endUpdate : function(){
40729 this.updating = false;
40730 this.autoSizeTabs();
40734 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40736 autoSizeTabs : function()
40738 var count = this.items.length;
40739 var vcount = count - this.hiddenCount;
40742 this.stripEl.hide();
40744 this.stripEl.show();
40747 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40752 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40753 var availWidth = Math.floor(w / vcount);
40754 var b = this.stripBody;
40755 if(b.getWidth() > w){
40756 var tabs = this.items;
40757 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40758 if(availWidth < this.minTabWidth){
40759 /*if(!this.sleft){ // incomplete scrolling code
40760 this.createScrollButtons();
40763 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40766 if(this.currentTabWidth < this.preferredTabWidth){
40767 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40773 * Returns the number of tabs in this TabPanel.
40776 getCount : function(){
40777 return this.items.length;
40781 * Resizes all the tabs to the passed width
40782 * @param {Number} The new width
40784 setTabWidth : function(width){
40785 this.currentTabWidth = width;
40786 for(var i = 0, len = this.items.length; i < len; i++) {
40787 if(!this.items[i].isHidden()) {
40788 this.items[i].setWidth(width);
40794 * Destroys this TabPanel
40795 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40797 destroy : function(removeEl){
40798 Roo.EventManager.removeResizeListener(this.onResize, this);
40799 for(var i = 0, len = this.items.length; i < len; i++){
40800 this.items[i].purgeListeners();
40802 if(removeEl === true){
40803 this.el.update("");
40808 createStrip : function(container)
40810 var strip = document.createElement("nav");
40811 strip.className = Roo.bootstrap.version == 4 ?
40812 "navbar-light bg-light" :
40813 "navbar navbar-default"; //"x-tabs-wrap";
40814 container.appendChild(strip);
40818 createStripList : function(strip)
40820 // div wrapper for retard IE
40821 // returns the "tr" element.
40822 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40823 //'<div class="x-tabs-strip-wrap">'+
40824 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40825 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40826 return strip.firstChild; //.firstChild.firstChild.firstChild;
40828 createBody : function(container)
40830 var body = document.createElement("div");
40831 Roo.id(body, "tab-body");
40832 //Roo.fly(body).addClass("x-tabs-body");
40833 Roo.fly(body).addClass("tab-content");
40834 container.appendChild(body);
40837 createItemBody :function(bodyEl, id){
40838 var body = Roo.getDom(id);
40840 body = document.createElement("div");
40843 //Roo.fly(body).addClass("x-tabs-item-body");
40844 Roo.fly(body).addClass("tab-pane");
40845 bodyEl.insertBefore(body, bodyEl.firstChild);
40849 createStripElements : function(stripEl, text, closable, tpl)
40851 var td = document.createElement("li"); // was td..
40852 td.className = 'nav-item';
40854 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40857 stripEl.appendChild(td);
40859 td.className = "x-tabs-closable";
40860 if(!this.closeTpl){
40861 this.closeTpl = new Roo.Template(
40862 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40863 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40864 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40867 var el = this.closeTpl.overwrite(td, {"text": text});
40868 var close = el.getElementsByTagName("div")[0];
40869 var inner = el.getElementsByTagName("em")[0];
40870 return {"el": el, "close": close, "inner": inner};
40873 // not sure what this is..
40874 // if(!this.tabTpl){
40875 //this.tabTpl = new Roo.Template(
40876 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40877 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40879 // this.tabTpl = new Roo.Template(
40880 // '<a href="#">' +
40881 // '<span unselectable="on"' +
40882 // (this.disableTooltips ? '' : ' title="{text}"') +
40883 // ' >{text}</span></a>'
40889 var template = tpl || this.tabTpl || false;
40892 template = new Roo.Template(
40893 Roo.bootstrap.version == 4 ?
40895 '<a class="nav-link" href="#" unselectable="on"' +
40896 (this.disableTooltips ? '' : ' title="{text}"') +
40899 '<a class="nav-link" href="#">' +
40900 '<span unselectable="on"' +
40901 (this.disableTooltips ? '' : ' title="{text}"') +
40902 ' >{text}</span></a>'
40907 switch (typeof(template)) {
40911 template = new Roo.Template(template);
40917 var el = template.overwrite(td, {"text": text});
40919 var inner = el.getElementsByTagName("span")[0];
40921 return {"el": el, "inner": inner};
40929 * @class Roo.TabPanelItem
40930 * @extends Roo.util.Observable
40931 * Represents an individual item (tab plus body) in a TabPanel.
40932 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40933 * @param {String} id The id of this TabPanelItem
40934 * @param {String} text The text for the tab of this TabPanelItem
40935 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40937 Roo.bootstrap.panel.TabItem = function(config){
40939 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40940 * @type Roo.TabPanel
40942 this.tabPanel = config.panel;
40944 * The id for this TabPanelItem
40947 this.id = config.id;
40949 this.disabled = false;
40951 this.text = config.text;
40953 this.loaded = false;
40954 this.closable = config.closable;
40957 * The body element for this TabPanelItem.
40958 * @type Roo.Element
40960 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40961 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40962 this.bodyEl.setStyle("display", "block");
40963 this.bodyEl.setStyle("zoom", "1");
40964 //this.hideAction();
40966 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40968 this.el = Roo.get(els.el);
40969 this.inner = Roo.get(els.inner, true);
40970 this.textEl = Roo.bootstrap.version == 4 ?
40971 this.el : Roo.get(this.el.dom.firstChild, true);
40973 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40974 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40977 // this.el.on("mousedown", this.onTabMouseDown, this);
40978 this.el.on("click", this.onTabClick, this);
40980 if(config.closable){
40981 var c = Roo.get(els.close, true);
40982 c.dom.title = this.closeText;
40983 c.addClassOnOver("close-over");
40984 c.on("click", this.closeClick, this);
40990 * Fires when this tab becomes the active tab.
40991 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40992 * @param {Roo.TabPanelItem} this
40996 * @event beforeclose
40997 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40998 * @param {Roo.TabPanelItem} this
40999 * @param {Object} e Set cancel to true on this object to cancel the close.
41001 "beforeclose": true,
41004 * Fires when this tab is closed.
41005 * @param {Roo.TabPanelItem} this
41009 * @event deactivate
41010 * Fires when this tab is no longer the active tab.
41011 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41012 * @param {Roo.TabPanelItem} this
41014 "deactivate" : true
41016 this.hidden = false;
41018 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41021 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41023 purgeListeners : function(){
41024 Roo.util.Observable.prototype.purgeListeners.call(this);
41025 this.el.removeAllListeners();
41028 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41031 this.status_node.addClass("active");
41034 this.tabPanel.stripWrap.repaint();
41036 this.fireEvent("activate", this.tabPanel, this);
41040 * Returns true if this tab is the active tab.
41041 * @return {Boolean}
41043 isActive : function(){
41044 return this.tabPanel.getActiveTab() == this;
41048 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41051 this.status_node.removeClass("active");
41053 this.fireEvent("deactivate", this.tabPanel, this);
41056 hideAction : function(){
41057 this.bodyEl.hide();
41058 this.bodyEl.setStyle("position", "absolute");
41059 this.bodyEl.setLeft("-20000px");
41060 this.bodyEl.setTop("-20000px");
41063 showAction : function(){
41064 this.bodyEl.setStyle("position", "relative");
41065 this.bodyEl.setTop("");
41066 this.bodyEl.setLeft("");
41067 this.bodyEl.show();
41071 * Set the tooltip for the tab.
41072 * @param {String} tooltip The tab's tooltip
41074 setTooltip : function(text){
41075 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41076 this.textEl.dom.qtip = text;
41077 this.textEl.dom.removeAttribute('title');
41079 this.textEl.dom.title = text;
41083 onTabClick : function(e){
41084 e.preventDefault();
41085 this.tabPanel.activate(this.id);
41088 onTabMouseDown : function(e){
41089 e.preventDefault();
41090 this.tabPanel.activate(this.id);
41093 getWidth : function(){
41094 return this.inner.getWidth();
41097 setWidth : function(width){
41098 var iwidth = width - this.linode.getPadding("lr");
41099 this.inner.setWidth(iwidth);
41100 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41101 this.linode.setWidth(width);
41105 * Show or hide the tab
41106 * @param {Boolean} hidden True to hide or false to show.
41108 setHidden : function(hidden){
41109 this.hidden = hidden;
41110 this.linode.setStyle("display", hidden ? "none" : "");
41114 * Returns true if this tab is "hidden"
41115 * @return {Boolean}
41117 isHidden : function(){
41118 return this.hidden;
41122 * Returns the text for this tab
41125 getText : function(){
41129 autoSize : function(){
41130 //this.el.beginMeasure();
41131 this.textEl.setWidth(1);
41133 * #2804 [new] Tabs in Roojs
41134 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41136 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41137 //this.el.endMeasure();
41141 * Sets the text for the tab (Note: this also sets the tooltip text)
41142 * @param {String} text The tab's text and tooltip
41144 setText : function(text){
41146 this.textEl.update(text);
41147 this.setTooltip(text);
41148 //if(!this.tabPanel.resizeTabs){
41149 // this.autoSize();
41153 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41155 activate : function(){
41156 this.tabPanel.activate(this.id);
41160 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41162 disable : function(){
41163 if(this.tabPanel.active != this){
41164 this.disabled = true;
41165 this.status_node.addClass("disabled");
41170 * Enables this TabPanelItem if it was previously disabled.
41172 enable : function(){
41173 this.disabled = false;
41174 this.status_node.removeClass("disabled");
41178 * Sets the content for this TabPanelItem.
41179 * @param {String} content The content
41180 * @param {Boolean} loadScripts true to look for and load scripts
41182 setContent : function(content, loadScripts){
41183 this.bodyEl.update(content, loadScripts);
41187 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41188 * @return {Roo.UpdateManager} The UpdateManager
41190 getUpdateManager : function(){
41191 return this.bodyEl.getUpdateManager();
41195 * Set a URL to be used to load the content for this TabPanelItem.
41196 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41197 * @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)
41198 * @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)
41199 * @return {Roo.UpdateManager} The UpdateManager
41201 setUrl : function(url, params, loadOnce){
41202 if(this.refreshDelegate){
41203 this.un('activate', this.refreshDelegate);
41205 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41206 this.on("activate", this.refreshDelegate);
41207 return this.bodyEl.getUpdateManager();
41211 _handleRefresh : function(url, params, loadOnce){
41212 if(!loadOnce || !this.loaded){
41213 var updater = this.bodyEl.getUpdateManager();
41214 updater.update(url, params, this._setLoaded.createDelegate(this));
41219 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41220 * Will fail silently if the setUrl method has not been called.
41221 * This does not activate the panel, just updates its content.
41223 refresh : function(){
41224 if(this.refreshDelegate){
41225 this.loaded = false;
41226 this.refreshDelegate();
41231 _setLoaded : function(){
41232 this.loaded = true;
41236 closeClick : function(e){
41239 this.fireEvent("beforeclose", this, o);
41240 if(o.cancel !== true){
41241 this.tabPanel.removeTab(this.id);
41245 * The text displayed in the tooltip for the close icon.
41248 closeText : "Close this tab"
41251 * This script refer to:
41252 * Title: International Telephone Input
41253 * Author: Jack O'Connor
41254 * Code version: v12.1.12
41255 * Availability: https://github.com/jackocnr/intl-tel-input.git
41258 Roo.bootstrap.PhoneInputData = function() {
41261 "Afghanistan (افغانستان)",
41266 "Albania (Shqipëri)",
41271 "Algeria (الجزائر)",
41296 "Antigua and Barbuda",
41306 "Armenia (Հայաստան)",
41322 "Austria (Österreich)",
41327 "Azerbaijan (Azərbaycan)",
41337 "Bahrain (البحرين)",
41342 "Bangladesh (বাংলাদেশ)",
41352 "Belarus (Беларусь)",
41357 "Belgium (België)",
41387 "Bosnia and Herzegovina (Босна и Херцеговина)",
41402 "British Indian Ocean Territory",
41407 "British Virgin Islands",
41417 "Bulgaria (България)",
41427 "Burundi (Uburundi)",
41432 "Cambodia (កម្ពុជា)",
41437 "Cameroon (Cameroun)",
41446 ["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"]
41449 "Cape Verde (Kabu Verdi)",
41454 "Caribbean Netherlands",
41465 "Central African Republic (République centrafricaine)",
41485 "Christmas Island",
41491 "Cocos (Keeling) Islands",
41502 "Comoros (جزر القمر)",
41507 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41512 "Congo (Republic) (Congo-Brazzaville)",
41532 "Croatia (Hrvatska)",
41553 "Czech Republic (Česká republika)",
41558 "Denmark (Danmark)",
41573 "Dominican Republic (República Dominicana)",
41577 ["809", "829", "849"]
41595 "Equatorial Guinea (Guinea Ecuatorial)",
41615 "Falkland Islands (Islas Malvinas)",
41620 "Faroe Islands (Føroyar)",
41641 "French Guiana (Guyane française)",
41646 "French Polynesia (Polynésie française)",
41661 "Georgia (საქართველო)",
41666 "Germany (Deutschland)",
41686 "Greenland (Kalaallit Nunaat)",
41723 "Guinea-Bissau (Guiné Bissau)",
41748 "Hungary (Magyarország)",
41753 "Iceland (Ísland)",
41773 "Iraq (العراق)",
41789 "Israel (ישראל)",
41816 "Jordan (الأردن)",
41821 "Kazakhstan (Казахстан)",
41842 "Kuwait (الكويت)",
41847 "Kyrgyzstan (Кыргызстан)",
41857 "Latvia (Latvija)",
41862 "Lebanon (لبنان)",
41877 "Libya (ليبيا)",
41887 "Lithuania (Lietuva)",
41902 "Macedonia (FYROM) (Македонија)",
41907 "Madagascar (Madagasikara)",
41937 "Marshall Islands",
41947 "Mauritania (موريتانيا)",
41952 "Mauritius (Moris)",
41973 "Moldova (Republica Moldova)",
41983 "Mongolia (Монгол)",
41988 "Montenegro (Crna Gora)",
41998 "Morocco (المغرب)",
42004 "Mozambique (Moçambique)",
42009 "Myanmar (Burma) (မြန်မာ)",
42014 "Namibia (Namibië)",
42029 "Netherlands (Nederland)",
42034 "New Caledonia (Nouvelle-Calédonie)",
42069 "North Korea (조선 민주주의 인민 공화국)",
42074 "Northern Mariana Islands",
42090 "Pakistan (پاکستان)",
42100 "Palestine (فلسطين)",
42110 "Papua New Guinea",
42152 "Réunion (La Réunion)",
42158 "Romania (România)",
42174 "Saint Barthélemy",
42185 "Saint Kitts and Nevis",
42195 "Saint Martin (Saint-Martin (partie française))",
42201 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42206 "Saint Vincent and the Grenadines",
42221 "São Tomé and Príncipe (São Tomé e Príncipe)",
42226 "Saudi Arabia (المملكة العربية السعودية)",
42231 "Senegal (Sénégal)",
42261 "Slovakia (Slovensko)",
42266 "Slovenia (Slovenija)",
42276 "Somalia (Soomaaliya)",
42286 "South Korea (대한민국)",
42291 "South Sudan (جنوب السودان)",
42301 "Sri Lanka (ශ්රී ලංකාව)",
42306 "Sudan (السودان)",
42316 "Svalbard and Jan Mayen",
42327 "Sweden (Sverige)",
42332 "Switzerland (Schweiz)",
42337 "Syria (سوريا)",
42382 "Trinidad and Tobago",
42387 "Tunisia (تونس)",
42392 "Turkey (Türkiye)",
42402 "Turks and Caicos Islands",
42412 "U.S. Virgin Islands",
42422 "Ukraine (Україна)",
42427 "United Arab Emirates (الإمارات العربية المتحدة)",
42449 "Uzbekistan (Oʻzbekiston)",
42459 "Vatican City (Città del Vaticano)",
42470 "Vietnam (Việt Nam)",
42475 "Wallis and Futuna (Wallis-et-Futuna)",
42480 "Western Sahara (الصحراء الغربية)",
42486 "Yemen (اليمن)",
42510 * This script refer to:
42511 * Title: International Telephone Input
42512 * Author: Jack O'Connor
42513 * Code version: v12.1.12
42514 * Availability: https://github.com/jackocnr/intl-tel-input.git
42518 * @class Roo.bootstrap.PhoneInput
42519 * @extends Roo.bootstrap.TriggerField
42520 * An input with International dial-code selection
42522 * @cfg {String} defaultDialCode default '+852'
42523 * @cfg {Array} preferedCountries default []
42526 * Create a new PhoneInput.
42527 * @param {Object} config Configuration options
42530 Roo.bootstrap.PhoneInput = function(config) {
42531 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42534 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42536 listWidth: undefined,
42538 selectedClass: 'active',
42540 invalidClass : "has-warning",
42542 validClass: 'has-success',
42544 allowed: '0123456789',
42549 * @cfg {String} defaultDialCode The default dial code when initializing the input
42551 defaultDialCode: '+852',
42554 * @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
42556 preferedCountries: false,
42558 getAutoCreate : function()
42560 var data = Roo.bootstrap.PhoneInputData();
42561 var align = this.labelAlign || this.parentLabelAlign();
42564 this.allCountries = [];
42565 this.dialCodeMapping = [];
42567 for (var i = 0; i < data.length; i++) {
42569 this.allCountries[i] = {
42573 priority: c[3] || 0,
42574 areaCodes: c[4] || null
42576 this.dialCodeMapping[c[2]] = {
42579 priority: c[3] || 0,
42580 areaCodes: c[4] || null
42592 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42593 maxlength: this.max_length,
42594 cls : 'form-control tel-input',
42595 autocomplete: 'new-password'
42598 var hiddenInput = {
42601 cls: 'hidden-tel-input'
42605 hiddenInput.name = this.name;
42608 if (this.disabled) {
42609 input.disabled = true;
42612 var flag_container = {
42629 cls: this.hasFeedback ? 'has-feedback' : '',
42635 cls: 'dial-code-holder',
42642 cls: 'roo-select2-container input-group',
42649 if (this.fieldLabel.length) {
42652 tooltip: 'This field is required'
42658 cls: 'control-label',
42664 html: this.fieldLabel
42667 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42673 if(this.indicatorpos == 'right') {
42674 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42681 if(align == 'left') {
42689 if(this.labelWidth > 12){
42690 label.style = "width: " + this.labelWidth + 'px';
42692 if(this.labelWidth < 13 && this.labelmd == 0){
42693 this.labelmd = this.labelWidth;
42695 if(this.labellg > 0){
42696 label.cls += ' col-lg-' + this.labellg;
42697 input.cls += ' col-lg-' + (12 - this.labellg);
42699 if(this.labelmd > 0){
42700 label.cls += ' col-md-' + this.labelmd;
42701 container.cls += ' col-md-' + (12 - this.labelmd);
42703 if(this.labelsm > 0){
42704 label.cls += ' col-sm-' + this.labelsm;
42705 container.cls += ' col-sm-' + (12 - this.labelsm);
42707 if(this.labelxs > 0){
42708 label.cls += ' col-xs-' + this.labelxs;
42709 container.cls += ' col-xs-' + (12 - this.labelxs);
42719 var settings = this;
42721 ['xs','sm','md','lg'].map(function(size){
42722 if (settings[size]) {
42723 cfg.cls += ' col-' + size + '-' + settings[size];
42727 this.store = new Roo.data.Store({
42728 proxy : new Roo.data.MemoryProxy({}),
42729 reader : new Roo.data.JsonReader({
42740 'name' : 'dialCode',
42744 'name' : 'priority',
42748 'name' : 'areaCodes',
42755 if(!this.preferedCountries) {
42756 this.preferedCountries = [
42763 var p = this.preferedCountries.reverse();
42766 for (var i = 0; i < p.length; i++) {
42767 for (var j = 0; j < this.allCountries.length; j++) {
42768 if(this.allCountries[j].iso2 == p[i]) {
42769 var t = this.allCountries[j];
42770 this.allCountries.splice(j,1);
42771 this.allCountries.unshift(t);
42777 this.store.proxy.data = {
42779 data: this.allCountries
42785 initEvents : function()
42788 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42790 this.indicator = this.indicatorEl();
42791 this.flag = this.flagEl();
42792 this.dialCodeHolder = this.dialCodeHolderEl();
42794 this.trigger = this.el.select('div.flag-box',true).first();
42795 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42800 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42801 _this.list.setWidth(lw);
42804 this.list.on('mouseover', this.onViewOver, this);
42805 this.list.on('mousemove', this.onViewMove, this);
42806 this.inputEl().on("keyup", this.onKeyUp, this);
42807 this.inputEl().on("keypress", this.onKeyPress, this);
42809 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42811 this.view = new Roo.View(this.list, this.tpl, {
42812 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42815 this.view.on('click', this.onViewClick, this);
42816 this.setValue(this.defaultDialCode);
42819 onTriggerClick : function(e)
42821 Roo.log('trigger click');
42826 if(this.isExpanded()){
42828 this.hasFocus = false;
42830 this.store.load({});
42831 this.hasFocus = true;
42836 isExpanded : function()
42838 return this.list.isVisible();
42841 collapse : function()
42843 if(!this.isExpanded()){
42847 Roo.get(document).un('mousedown', this.collapseIf, this);
42848 Roo.get(document).un('mousewheel', this.collapseIf, this);
42849 this.fireEvent('collapse', this);
42853 expand : function()
42857 if(this.isExpanded() || !this.hasFocus){
42861 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42862 this.list.setWidth(lw);
42865 this.restrictHeight();
42867 Roo.get(document).on('mousedown', this.collapseIf, this);
42868 Roo.get(document).on('mousewheel', this.collapseIf, this);
42870 this.fireEvent('expand', this);
42873 restrictHeight : function()
42875 this.list.alignTo(this.inputEl(), this.listAlign);
42876 this.list.alignTo(this.inputEl(), this.listAlign);
42879 onViewOver : function(e, t)
42881 if(this.inKeyMode){
42884 var item = this.view.findItemFromChild(t);
42887 var index = this.view.indexOf(item);
42888 this.select(index, false);
42893 onViewClick : function(view, doFocus, el, e)
42895 var index = this.view.getSelectedIndexes()[0];
42897 var r = this.store.getAt(index);
42900 this.onSelect(r, index);
42902 if(doFocus !== false && !this.blockFocus){
42903 this.inputEl().focus();
42907 onViewMove : function(e, t)
42909 this.inKeyMode = false;
42912 select : function(index, scrollIntoView)
42914 this.selectedIndex = index;
42915 this.view.select(index);
42916 if(scrollIntoView !== false){
42917 var el = this.view.getNode(index);
42919 this.list.scrollChildIntoView(el, false);
42924 createList : function()
42926 this.list = Roo.get(document.body).createChild({
42928 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42929 style: 'display:none'
42932 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42935 collapseIf : function(e)
42937 var in_combo = e.within(this.el);
42938 var in_list = e.within(this.list);
42939 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42941 if (in_combo || in_list || is_list) {
42947 onSelect : function(record, index)
42949 if(this.fireEvent('beforeselect', this, record, index) !== false){
42951 this.setFlagClass(record.data.iso2);
42952 this.setDialCode(record.data.dialCode);
42953 this.hasFocus = false;
42955 this.fireEvent('select', this, record, index);
42959 flagEl : function()
42961 var flag = this.el.select('div.flag',true).first();
42968 dialCodeHolderEl : function()
42970 var d = this.el.select('input.dial-code-holder',true).first();
42977 setDialCode : function(v)
42979 this.dialCodeHolder.dom.value = '+'+v;
42982 setFlagClass : function(n)
42984 this.flag.dom.className = 'flag '+n;
42987 getValue : function()
42989 var v = this.inputEl().getValue();
42990 if(this.dialCodeHolder) {
42991 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42996 setValue : function(v)
42998 var d = this.getDialCode(v);
43000 //invalid dial code
43001 if(v.length == 0 || !d || d.length == 0) {
43003 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43004 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43010 this.setFlagClass(this.dialCodeMapping[d].iso2);
43011 this.setDialCode(d);
43012 this.inputEl().dom.value = v.replace('+'+d,'');
43013 this.hiddenEl().dom.value = this.getValue();
43018 getDialCode : function(v)
43022 if (v.length == 0) {
43023 return this.dialCodeHolder.dom.value;
43027 if (v.charAt(0) != "+") {
43030 var numericChars = "";
43031 for (var i = 1; i < v.length; i++) {
43032 var c = v.charAt(i);
43035 if (this.dialCodeMapping[numericChars]) {
43036 dialCode = v.substr(1, i);
43038 if (numericChars.length == 4) {
43048 this.setValue(this.defaultDialCode);
43052 hiddenEl : function()
43054 return this.el.select('input.hidden-tel-input',true).first();
43057 // after setting val
43058 onKeyUp : function(e){
43059 this.setValue(this.getValue());
43062 onKeyPress : function(e){
43063 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43070 * @class Roo.bootstrap.MoneyField
43071 * @extends Roo.bootstrap.ComboBox
43072 * Bootstrap MoneyField class
43075 * Create a new MoneyField.
43076 * @param {Object} config Configuration options
43079 Roo.bootstrap.MoneyField = function(config) {
43081 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43085 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43088 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43090 allowDecimals : true,
43092 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43094 decimalSeparator : ".",
43096 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43098 decimalPrecision : 0,
43100 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43102 allowNegative : true,
43104 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43108 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43110 minValue : Number.NEGATIVE_INFINITY,
43112 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43114 maxValue : Number.MAX_VALUE,
43116 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43118 minText : "The minimum value for this field is {0}",
43120 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43122 maxText : "The maximum value for this field is {0}",
43124 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43125 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43127 nanText : "{0} is not a valid number",
43129 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43133 * @cfg {String} defaults currency of the MoneyField
43134 * value should be in lkey
43136 defaultCurrency : false,
43138 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43140 thousandsDelimiter : false,
43142 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43153 getAutoCreate : function()
43155 var align = this.labelAlign || this.parentLabelAlign();
43167 cls : 'form-control roo-money-amount-input',
43168 autocomplete: 'new-password'
43171 var hiddenInput = {
43175 cls: 'hidden-number-input'
43178 if(this.max_length) {
43179 input.maxlength = this.max_length;
43183 hiddenInput.name = this.name;
43186 if (this.disabled) {
43187 input.disabled = true;
43190 var clg = 12 - this.inputlg;
43191 var cmd = 12 - this.inputmd;
43192 var csm = 12 - this.inputsm;
43193 var cxs = 12 - this.inputxs;
43197 cls : 'row roo-money-field',
43201 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43205 cls: 'roo-select2-container input-group',
43209 cls : 'form-control roo-money-currency-input',
43210 autocomplete: 'new-password',
43212 name : this.currencyName
43216 cls : 'input-group-addon',
43230 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43234 cls: this.hasFeedback ? 'has-feedback' : '',
43245 if (this.fieldLabel.length) {
43248 tooltip: 'This field is required'
43254 cls: 'control-label',
43260 html: this.fieldLabel
43263 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43269 if(this.indicatorpos == 'right') {
43270 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43277 if(align == 'left') {
43285 if(this.labelWidth > 12){
43286 label.style = "width: " + this.labelWidth + 'px';
43288 if(this.labelWidth < 13 && this.labelmd == 0){
43289 this.labelmd = this.labelWidth;
43291 if(this.labellg > 0){
43292 label.cls += ' col-lg-' + this.labellg;
43293 input.cls += ' col-lg-' + (12 - this.labellg);
43295 if(this.labelmd > 0){
43296 label.cls += ' col-md-' + this.labelmd;
43297 container.cls += ' col-md-' + (12 - this.labelmd);
43299 if(this.labelsm > 0){
43300 label.cls += ' col-sm-' + this.labelsm;
43301 container.cls += ' col-sm-' + (12 - this.labelsm);
43303 if(this.labelxs > 0){
43304 label.cls += ' col-xs-' + this.labelxs;
43305 container.cls += ' col-xs-' + (12 - this.labelxs);
43316 var settings = this;
43318 ['xs','sm','md','lg'].map(function(size){
43319 if (settings[size]) {
43320 cfg.cls += ' col-' + size + '-' + settings[size];
43327 initEvents : function()
43329 this.indicator = this.indicatorEl();
43331 this.initCurrencyEvent();
43333 this.initNumberEvent();
43336 initCurrencyEvent : function()
43339 throw "can not find store for combo";
43342 this.store = Roo.factory(this.store, Roo.data);
43343 this.store.parent = this;
43347 this.triggerEl = this.el.select('.input-group-addon', true).first();
43349 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43354 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43355 _this.list.setWidth(lw);
43358 this.list.on('mouseover', this.onViewOver, this);
43359 this.list.on('mousemove', this.onViewMove, this);
43360 this.list.on('scroll', this.onViewScroll, this);
43363 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43366 this.view = new Roo.View(this.list, this.tpl, {
43367 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43370 this.view.on('click', this.onViewClick, this);
43372 this.store.on('beforeload', this.onBeforeLoad, this);
43373 this.store.on('load', this.onLoad, this);
43374 this.store.on('loadexception', this.onLoadException, this);
43376 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43377 "up" : function(e){
43378 this.inKeyMode = true;
43382 "down" : function(e){
43383 if(!this.isExpanded()){
43384 this.onTriggerClick();
43386 this.inKeyMode = true;
43391 "enter" : function(e){
43394 if(this.fireEvent("specialkey", this, e)){
43395 this.onViewClick(false);
43401 "esc" : function(e){
43405 "tab" : function(e){
43408 if(this.fireEvent("specialkey", this, e)){
43409 this.onViewClick(false);
43417 doRelay : function(foo, bar, hname){
43418 if(hname == 'down' || this.scope.isExpanded()){
43419 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43427 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43431 initNumberEvent : function(e)
43433 this.inputEl().on("keydown" , this.fireKey, this);
43434 this.inputEl().on("focus", this.onFocus, this);
43435 this.inputEl().on("blur", this.onBlur, this);
43437 this.inputEl().relayEvent('keyup', this);
43439 if(this.indicator){
43440 this.indicator.addClass('invisible');
43443 this.originalValue = this.getValue();
43445 if(this.validationEvent == 'keyup'){
43446 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43447 this.inputEl().on('keyup', this.filterValidation, this);
43449 else if(this.validationEvent !== false){
43450 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43453 if(this.selectOnFocus){
43454 this.on("focus", this.preFocus, this);
43457 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43458 this.inputEl().on("keypress", this.filterKeys, this);
43460 this.inputEl().relayEvent('keypress', this);
43463 var allowed = "0123456789";
43465 if(this.allowDecimals){
43466 allowed += this.decimalSeparator;
43469 if(this.allowNegative){
43473 if(this.thousandsDelimiter) {
43477 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43479 var keyPress = function(e){
43481 var k = e.getKey();
43483 var c = e.getCharCode();
43486 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43487 allowed.indexOf(String.fromCharCode(c)) === -1
43493 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43497 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43502 this.inputEl().on("keypress", keyPress, this);
43506 onTriggerClick : function(e)
43513 this.loadNext = false;
43515 if(this.isExpanded()){
43520 this.hasFocus = true;
43522 if(this.triggerAction == 'all') {
43523 this.doQuery(this.allQuery, true);
43527 this.doQuery(this.getRawValue());
43530 getCurrency : function()
43532 var v = this.currencyEl().getValue();
43537 restrictHeight : function()
43539 this.list.alignTo(this.currencyEl(), this.listAlign);
43540 this.list.alignTo(this.currencyEl(), this.listAlign);
43543 onViewClick : function(view, doFocus, el, e)
43545 var index = this.view.getSelectedIndexes()[0];
43547 var r = this.store.getAt(index);
43550 this.onSelect(r, index);
43554 onSelect : function(record, index){
43556 if(this.fireEvent('beforeselect', this, record, index) !== false){
43558 this.setFromCurrencyData(index > -1 ? record.data : false);
43562 this.fireEvent('select', this, record, index);
43566 setFromCurrencyData : function(o)
43570 this.lastCurrency = o;
43572 if (this.currencyField) {
43573 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43575 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43578 this.lastSelectionText = currency;
43580 //setting default currency
43581 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43582 this.setCurrency(this.defaultCurrency);
43586 this.setCurrency(currency);
43589 setFromData : function(o)
43593 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43595 this.setFromCurrencyData(c);
43600 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43602 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43605 this.setValue(value);
43609 setCurrency : function(v)
43611 this.currencyValue = v;
43614 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43619 setValue : function(v)
43621 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43627 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43629 this.inputEl().dom.value = (v == '') ? '' :
43630 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43632 if(!this.allowZero && v === '0') {
43633 this.hiddenEl().dom.value = '';
43634 this.inputEl().dom.value = '';
43641 getRawValue : function()
43643 var v = this.inputEl().getValue();
43648 getValue : function()
43650 return this.fixPrecision(this.parseValue(this.getRawValue()));
43653 parseValue : function(value)
43655 if(this.thousandsDelimiter) {
43657 r = new RegExp(",", "g");
43658 value = value.replace(r, "");
43661 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43662 return isNaN(value) ? '' : value;
43666 fixPrecision : function(value)
43668 if(this.thousandsDelimiter) {
43670 r = new RegExp(",", "g");
43671 value = value.replace(r, "");
43674 var nan = isNaN(value);
43676 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43677 return nan ? '' : value;
43679 return parseFloat(value).toFixed(this.decimalPrecision);
43682 decimalPrecisionFcn : function(v)
43684 return Math.floor(v);
43687 validateValue : function(value)
43689 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43693 var num = this.parseValue(value);
43696 this.markInvalid(String.format(this.nanText, value));
43700 if(num < this.minValue){
43701 this.markInvalid(String.format(this.minText, this.minValue));
43705 if(num > this.maxValue){
43706 this.markInvalid(String.format(this.maxText, this.maxValue));
43713 validate : function()
43715 if(this.disabled || this.allowBlank){
43720 var currency = this.getCurrency();
43722 if(this.validateValue(this.getRawValue()) && currency.length){
43727 this.markInvalid();
43731 getName: function()
43736 beforeBlur : function()
43742 var v = this.parseValue(this.getRawValue());
43749 onBlur : function()
43753 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43754 //this.el.removeClass(this.focusClass);
43757 this.hasFocus = false;
43759 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43763 var v = this.getValue();
43765 if(String(v) !== String(this.startValue)){
43766 this.fireEvent('change', this, v, this.startValue);
43769 this.fireEvent("blur", this);
43772 inputEl : function()
43774 return this.el.select('.roo-money-amount-input', true).first();
43777 currencyEl : function()
43779 return this.el.select('.roo-money-currency-input', true).first();
43782 hiddenEl : function()
43784 return this.el.select('input.hidden-number-input',true).first();
43788 * @class Roo.bootstrap.BezierSignature
43789 * @extends Roo.bootstrap.Component
43790 * Bootstrap BezierSignature class
43791 * This script refer to:
43792 * Title: Signature Pad
43794 * Availability: https://github.com/szimek/signature_pad
43797 * Create a new BezierSignature
43798 * @param {Object} config The config object
43801 Roo.bootstrap.BezierSignature = function(config){
43802 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43808 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43815 mouse_btn_down: true,
43818 * @cfg {int} canvas height
43820 canvas_height: '200px',
43823 * @cfg {float|function} Radius of a single dot.
43828 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43833 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43838 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43843 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43848 * @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.
43850 bg_color: 'rgba(0, 0, 0, 0)',
43853 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43855 dot_color: 'black',
43858 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43860 velocity_filter_weight: 0.7,
43863 * @cfg {function} Callback when stroke begin.
43868 * @cfg {function} Callback when stroke end.
43872 getAutoCreate : function()
43874 var cls = 'roo-signature column';
43877 cls += ' ' + this.cls;
43887 for(var i = 0; i < col_sizes.length; i++) {
43888 if(this[col_sizes[i]]) {
43889 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43899 cls: 'roo-signature-body',
43903 cls: 'roo-signature-body-canvas',
43904 height: this.canvas_height,
43905 width: this.canvas_width
43912 style: 'display: none'
43920 initEvents: function()
43922 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43924 var canvas = this.canvasEl();
43926 // mouse && touch event swapping...
43927 canvas.dom.style.touchAction = 'none';
43928 canvas.dom.style.msTouchAction = 'none';
43930 this.mouse_btn_down = false;
43931 canvas.on('mousedown', this._handleMouseDown, this);
43932 canvas.on('mousemove', this._handleMouseMove, this);
43933 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43935 if (window.PointerEvent) {
43936 canvas.on('pointerdown', this._handleMouseDown, this);
43937 canvas.on('pointermove', this._handleMouseMove, this);
43938 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43941 if ('ontouchstart' in window) {
43942 canvas.on('touchstart', this._handleTouchStart, this);
43943 canvas.on('touchmove', this._handleTouchMove, this);
43944 canvas.on('touchend', this._handleTouchEnd, this);
43947 Roo.EventManager.onWindowResize(this.resize, this, true);
43949 // file input event
43950 this.fileEl().on('change', this.uploadImage, this);
43957 resize: function(){
43959 var canvas = this.canvasEl().dom;
43960 var ctx = this.canvasElCtx();
43961 var img_data = false;
43963 if(canvas.width > 0) {
43964 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43966 // setting canvas width will clean img data
43969 var style = window.getComputedStyle ?
43970 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43972 var padding_left = parseInt(style.paddingLeft) || 0;
43973 var padding_right = parseInt(style.paddingRight) || 0;
43975 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43978 ctx.putImageData(img_data, 0, 0);
43982 _handleMouseDown: function(e)
43984 if (e.browserEvent.which === 1) {
43985 this.mouse_btn_down = true;
43986 this.strokeBegin(e);
43990 _handleMouseMove: function (e)
43992 if (this.mouse_btn_down) {
43993 this.strokeMoveUpdate(e);
43997 _handleMouseUp: function (e)
43999 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44000 this.mouse_btn_down = false;
44005 _handleTouchStart: function (e) {
44007 e.preventDefault();
44008 if (e.browserEvent.targetTouches.length === 1) {
44009 // var touch = e.browserEvent.changedTouches[0];
44010 // this.strokeBegin(touch);
44012 this.strokeBegin(e); // assume e catching the correct xy...
44016 _handleTouchMove: function (e) {
44017 e.preventDefault();
44018 // var touch = event.targetTouches[0];
44019 // _this._strokeMoveUpdate(touch);
44020 this.strokeMoveUpdate(e);
44023 _handleTouchEnd: function (e) {
44024 var wasCanvasTouched = e.target === this.canvasEl().dom;
44025 if (wasCanvasTouched) {
44026 e.preventDefault();
44027 // var touch = event.changedTouches[0];
44028 // _this._strokeEnd(touch);
44033 reset: function () {
44034 this._lastPoints = [];
44035 this._lastVelocity = 0;
44036 this._lastWidth = (this.min_width + this.max_width) / 2;
44037 this.canvasElCtx().fillStyle = this.dot_color;
44040 strokeMoveUpdate: function(e)
44042 this.strokeUpdate(e);
44044 if (this.throttle) {
44045 this.throttleStroke(this.strokeUpdate, this.throttle);
44048 this.strokeUpdate(e);
44052 strokeBegin: function(e)
44054 var newPointGroup = {
44055 color: this.dot_color,
44059 if (typeof this.onBegin === 'function') {
44063 this.curve_data.push(newPointGroup);
44065 this.strokeUpdate(e);
44068 strokeUpdate: function(e)
44070 var rect = this.canvasEl().dom.getBoundingClientRect();
44071 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44072 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44073 var lastPoints = lastPointGroup.points;
44074 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44075 var isLastPointTooClose = lastPoint
44076 ? point.distanceTo(lastPoint) <= this.min_distance
44078 var color = lastPointGroup.color;
44079 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44080 var curve = this.addPoint(point);
44082 this.drawDot({color: color, point: point});
44085 this.drawCurve({color: color, curve: curve});
44095 strokeEnd: function(e)
44097 this.strokeUpdate(e);
44098 if (typeof this.onEnd === 'function') {
44103 addPoint: function (point) {
44104 var _lastPoints = this._lastPoints;
44105 _lastPoints.push(point);
44106 if (_lastPoints.length > 2) {
44107 if (_lastPoints.length === 3) {
44108 _lastPoints.unshift(_lastPoints[0]);
44110 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44111 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44112 _lastPoints.shift();
44118 calculateCurveWidths: function (startPoint, endPoint) {
44119 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44120 (1 - this.velocity_filter_weight) * this._lastVelocity;
44122 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44125 start: this._lastWidth
44128 this._lastVelocity = velocity;
44129 this._lastWidth = newWidth;
44133 drawDot: function (_a) {
44134 var color = _a.color, point = _a.point;
44135 var ctx = this.canvasElCtx();
44136 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44138 this.drawCurveSegment(point.x, point.y, width);
44140 ctx.fillStyle = color;
44144 drawCurve: function (_a) {
44145 var color = _a.color, curve = _a.curve;
44146 var ctx = this.canvasElCtx();
44147 var widthDelta = curve.endWidth - curve.startWidth;
44148 var drawSteps = Math.floor(curve.length()) * 2;
44150 ctx.fillStyle = color;
44151 for (var i = 0; i < drawSteps; i += 1) {
44152 var t = i / drawSteps;
44158 var x = uuu * curve.startPoint.x;
44159 x += 3 * uu * t * curve.control1.x;
44160 x += 3 * u * tt * curve.control2.x;
44161 x += ttt * curve.endPoint.x;
44162 var y = uuu * curve.startPoint.y;
44163 y += 3 * uu * t * curve.control1.y;
44164 y += 3 * u * tt * curve.control2.y;
44165 y += ttt * curve.endPoint.y;
44166 var width = curve.startWidth + ttt * widthDelta;
44167 this.drawCurveSegment(x, y, width);
44173 drawCurveSegment: function (x, y, width) {
44174 var ctx = this.canvasElCtx();
44176 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44177 this.is_empty = false;
44182 var ctx = this.canvasElCtx();
44183 var canvas = this.canvasEl().dom;
44184 ctx.fillStyle = this.bg_color;
44185 ctx.clearRect(0, 0, canvas.width, canvas.height);
44186 ctx.fillRect(0, 0, canvas.width, canvas.height);
44187 this.curve_data = [];
44189 this.is_empty = true;
44194 return this.el.select('input',true).first();
44197 canvasEl: function()
44199 return this.el.select('canvas',true).first();
44202 canvasElCtx: function()
44204 return this.el.select('canvas',true).first().dom.getContext('2d');
44207 getImage: function(type)
44209 if(this.is_empty) {
44214 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44217 drawFromImage: function(img_src)
44219 var img = new Image();
44221 img.onload = function(){
44222 this.canvasElCtx().drawImage(img, 0, 0);
44227 this.is_empty = false;
44230 selectImage: function()
44232 this.fileEl().dom.click();
44235 uploadImage: function(e)
44237 var reader = new FileReader();
44239 reader.onload = function(e){
44240 var img = new Image();
44241 img.onload = function(){
44243 this.canvasElCtx().drawImage(img, 0, 0);
44245 img.src = e.target.result;
44248 reader.readAsDataURL(e.target.files[0]);
44251 // Bezier Point Constructor
44252 Point: (function () {
44253 function Point(x, y, time) {
44256 this.time = time || Date.now();
44258 Point.prototype.distanceTo = function (start) {
44259 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44261 Point.prototype.equals = function (other) {
44262 return this.x === other.x && this.y === other.y && this.time === other.time;
44264 Point.prototype.velocityFrom = function (start) {
44265 return this.time !== start.time
44266 ? this.distanceTo(start) / (this.time - start.time)
44273 // Bezier Constructor
44274 Bezier: (function () {
44275 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44276 this.startPoint = startPoint;
44277 this.control2 = control2;
44278 this.control1 = control1;
44279 this.endPoint = endPoint;
44280 this.startWidth = startWidth;
44281 this.endWidth = endWidth;
44283 Bezier.fromPoints = function (points, widths, scope) {
44284 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44285 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44286 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44288 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44289 var dx1 = s1.x - s2.x;
44290 var dy1 = s1.y - s2.y;
44291 var dx2 = s2.x - s3.x;
44292 var dy2 = s2.y - s3.y;
44293 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44294 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44295 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44296 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44297 var dxm = m1.x - m2.x;
44298 var dym = m1.y - m2.y;
44299 var k = l2 / (l1 + l2);
44300 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44301 var tx = s2.x - cm.x;
44302 var ty = s2.y - cm.y;
44304 c1: new scope.Point(m1.x + tx, m1.y + ty),
44305 c2: new scope.Point(m2.x + tx, m2.y + ty)
44308 Bezier.prototype.length = function () {
44313 for (var i = 0; i <= steps; i += 1) {
44315 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44316 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44318 var xdiff = cx - px;
44319 var ydiff = cy - py;
44320 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44327 Bezier.prototype.point = function (t, start, c1, c2, end) {
44328 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44329 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44330 + (3.0 * c2 * (1.0 - t) * t * t)
44331 + (end * t * t * t);
44336 throttleStroke: function(fn, wait) {
44337 if (wait === void 0) { wait = 250; }
44339 var timeout = null;
44343 var later = function () {
44344 previous = Date.now();
44346 result = fn.apply(storedContext, storedArgs);
44348 storedContext = null;
44352 return function wrapper() {
44354 for (var _i = 0; _i < arguments.length; _i++) {
44355 args[_i] = arguments[_i];
44357 var now = Date.now();
44358 var remaining = wait - (now - previous);
44359 storedContext = this;
44361 if (remaining <= 0 || remaining > wait) {
44363 clearTimeout(timeout);
44367 result = fn.apply(storedContext, storedArgs);
44369 storedContext = null;
44373 else if (!timeout) {
44374 timeout = window.setTimeout(later, remaining);