2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952 * @cfg {String} size (sm|lg|xl) default empty
3953 * @cfg {Number} max_width set the max width of modal
3954 * @cfg {Boolean} editableTitle can the title be edited
3959 * Create a new Modal Dialog
3960 * @param {Object} config The config object
3963 Roo.bootstrap.Modal = function(config){
3964 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969 * The raw btnclick event for the button
3970 * @param {Roo.EventObject} e
3975 * Fire when dialog resize
3976 * @param {Roo.bootstrap.Modal} this
3977 * @param {Roo.EventObject} e
3981 * @event titlechanged
3982 * Fire when the editable title has been changed
3983 * @param {Roo.bootstrap.Modal} this
3984 * @param {Roo.EventObject} value
3986 "titlechanged" : true
3989 this.buttons = this.buttons || [];
3992 this.tmpl = Roo.factory(this.tmpl);
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3999 title : 'test dialog',
4009 specificTitle: false,
4011 buttonPosition: 'right',
4033 editableTitle : false,
4035 onRender : function(ct, position)
4037 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4040 var cfg = Roo.apply({}, this.getAutoCreate());
4043 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045 //if (!cfg.name.length) {
4049 cfg.cls += ' ' + this.cls;
4052 cfg.style = this.style;
4054 this.el = Roo.get(document.body).createChild(cfg, position);
4056 //var type = this.el.dom.type;
4059 if(this.tabIndex !== undefined){
4060 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4063 this.dialogEl = this.el.select('.modal-dialog',true).first();
4064 this.bodyEl = this.el.select('.modal-body',true).first();
4065 this.closeEl = this.el.select('.modal-header .close', true).first();
4066 this.headerEl = this.el.select('.modal-header',true).first();
4067 this.titleEl = this.el.select('.modal-title',true).first();
4068 this.footerEl = this.el.select('.modal-footer',true).first();
4070 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072 //this.el.addClass("x-dlg-modal");
4074 if (this.buttons.length) {
4075 Roo.each(this.buttons, function(bb) {
4076 var b = Roo.apply({}, bb);
4077 b.xns = b.xns || Roo.bootstrap;
4078 b.xtype = b.xtype || 'Button';
4079 if (typeof(b.listeners) == 'undefined') {
4080 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4083 var btn = Roo.factory(b);
4085 btn.render(this.getButtonContainer());
4089 // render the children.
4092 if(typeof(this.items) != 'undefined'){
4093 var items = this.items;
4096 for(var i =0;i < items.length;i++) {
4097 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4101 this.items = nitems;
4103 // where are these used - they used to be body/close/footer
4107 //this.el.addClass([this.fieldClass, this.cls]);
4111 getAutoCreate : function()
4113 // we will default to modal-body-overflow - might need to remove or make optional later.
4115 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''),
4116 html : this.html || ''
4121 cls : 'modal-title',
4125 if(this.specificTitle){ // WTF is this?
4130 if (this.allow_close && Roo.bootstrap.version == 3) {
4140 if (this.editableTitle) {
4142 cls: 'form-control roo-editable-title d-none',
4148 if (this.allow_close && Roo.bootstrap.version == 4) {
4158 if(this.size.length){
4159 size = 'modal-' + this.size;
4162 var footer = Roo.bootstrap.version == 3 ?
4164 cls : 'modal-footer',
4168 cls: 'btn-' + this.buttonPosition
4173 { // BS4 uses mr-auto on left buttons....
4174 cls : 'modal-footer'
4185 cls: "modal-dialog " + size,
4188 cls : "modal-content",
4191 cls : 'modal-header',
4206 modal.cls += ' fade';
4212 getChildContainer : function() {
4217 getButtonContainer : function() {
4219 return Roo.bootstrap.version == 4 ?
4220 this.el.select('.modal-footer',true).first()
4221 : this.el.select('.modal-footer div',true).first();
4224 initEvents : function()
4226 if (this.allow_close) {
4227 this.closeEl.on('click', this.hide, this);
4229 Roo.EventManager.onWindowResize(this.resize, this, true);
4230 if (this.editableTitle) {
4231 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4232 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233 this.headerEditEl.on('keyup', function(e) {
4234 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235 this.toggleHeaderInput(false)
4238 this.headerEditEl.on('blur', function(e) {
4239 this.toggleHeaderInput(false)
4248 this.maskEl.setSize(
4249 Roo.lib.Dom.getViewWidth(true),
4250 Roo.lib.Dom.getViewHeight(true)
4253 if (this.fitwindow) {
4257 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263 if(this.max_width !== 0) {
4265 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4268 this.setSize(w, this.height);
4272 if(this.max_height) {
4273 this.setSize(w,Math.min(
4275 Roo.lib.Dom.getViewportHeight(true) - 60
4281 if(!this.fit_content) {
4282 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4286 this.setSize(w, Math.min(
4288 this.headerEl.getHeight() +
4289 this.footerEl.getHeight() +
4290 this.getChildHeight(this.bodyEl.dom.childNodes),
4291 Roo.lib.Dom.getViewportHeight(true) - 60)
4297 setSize : function(w,h)
4308 if (!this.rendered) {
4311 this.toggleHeaderInput(false);
4312 //this.el.setStyle('display', 'block');
4313 this.el.removeClass('hideing');
4314 this.el.dom.style.display='block';
4316 Roo.get(document.body).addClass('modal-open');
4318 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4321 this.el.addClass('show');
4322 this.el.addClass('in');
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 // not sure how we can show data in here..
4331 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4334 Roo.get(document.body).addClass("x-body-masked");
4336 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4337 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338 this.maskEl.dom.style.display = 'block';
4339 this.maskEl.addClass('show');
4344 this.fireEvent('show', this);
4346 // set zindex here - otherwise it appears to be ignored...
4347 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4350 this.items.forEach( function(e) {
4351 e.layout ? e.layout() : false;
4359 if(this.fireEvent("beforehide", this) !== false){
4361 this.maskEl.removeClass('show');
4363 this.maskEl.dom.style.display = '';
4364 Roo.get(document.body).removeClass("x-body-masked");
4365 this.el.removeClass('in');
4366 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368 if(this.animate){ // why
4369 this.el.addClass('hideing');
4370 this.el.removeClass('show');
4372 if (!this.el.hasClass('hideing')) {
4373 return; // it's been shown again...
4376 this.el.dom.style.display='';
4378 Roo.get(document.body).removeClass('modal-open');
4379 this.el.removeClass('hideing');
4383 this.el.removeClass('show');
4384 this.el.dom.style.display='';
4385 Roo.get(document.body).removeClass('modal-open');
4388 this.fireEvent('hide', this);
4391 isVisible : function()
4394 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4398 addButton : function(str, cb)
4402 var b = Roo.apply({}, { html : str } );
4403 b.xns = b.xns || Roo.bootstrap;
4404 b.xtype = b.xtype || 'Button';
4405 if (typeof(b.listeners) == 'undefined') {
4406 b.listeners = { click : cb.createDelegate(this) };
4409 var btn = Roo.factory(b);
4411 btn.render(this.getButtonContainer());
4417 setDefaultButton : function(btn)
4419 //this.el.select('.modal-footer').()
4422 resizeTo: function(w,h)
4424 this.dialogEl.setWidth(w);
4426 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4428 this.bodyEl.setHeight(h - diff);
4430 this.fireEvent('resize', this);
4433 setContentSize : function(w, h)
4437 onButtonClick: function(btn,e)
4440 this.fireEvent('btnclick', btn.name, e);
4443 * Set the title of the Dialog
4444 * @param {String} str new Title
4446 setTitle: function(str) {
4447 this.titleEl.dom.innerHTML = str;
4451 * Set the body of the Dialog
4452 * @param {String} str new Title
4454 setBody: function(str) {
4455 this.bodyEl.dom.innerHTML = str;
4458 * Set the body of the Dialog using the template
4459 * @param {Obj} data - apply this data to the template and replace the body contents.
4461 applyBody: function(obj)
4464 Roo.log("Error - using apply Body without a template");
4467 this.tmpl.overwrite(this.bodyEl, obj);
4470 getChildHeight : function(child_nodes)
4474 child_nodes.length == 0
4479 var child_height = 0;
4481 for(var i = 0; i < child_nodes.length; i++) {
4484 * for modal with tabs...
4485 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487 var layout_childs = child_nodes[i].childNodes;
4489 for(var j = 0; j < layout_childs.length; j++) {
4491 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493 var layout_body_childs = layout_childs[j].childNodes;
4495 for(var k = 0; k < layout_body_childs.length; k++) {
4497 if(layout_body_childs[k].classList.contains('navbar')) {
4498 child_height += layout_body_childs[k].offsetHeight;
4502 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4524 child_height += child_nodes[i].offsetHeight;
4525 // Roo.log(child_nodes[i].offsetHeight);
4528 return child_height;
4530 toggleHeaderInput : function(is_edit)
4532 if (!this.editableTitle) {
4533 return; // not editable.
4535 if (is_edit && this.is_header_editing) {
4536 return; // already editing..
4540 this.headerEditEl.dom.value = this.title;
4541 this.headerEditEl.removeClass('d-none');
4542 this.headerEditEl.dom.focus();
4543 this.titleEl.addClass('d-none');
4545 this.is_header_editing = true;
4548 // flip back to not editing.
4549 this.title = this.headerEditEl.dom.value;
4550 this.headerEditEl.addClass('d-none');
4551 this.titleEl.removeClass('d-none');
4552 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553 this.is_header_editing = false;
4554 this.fireEvent('titlechanged', this, this.title);
4563 Roo.apply(Roo.bootstrap.Modal, {
4565 * Button config that displays a single OK button
4574 * Button config that displays Yes and No buttons
4590 * Button config that displays OK and Cancel buttons
4605 * Button config that displays Yes, No and Cancel buttons
4630 * messagebox - can be used as a replace
4634 * @class Roo.MessageBox
4635 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644 // process text value...
4648 // Show a dialog using config options:
4650 title:'Save Changes?',
4651 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652 buttons: Roo.Msg.YESNOCANCEL,
4659 Roo.bootstrap.MessageBox = function(){
4660 var dlg, opt, mask, waitTimer;
4661 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662 var buttons, activeTextEl, bwidth;
4666 var handleButton = function(button){
4668 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4672 var handleHide = function(){
4674 dlg.el.removeClass(opt.cls);
4677 // Roo.TaskMgr.stop(waitTimer);
4678 // waitTimer = null;
4683 var updateButtons = function(b){
4686 buttons["ok"].hide();
4687 buttons["cancel"].hide();
4688 buttons["yes"].hide();
4689 buttons["no"].hide();
4690 dlg.footerEl.hide();
4694 dlg.footerEl.show();
4695 for(var k in buttons){
4696 if(typeof buttons[k] != "function"){
4699 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700 width += buttons[k].el.getWidth()+15;
4710 var handleEsc = function(d, k, e){
4711 if(opt && opt.closable !== false){
4721 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722 * @return {Roo.BasicDialog} The BasicDialog element
4724 getDialog : function(){
4726 dlg = new Roo.bootstrap.Modal( {
4729 //constraintoviewport:false,
4731 //collapsible : false,
4736 //buttonAlign:"center",
4737 closeClick : function(){
4738 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4741 handleButton("cancel");
4746 dlg.on("hide", handleHide);
4748 //dlg.addKeyListener(27, handleEsc);
4750 this.buttons = buttons;
4751 var bt = this.buttonText;
4752 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757 bodyEl = dlg.bodyEl.createChild({
4759 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760 '<textarea class="roo-mb-textarea"></textarea>' +
4761 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4763 msgEl = bodyEl.dom.firstChild;
4764 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765 textboxEl.enableDisplayMode();
4766 textboxEl.addKeyListener([10,13], function(){
4767 if(dlg.isVisible() && opt && opt.buttons){
4770 }else if(opt.buttons.yes){
4771 handleButton("yes");
4775 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776 textareaEl.enableDisplayMode();
4777 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778 progressEl.enableDisplayMode();
4780 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781 var pf = progressEl.dom.firstChild;
4783 pp = Roo.get(pf.firstChild);
4784 pp.setHeight(pf.offsetHeight);
4792 * Updates the message box body text
4793 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794 * the XHTML-compliant non-breaking space character '&#160;')
4795 * @return {Roo.MessageBox} This message box
4797 updateText : function(text)
4799 if(!dlg.isVisible() && !opt.width){
4800 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803 msgEl.innerHTML = text || ' ';
4805 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808 Math.min(opt.width || cw , this.maxWidth),
4809 Math.max(opt.minWidth || this.minWidth, bwidth)
4812 activeTextEl.setWidth(w);
4814 if(dlg.isVisible()){
4815 dlg.fixedcenter = false;
4817 // to big, make it scroll. = But as usual stupid IE does not support
4820 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824 bodyEl.dom.style.height = '';
4825 bodyEl.dom.style.overflowY = '';
4828 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830 bodyEl.dom.style.overflowX = '';
4833 dlg.setContentSize(w, bodyEl.getHeight());
4834 if(dlg.isVisible()){
4835 dlg.fixedcenter = true;
4841 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4842 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845 * @return {Roo.MessageBox} This message box
4847 updateProgress : function(value, text){
4849 this.updateText(text);
4852 if (pp) { // weird bug on my firefox - for some reason this is not defined
4853 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4860 * Returns true if the message box is currently displayed
4861 * @return {Boolean} True if the message box is visible, else false
4863 isVisible : function(){
4864 return dlg && dlg.isVisible();
4868 * Hides the message box if it is displayed
4871 if(this.isVisible()){
4877 * Displays a new message box, or reinitializes an existing message box, based on the config options
4878 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879 * The following config object properties are supported:
4881 Property Type Description
4882 ---------- --------------- ------------------------------------------------------------------------------------
4883 animEl String/Element An id or Element from which the message box should animate as it opens and
4884 closes (defaults to undefined)
4885 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable Boolean False to hide the top-right close button (defaults to true). Note that
4888 progress and wait dialogs will ignore this property and always hide the
4889 close button as they can only be closed programmatically.
4890 cls String A custom CSS class to apply to the message box element
4891 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4892 displayed (defaults to 75)
4893 fn Function A callback function to execute after closing the dialog. The arguments to the
4894 function will be btn (the name of the button that was clicked, if applicable,
4895 e.g. "ok"), and text (the value of the active text field, if applicable).
4896 Progress and wait dialogs will ignore this option since they do not respond to
4897 user actions and can only be closed programmatically, so any required function
4898 should be called by the same code after it closes the dialog.
4899 icon String A CSS class that provides a background image to be used as an icon for
4900 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4902 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4903 modal Boolean False to allow user interaction with the page while the message box is
4904 displayed (defaults to true)
4905 msg String A string that will replace the existing message box body text (defaults
4906 to the XHTML-compliant non-breaking space character ' ')
4907 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4908 progress Boolean True to display a progress bar (defaults to false)
4909 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4912 title String The title text
4913 value String The string value to set into the active textbox element if displayed
4914 wait Boolean True to display a progress bar (defaults to false)
4915 width Number The width of the dialog in pixels
4922 msg: 'Please enter your address:',
4924 buttons: Roo.MessageBox.OKCANCEL,
4927 animEl: 'addAddressBtn'
4930 * @param {Object} config Configuration options
4931 * @return {Roo.MessageBox} This message box
4933 show : function(options)
4936 // this causes nightmares if you show one dialog after another
4937 // especially on callbacks..
4939 if(this.isVisible()){
4942 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4944 Roo.log("New Dialog Message:" + options.msg )
4945 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4949 var d = this.getDialog();
4951 d.setTitle(opt.title || " ");
4952 d.closeEl.setDisplayed(opt.closable !== false);
4953 activeTextEl = textboxEl;
4954 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959 textareaEl.setHeight(typeof opt.multiline == "number" ?
4960 opt.multiline : this.defaultTextHeight);
4961 activeTextEl = textareaEl;
4970 progressEl.setDisplayed(opt.progress === true);
4972 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974 this.updateProgress(0);
4975 activeTextEl.dom.value = opt.value || "";
4977 dlg.setDefaultButton(activeTextEl);
4979 var bs = opt.buttons;
4983 }else if(bs && bs.yes){
4984 db = buttons["yes"];
4986 dlg.setDefaultButton(db);
4988 bwidth = updateButtons(opt.buttons);
4989 this.updateText(opt.msg);
4991 d.el.addClass(opt.cls);
4993 d.proxyDrag = opt.proxyDrag === true;
4994 d.modal = opt.modal !== false;
4995 d.mask = opt.modal !== false ? mask : false;
4997 // force it to the end of the z-index stack so it gets a cursor in FF
4998 document.body.appendChild(dlg.el.dom);
4999 d.animateTarget = null;
5000 d.show(options.animEl);
5006 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5007 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008 * and closing the message box when the process is complete.
5009 * @param {String} title The title bar text
5010 * @param {String} msg The message box body text
5011 * @return {Roo.MessageBox} This message box
5013 progress : function(title, msg){
5020 minWidth: this.minProgressWidth,
5027 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028 * If a callback function is passed it will be called after the user clicks the button, and the
5029 * id of the button that was clicked will be passed as the only parameter to the callback
5030 * (could also be the top-right close button).
5031 * @param {String} title The title bar text
5032 * @param {String} msg The message box body text
5033 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034 * @param {Object} scope (optional) The scope of the callback function
5035 * @return {Roo.MessageBox} This message box
5037 alert : function(title, msg, fn, scope)
5052 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5053 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054 * You are responsible for closing the message box when the process is complete.
5055 * @param {String} msg The message box body text
5056 * @param {String} title (optional) The title bar text
5057 * @return {Roo.MessageBox} This message box
5059 wait : function(msg, title){
5070 waitTimer = Roo.TaskMgr.start({
5072 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5080 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083 * @param {String} title The title bar text
5084 * @param {String} msg The message box body text
5085 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086 * @param {Object} scope (optional) The scope of the callback function
5087 * @return {Roo.MessageBox} This message box
5089 confirm : function(title, msg, fn, scope){
5093 buttons: this.YESNO,
5102 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5104 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105 * (could also be the top-right close button) and the text that was entered will be passed as the two
5106 * parameters to the callback.
5107 * @param {String} title The title bar text
5108 * @param {String} msg The message box body text
5109 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110 * @param {Object} scope (optional) The scope of the callback function
5111 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113 * @return {Roo.MessageBox} This message box
5115 prompt : function(title, msg, fn, scope, multiline){
5119 buttons: this.OKCANCEL,
5124 multiline: multiline,
5131 * Button config that displays a single OK button
5136 * Button config that displays Yes and No buttons
5139 YESNO : {yes:true, no:true},
5141 * Button config that displays OK and Cancel buttons
5144 OKCANCEL : {ok:true, cancel:true},
5146 * Button config that displays Yes, No and Cancel buttons
5149 YESNOCANCEL : {yes:true, no:true, cancel:true},
5152 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5155 defaultTextHeight : 75,
5157 * The maximum width in pixels of the message box (defaults to 600)
5162 * The minimum width in pixels of the message box (defaults to 100)
5167 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5168 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5171 minProgressWidth : 250,
5173 * An object containing the default button text strings that can be overriden for localized language support.
5174 * Supported properties are: ok, cancel, yes and no.
5175 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5188 * Shorthand for {@link Roo.MessageBox}
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5200 * @class Roo.bootstrap.Navbar
5201 * @extends Roo.bootstrap.Component
5202 * Bootstrap Navbar class
5205 * Create a new Navbar
5206 * @param {Object} config The config object
5210 Roo.bootstrap.Navbar = function(config){
5211 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5215 * @event beforetoggle
5216 * Fire before toggle the menu
5217 * @param {Roo.EventObject} e
5219 "beforetoggle" : true
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5232 getAutoCreate : function(){
5235 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5239 initEvents :function ()
5241 //Roo.log(this.el.select('.navbar-toggle',true));
5242 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5249 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251 var size = this.el.getSize();
5252 this.maskEl.setSize(size.width, size.height);
5253 this.maskEl.enableDisplayMode("block");
5262 getChildContainer : function()
5264 if (this.el && this.el.select('.collapse').getCount()) {
5265 return this.el.select('.collapse',true).first();
5280 onToggle : function()
5283 if(this.fireEvent('beforetoggle', this) === false){
5286 var ce = this.el.select('.navbar-collapse',true).first();
5288 if (!ce.hasClass('show')) {
5298 * Expand the navbar pulldown
5300 expand : function ()
5303 var ce = this.el.select('.navbar-collapse',true).first();
5304 if (ce.hasClass('collapsing')) {
5307 ce.dom.style.height = '';
5309 ce.addClass('in'); // old...
5310 ce.removeClass('collapse');
5311 ce.addClass('show');
5312 var h = ce.getHeight();
5314 ce.removeClass('show');
5315 // at this point we should be able to see it..
5316 ce.addClass('collapsing');
5318 ce.setHeight(0); // resize it ...
5319 ce.on('transitionend', function() {
5320 //Roo.log('done transition');
5321 ce.removeClass('collapsing');
5322 ce.addClass('show');
5323 ce.removeClass('collapse');
5325 ce.dom.style.height = '';
5326 }, this, { single: true} );
5328 ce.dom.scrollTop = 0;
5331 * Collapse the navbar pulldown
5333 collapse : function()
5335 var ce = this.el.select('.navbar-collapse',true).first();
5337 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338 // it's collapsed or collapsing..
5341 ce.removeClass('in'); // old...
5342 ce.setHeight(ce.getHeight());
5343 ce.removeClass('show');
5344 ce.addClass('collapsing');
5346 ce.on('transitionend', function() {
5347 ce.dom.style.height = '';
5348 ce.removeClass('collapsing');
5349 ce.addClass('collapse');
5350 }, this, { single: true} );
5370 * @class Roo.bootstrap.NavSimplebar
5371 * @extends Roo.bootstrap.Navbar
5372 * Bootstrap Sidebar class
5374 * @cfg {Boolean} inverse is inverted color
5376 * @cfg {String} type (nav | pills | tabs)
5377 * @cfg {Boolean} arrangement stacked | justified
5378 * @cfg {String} align (left | right) alignment
5380 * @cfg {Boolean} main (true|false) main nav bar? default false
5381 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383 * @cfg {String} tag (header|footer|nav|div) default is nav
5385 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5389 * Create a new Sidebar
5390 * @param {Object} config The config object
5394 Roo.bootstrap.NavSimplebar = function(config){
5395 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5414 getAutoCreate : function(){
5418 tag : this.tag || 'div',
5419 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421 if (['light','white'].indexOf(this.weight) > -1) {
5422 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424 cfg.cls += ' bg-' + this.weight;
5427 cfg.cls += ' navbar-inverse';
5431 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5442 cls: 'nav nav-' + this.xtype,
5448 this.type = this.type || 'nav';
5449 if (['tabs','pills'].indexOf(this.type) != -1) {
5450 cfg.cn[0].cls += ' nav-' + this.type
5454 if (this.type!=='nav') {
5455 Roo.log('nav type must be nav/tabs/pills')
5457 cfg.cn[0].cls += ' navbar-nav'
5463 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464 cfg.cn[0].cls += ' nav-' + this.arrangement;
5468 if (this.align === 'right') {
5469 cfg.cn[0].cls += ' navbar-right';
5494 * navbar-expand-md fixed-top
5498 * @class Roo.bootstrap.NavHeaderbar
5499 * @extends Roo.bootstrap.NavSimplebar
5500 * Bootstrap Sidebar class
5502 * @cfg {String} brand what is brand
5503 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504 * @cfg {String} brand_href href of the brand
5505 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5506 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5511 * Create a new Sidebar
5512 * @param {Object} config The config object
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5528 desktopCenter : false,
5531 getAutoCreate : function(){
5534 tag: this.nav || 'nav',
5535 cls: 'navbar navbar-expand-md',
5541 if (this.desktopCenter) {
5542 cn.push({cls : 'container', cn : []});
5550 cls: 'navbar-toggle navbar-toggler',
5551 'data-toggle': 'collapse',
5556 html: 'Toggle navigation'
5560 cls: 'icon-bar navbar-toggler-icon'
5573 cn.push( Roo.bootstrap.version == 4 ? btn : {
5575 cls: 'navbar-header',
5584 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5588 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590 if (['light','white'].indexOf(this.weight) > -1) {
5591 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593 cfg.cls += ' bg-' + this.weight;
5596 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599 // tag can override this..
5601 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5604 if (this.brand !== '') {
5605 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608 href: this.brand_href ? this.brand_href : '#',
5609 cls: 'navbar-brand',
5617 cfg.cls += ' main-nav';
5625 getHeaderChildContainer : function()
5627 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628 return this.el.select('.navbar-header',true).first();
5631 return this.getChildContainer();
5634 getChildContainer : function()
5637 return this.el.select('.roo-navbar-collapse',true).first();
5642 initEvents : function()
5644 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646 if (this.autohide) {
5651 Roo.get(document).on('scroll',function(e) {
5652 var ns = Roo.get(document).getScroll().top;
5653 var os = prevScroll;
5657 ft.removeClass('slideDown');
5658 ft.addClass('slideUp');
5661 ft.removeClass('slideUp');
5662 ft.addClass('slideDown');
5683 * @class Roo.bootstrap.NavSidebar
5684 * @extends Roo.bootstrap.Navbar
5685 * Bootstrap Sidebar class
5688 * Create a new Sidebar
5689 * @param {Object} config The config object
5693 Roo.bootstrap.NavSidebar = function(config){
5694 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5699 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701 getAutoCreate : function(){
5706 cls: 'sidebar sidebar-nav'
5728 * @class Roo.bootstrap.NavGroup
5729 * @extends Roo.bootstrap.Component
5730 * Bootstrap NavGroup class
5731 * @cfg {String} align (left|right)
5732 * @cfg {Boolean} inverse
5733 * @cfg {String} type (nav|pills|tab) default nav
5734 * @cfg {String} navId - reference Id for navbar.
5738 * Create a new nav group
5739 * @param {Object} config The config object
5742 Roo.bootstrap.NavGroup = function(config){
5743 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5746 Roo.bootstrap.NavGroup.register(this);
5750 * Fires when the active item changes
5751 * @param {Roo.bootstrap.NavGroup} this
5752 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5771 getAutoCreate : function()
5773 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779 if (Roo.bootstrap.version == 4) {
5780 if (['tabs','pills'].indexOf(this.type) != -1) {
5781 cfg.cls += ' nav-' + this.type;
5783 // trying to remove so header bar can right align top?
5784 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785 // do not use on header bar...
5786 cfg.cls += ' navbar-nav';
5791 if (['tabs','pills'].indexOf(this.type) != -1) {
5792 cfg.cls += ' nav-' + this.type
5794 if (this.type !== 'nav') {
5795 Roo.log('nav type must be nav/tabs/pills')
5797 cfg.cls += ' navbar-nav'
5801 if (this.parent() && this.parent().sidebar) {
5804 cls: 'dashboard-menu sidebar-menu'
5810 if (this.form === true) {
5813 cls: 'navbar-form form-inline'
5815 //nav navbar-right ml-md-auto
5816 if (this.align === 'right') {
5817 cfg.cls += ' navbar-right ml-md-auto';
5819 cfg.cls += ' navbar-left';
5823 if (this.align === 'right') {
5824 cfg.cls += ' navbar-right ml-md-auto';
5826 cfg.cls += ' mr-auto';
5830 cfg.cls += ' navbar-inverse';
5838 * sets the active Navigation item
5839 * @param {Roo.bootstrap.NavItem} the new current navitem
5841 setActiveItem : function(item)
5844 Roo.each(this.navItems, function(v){
5849 v.setActive(false, true);
5856 item.setActive(true, true);
5857 this.fireEvent('changed', this, item, prev);
5862 * gets the active Navigation item
5863 * @return {Roo.bootstrap.NavItem} the current navitem
5865 getActive : function()
5869 Roo.each(this.navItems, function(v){
5880 indexOfNav : function()
5884 Roo.each(this.navItems, function(v,i){
5895 * adds a Navigation item
5896 * @param {Roo.bootstrap.NavItem} the navitem to add
5898 addItem : function(cfg)
5900 if (this.form && Roo.bootstrap.version == 4) {
5903 var cn = new Roo.bootstrap.NavItem(cfg);
5905 cn.parentId = this.id;
5906 cn.onRender(this.el, null);
5910 * register a Navigation item
5911 * @param {Roo.bootstrap.NavItem} the navitem to add
5913 register : function(item)
5915 this.navItems.push( item);
5916 item.navId = this.navId;
5921 * clear all the Navigation item
5924 clearAll : function()
5927 this.el.dom.innerHTML = '';
5930 getNavItem: function(tabId)
5933 Roo.each(this.navItems, function(e) {
5934 if (e.tabId == tabId) {
5944 setActiveNext : function()
5946 var i = this.indexOfNav(this.getActive());
5947 if (i > this.navItems.length) {
5950 this.setActiveItem(this.navItems[i+1]);
5952 setActivePrev : function()
5954 var i = this.indexOfNav(this.getActive());
5958 this.setActiveItem(this.navItems[i-1]);
5960 clearWasActive : function(except) {
5961 Roo.each(this.navItems, function(e) {
5962 if (e.tabId != except.tabId && e.was_active) {
5963 e.was_active = false;
5970 getWasActive : function ()
5973 Roo.each(this.navItems, function(e) {
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5992 * register a Navigation Group
5993 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5995 register : function(navgrp)
5997 this.groups[navgrp.navId] = navgrp;
6001 * fetch a Navigation Group based on the navigation ID
6002 * @param {string} the navgroup to add
6003 * @returns {Roo.bootstrap.NavGroup} the navgroup
6005 get: function(navId) {
6006 if (typeof(this.groups[navId]) == 'undefined') {
6008 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6010 return this.groups[navId] ;
6025 * @class Roo.bootstrap.NavItem
6026 * @extends Roo.bootstrap.Component
6027 * Bootstrap Navbar.NavItem class
6028 * @cfg {String} href link to
6029 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6030 * @cfg {String} html content of button
6031 * @cfg {String} badge text inside badge
6032 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6033 * @cfg {String} glyphicon DEPRICATED - use fa
6034 * @cfg {String} icon DEPRICATED - use fa
6035 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6036 * @cfg {Boolean} active Is item active
6037 * @cfg {Boolean} disabled Is item disabled
6039 * @cfg {Boolean} preventDefault (true | false) default false
6040 * @cfg {String} tabId the tab that this item activates.
6041 * @cfg {String} tagtype (a|span) render as a href or span?
6042 * @cfg {Boolean} animateRef (true|false) link to element default false
6045 * Create a new Navbar Item
6046 * @param {Object} config The config object
6048 Roo.bootstrap.NavItem = function(config){
6049 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6054 * The raw click event for the entire grid.
6055 * @param {Roo.EventObject} e
6060 * Fires when the active item active state changes
6061 * @param {Roo.bootstrap.NavItem} this
6062 * @param {boolean} state the new state
6068 * Fires when scroll to element
6069 * @param {Roo.bootstrap.NavItem} this
6070 * @param {Object} options
6071 * @param {Roo.EventObject} e
6079 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6088 preventDefault : false,
6096 button_outline : false,
6100 getAutoCreate : function(){
6107 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6110 cfg.cls += ' active' ;
6112 if (this.disabled) {
6113 cfg.cls += ' disabled';
6117 if (this.button_weight.length) {
6118 cfg.tag = this.href ? 'a' : 'button';
6119 cfg.html = this.html || '';
6120 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6122 cfg.href = this.href;
6125 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6128 // menu .. should add dropdown-menu class - so no need for carat..
6130 if (this.badge !== '') {
6132 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6137 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141 href : this.href || "#",
6142 html: this.html || ''
6145 if (this.tagtype == 'a') {
6146 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '');
6150 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6153 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6155 if(this.glyphicon) {
6156 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6161 cfg.cn[0].html += " <span class='caret'></span>";
6165 if (this.badge !== '') {
6167 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6175 onRender : function(ct, position)
6177 // Roo.log("Call onRender: " + this.xtype);
6178 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6183 this.navLink = this.el.select('.nav-link',true).first();
6188 initEvents: function()
6190 if (typeof (this.menu) != 'undefined') {
6191 this.menu.parentType = this.xtype;
6192 this.menu.triggerEl = this.el;
6193 this.menu = this.addxtype(Roo.apply({}, this.menu));
6196 this.el.select('a',true).on('click', this.onClick, this);
6198 if(this.tagtype == 'span'){
6199 this.el.select('span',true).on('click', this.onClick, this);
6202 // at this point parent should be available..
6203 this.parent().register(this);
6206 onClick : function(e)
6208 if (e.getTarget('.dropdown-menu-item')) {
6209 // did you click on a menu itemm.... - then don't trigger onclick..
6214 this.preventDefault ||
6217 Roo.log("NavItem - prevent Default?");
6221 if (this.disabled) {
6225 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6226 if (tg && tg.transition) {
6227 Roo.log("waiting for the transitionend");
6233 //Roo.log("fire event clicked");
6234 if(this.fireEvent('click', this, e) === false){
6238 if(this.tagtype == 'span'){
6242 //Roo.log(this.href);
6243 var ael = this.el.select('a',true).first();
6246 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6247 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6248 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6249 return; // ignore... - it's a 'hash' to another page.
6251 Roo.log("NavItem - prevent Default?");
6253 this.scrollToElement(e);
6257 var p = this.parent();
6259 if (['tabs','pills'].indexOf(p.type)!==-1) {
6260 if (typeof(p.setActiveItem) !== 'undefined') {
6261 p.setActiveItem(this);
6265 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6266 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6267 // remove the collapsed menu expand...
6268 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6272 isActive: function () {
6275 setActive : function(state, fire, is_was_active)
6277 if (this.active && !state && this.navId) {
6278 this.was_active = true;
6279 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6281 nv.clearWasActive(this);
6285 this.active = state;
6288 this.el.removeClass('active');
6289 this.navLink ? this.navLink.removeClass('active') : false;
6290 } else if (!this.el.hasClass('active')) {
6292 this.el.addClass('active');
6293 if (Roo.bootstrap.version == 4 && this.navLink ) {
6294 this.navLink.addClass('active');
6299 this.fireEvent('changed', this, state);
6302 // show a panel if it's registered and related..
6304 if (!this.navId || !this.tabId || !state || is_was_active) {
6308 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312 var pan = tg.getPanelByName(this.tabId);
6316 // if we can not flip to new panel - go back to old nav highlight..
6317 if (false == tg.showPanel(pan)) {
6318 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6320 var onav = nv.getWasActive();
6322 onav.setActive(true, false, true);
6331 // this should not be here...
6332 setDisabled : function(state)
6334 this.disabled = state;
6336 this.el.removeClass('disabled');
6337 } else if (!this.el.hasClass('disabled')) {
6338 this.el.addClass('disabled');
6344 * Fetch the element to display the tooltip on.
6345 * @return {Roo.Element} defaults to this.el
6347 tooltipEl : function()
6349 return this.el.select('' + this.tagtype + '', true).first();
6352 scrollToElement : function(e)
6354 var c = document.body;
6357 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6359 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6360 c = document.documentElement;
6363 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6369 var o = target.calcOffsetsTo(c);
6376 this.fireEvent('scrollto', this, options, e);
6378 Roo.get(c).scrollTo('top', options.value, true);
6391 * <span> icon </span>
6392 * <span> text </span>
6393 * <span>badge </span>
6397 * @class Roo.bootstrap.NavSidebarItem
6398 * @extends Roo.bootstrap.NavItem
6399 * Bootstrap Navbar.NavSidebarItem class
6400 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6401 * {Boolean} open is the menu open
6402 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6403 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6404 * {String} buttonSize (sm|md|lg)the extra classes for the button
6405 * {Boolean} showArrow show arrow next to the text (default true)
6407 * Create a new Navbar Button
6408 * @param {Object} config The config object
6410 Roo.bootstrap.NavSidebarItem = function(config){
6411 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6416 * The raw click event for the entire grid.
6417 * @param {Roo.EventObject} e
6422 * Fires when the active item active state changes
6423 * @param {Roo.bootstrap.NavSidebarItem} this
6424 * @param {boolean} state the new state
6432 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6434 badgeWeight : 'default',
6440 buttonWeight : 'default',
6446 getAutoCreate : function(){
6451 href : this.href || '#',
6457 if(this.buttonView){
6460 href : this.href || '#',
6461 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6474 cfg.cls += ' active';
6477 if (this.disabled) {
6478 cfg.cls += ' disabled';
6481 cfg.cls += ' open x-open';
6484 if (this.glyphicon || this.icon) {
6485 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6486 a.cn.push({ tag : 'i', cls : c }) ;
6489 if(!this.buttonView){
6492 html : this.html || ''
6499 if (this.badge !== '') {
6500 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6506 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6509 a.cls += ' dropdown-toggle treeview' ;
6515 initEvents : function()
6517 if (typeof (this.menu) != 'undefined') {
6518 this.menu.parentType = this.xtype;
6519 this.menu.triggerEl = this.el;
6520 this.menu = this.addxtype(Roo.apply({}, this.menu));
6523 this.el.on('click', this.onClick, this);
6525 if(this.badge !== ''){
6526 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6531 onClick : function(e)
6538 if(this.preventDefault){
6542 this.fireEvent('click', this, e);
6545 disable : function()
6547 this.setDisabled(true);
6552 this.setDisabled(false);
6555 setDisabled : function(state)
6557 if(this.disabled == state){
6561 this.disabled = state;
6564 this.el.addClass('disabled');
6568 this.el.removeClass('disabled');
6573 setActive : function(state)
6575 if(this.active == state){
6579 this.active = state;
6582 this.el.addClass('active');
6586 this.el.removeClass('active');
6591 isActive: function ()
6596 setBadge : function(str)
6602 this.badgeEl.dom.innerHTML = str;
6617 Roo.namespace('Roo.bootstrap.breadcrumb');
6621 * @class Roo.bootstrap.breadcrumb.Nav
6622 * @extends Roo.bootstrap.Component
6623 * Bootstrap Breadcrumb Nav Class
6625 * @children Roo.bootstrap.breadcrumb.Item
6628 * Create a new breadcrumb.Nav
6629 * @param {Object} config The config object
6633 Roo.bootstrap.breadcrumb.Nav = function(config){
6634 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6639 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6641 getAutoCreate : function()
6658 initEvents: function()
6660 this.olEl = this.el.select('ol',true).first();
6662 getChildContainer : function()
6678 * @class Roo.bootstrap.breadcrumb.Nav
6679 * @extends Roo.bootstrap.Component
6680 * Bootstrap Breadcrumb Nav Class
6682 * @children Roo.bootstrap.breadcrumb.Component
6683 * @cfg {String} html the content of the link.
6684 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6685 * @cfg {Boolean} active is it active
6689 * Create a new breadcrumb.Nav
6690 * @param {Object} config The config object
6693 Roo.bootstrap.breadcrumb.Item = function(config){
6694 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6699 * The img click event for the img.
6700 * @param {Roo.EventObject} e
6707 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6712 getAutoCreate : function()
6717 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6719 if (this.href !== false) {
6726 cfg.html = this.html;
6732 initEvents: function()
6735 this.el.select('a', true).first().on('click',this.onClick, this)
6739 onClick : function(e)
6742 this.fireEvent('click',this, e);
6755 * @class Roo.bootstrap.Row
6756 * @extends Roo.bootstrap.Component
6757 * Bootstrap Row class (contains columns...)
6761 * @param {Object} config The config object
6764 Roo.bootstrap.Row = function(config){
6765 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6770 getAutoCreate : function(){
6789 * @class Roo.bootstrap.Pagination
6790 * @extends Roo.bootstrap.Component
6791 * Bootstrap Pagination class
6792 * @cfg {String} size xs | sm | md | lg
6793 * @cfg {Boolean} inverse false | true
6796 * Create a new Pagination
6797 * @param {Object} config The config object
6800 Roo.bootstrap.Pagination = function(config){
6801 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6804 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6810 getAutoCreate : function(){
6816 cfg.cls += ' inverse';
6822 cfg.cls += " " + this.cls;
6840 * @class Roo.bootstrap.PaginationItem
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap PaginationItem class
6843 * @cfg {String} html text
6844 * @cfg {String} href the link
6845 * @cfg {Boolean} preventDefault (true | false) default true
6846 * @cfg {Boolean} active (true | false) default false
6847 * @cfg {Boolean} disabled default false
6851 * Create a new PaginationItem
6852 * @param {Object} config The config object
6856 Roo.bootstrap.PaginationItem = function(config){
6857 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6862 * The raw click event for the entire grid.
6863 * @param {Roo.EventObject} e
6869 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6873 preventDefault: true,
6878 getAutoCreate : function(){
6884 href : this.href ? this.href : '#',
6885 html : this.html ? this.html : ''
6895 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6905 initEvents: function() {
6907 this.el.on('click', this.onClick, this);
6910 onClick : function(e)
6912 Roo.log('PaginationItem on click ');
6913 if(this.preventDefault){
6921 this.fireEvent('click', this, e);
6937 * @class Roo.bootstrap.Slider
6938 * @extends Roo.bootstrap.Component
6939 * Bootstrap Slider class
6942 * Create a new Slider
6943 * @param {Object} config The config object
6946 Roo.bootstrap.Slider = function(config){
6947 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6950 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6952 getAutoCreate : function(){
6956 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6972 * Ext JS Library 1.1.1
6973 * Copyright(c) 2006-2007, Ext JS, LLC.
6975 * Originally Released Under LGPL - original licence link has changed is not relivant.
6978 * <script type="text/javascript">
6983 * @class Roo.grid.ColumnModel
6984 * @extends Roo.util.Observable
6985 * This is the default implementation of a ColumnModel used by the Grid. It defines
6986 * the columns in the grid.
6989 var colModel = new Roo.grid.ColumnModel([
6990 {header: "Ticker", width: 60, sortable: true, locked: true},
6991 {header: "Company Name", width: 150, sortable: true},
6992 {header: "Market Cap.", width: 100, sortable: true},
6993 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6994 {header: "Employees", width: 100, sortable: true, resizable: false}
6999 * The config options listed for this class are options which may appear in each
7000 * individual column definition.
7001 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7003 * @param {Object} config An Array of column config objects. See this class's
7004 * config objects for details.
7006 Roo.grid.ColumnModel = function(config){
7008 * The config passed into the constructor
7010 this.config = config;
7013 // if no id, create one
7014 // if the column does not have a dataIndex mapping,
7015 // map it to the order it is in the config
7016 for(var i = 0, len = config.length; i < len; i++){
7018 if(typeof c.dataIndex == "undefined"){
7021 if(typeof c.renderer == "string"){
7022 c.renderer = Roo.util.Format[c.renderer];
7024 if(typeof c.id == "undefined"){
7027 if(c.editor && c.editor.xtype){
7028 c.editor = Roo.factory(c.editor, Roo.grid);
7030 if(c.editor && c.editor.isFormField){
7031 c.editor = new Roo.grid.GridEditor(c.editor);
7033 this.lookup[c.id] = c;
7037 * The width of columns which have no width specified (defaults to 100)
7040 this.defaultWidth = 100;
7043 * Default sortable of columns which have no sortable specified (defaults to false)
7046 this.defaultSortable = false;
7050 * @event widthchange
7051 * Fires when the width of a column changes.
7052 * @param {ColumnModel} this
7053 * @param {Number} columnIndex The column index
7054 * @param {Number} newWidth The new width
7056 "widthchange": true,
7058 * @event headerchange
7059 * Fires when the text of a header changes.
7060 * @param {ColumnModel} this
7061 * @param {Number} columnIndex The column index
7062 * @param {Number} newText The new header text
7064 "headerchange": true,
7066 * @event hiddenchange
7067 * Fires when a column is hidden or "unhidden".
7068 * @param {ColumnModel} this
7069 * @param {Number} columnIndex The column index
7070 * @param {Boolean} hidden true if hidden, false otherwise
7072 "hiddenchange": true,
7074 * @event columnmoved
7075 * Fires when a column is moved.
7076 * @param {ColumnModel} this
7077 * @param {Number} oldIndex
7078 * @param {Number} newIndex
7080 "columnmoved" : true,
7082 * @event columlockchange
7083 * Fires when a column's locked state is changed
7084 * @param {ColumnModel} this
7085 * @param {Number} colIndex
7086 * @param {Boolean} locked true if locked
7088 "columnlockchange" : true
7090 Roo.grid.ColumnModel.superclass.constructor.call(this);
7092 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7094 * @cfg {String} header The header text to display in the Grid view.
7097 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7098 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7099 * specified, the column's index is used as an index into the Record's data Array.
7102 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7103 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7106 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7107 * Defaults to the value of the {@link #defaultSortable} property.
7108 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7111 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7114 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7117 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7120 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7123 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7124 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7125 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7126 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7129 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7132 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7135 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7138 * @cfg {String} cursor (Optional)
7141 * @cfg {String} tooltip (Optional)
7144 * @cfg {Number} xs (Optional)
7147 * @cfg {Number} sm (Optional)
7150 * @cfg {Number} md (Optional)
7153 * @cfg {Number} lg (Optional)
7156 * Returns the id of the column at the specified index.
7157 * @param {Number} index The column index
7158 * @return {String} the id
7160 getColumnId : function(index){
7161 return this.config[index].id;
7165 * Returns the column for a specified id.
7166 * @param {String} id The column id
7167 * @return {Object} the column
7169 getColumnById : function(id){
7170 return this.lookup[id];
7175 * Returns the column for a specified dataIndex.
7176 * @param {String} dataIndex The column dataIndex
7177 * @return {Object|Boolean} the column or false if not found
7179 getColumnByDataIndex: function(dataIndex){
7180 var index = this.findColumnIndex(dataIndex);
7181 return index > -1 ? this.config[index] : false;
7185 * Returns the index for a specified column id.
7186 * @param {String} id The column id
7187 * @return {Number} the index, or -1 if not found
7189 getIndexById : function(id){
7190 for(var i = 0, len = this.config.length; i < len; i++){
7191 if(this.config[i].id == id){
7199 * Returns the index for a specified column dataIndex.
7200 * @param {String} dataIndex The column dataIndex
7201 * @return {Number} the index, or -1 if not found
7204 findColumnIndex : function(dataIndex){
7205 for(var i = 0, len = this.config.length; i < len; i++){
7206 if(this.config[i].dataIndex == dataIndex){
7214 moveColumn : function(oldIndex, newIndex){
7215 var c = this.config[oldIndex];
7216 this.config.splice(oldIndex, 1);
7217 this.config.splice(newIndex, 0, c);
7218 this.dataMap = null;
7219 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7222 isLocked : function(colIndex){
7223 return this.config[colIndex].locked === true;
7226 setLocked : function(colIndex, value, suppressEvent){
7227 if(this.isLocked(colIndex) == value){
7230 this.config[colIndex].locked = value;
7232 this.fireEvent("columnlockchange", this, colIndex, value);
7236 getTotalLockedWidth : function(){
7238 for(var i = 0; i < this.config.length; i++){
7239 if(this.isLocked(i) && !this.isHidden(i)){
7240 this.totalWidth += this.getColumnWidth(i);
7246 getLockedCount : function(){
7247 for(var i = 0, len = this.config.length; i < len; i++){
7248 if(!this.isLocked(i)){
7253 return this.config.length;
7257 * Returns the number of columns.
7260 getColumnCount : function(visibleOnly){
7261 if(visibleOnly === true){
7263 for(var i = 0, len = this.config.length; i < len; i++){
7264 if(!this.isHidden(i)){
7270 return this.config.length;
7274 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7275 * @param {Function} fn
7276 * @param {Object} scope (optional)
7277 * @return {Array} result
7279 getColumnsBy : function(fn, scope){
7281 for(var i = 0, len = this.config.length; i < len; i++){
7282 var c = this.config[i];
7283 if(fn.call(scope||this, c, i) === true){
7291 * Returns true if the specified column is sortable.
7292 * @param {Number} col The column index
7295 isSortable : function(col){
7296 if(typeof this.config[col].sortable == "undefined"){
7297 return this.defaultSortable;
7299 return this.config[col].sortable;
7303 * Returns the rendering (formatting) function defined for the column.
7304 * @param {Number} col The column index.
7305 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7307 getRenderer : function(col){
7308 if(!this.config[col].renderer){
7309 return Roo.grid.ColumnModel.defaultRenderer;
7311 return this.config[col].renderer;
7315 * Sets the rendering (formatting) function for a column.
7316 * @param {Number} col The column index
7317 * @param {Function} fn The function to use to process the cell's raw data
7318 * to return HTML markup for the grid view. The render function is called with
7319 * the following parameters:<ul>
7320 * <li>Data value.</li>
7321 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7322 * <li>css A CSS style string to apply to the table cell.</li>
7323 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7324 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7325 * <li>Row index</li>
7326 * <li>Column index</li>
7327 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7329 setRenderer : function(col, fn){
7330 this.config[col].renderer = fn;
7334 * Returns the width for the specified column.
7335 * @param {Number} col The column index
7338 getColumnWidth : function(col){
7339 return this.config[col].width * 1 || this.defaultWidth;
7343 * Sets the width for a column.
7344 * @param {Number} col The column index
7345 * @param {Number} width The new width
7347 setColumnWidth : function(col, width, suppressEvent){
7348 this.config[col].width = width;
7349 this.totalWidth = null;
7351 this.fireEvent("widthchange", this, col, width);
7356 * Returns the total width of all columns.
7357 * @param {Boolean} includeHidden True to include hidden column widths
7360 getTotalWidth : function(includeHidden){
7361 if(!this.totalWidth){
7362 this.totalWidth = 0;
7363 for(var i = 0, len = this.config.length; i < len; i++){
7364 if(includeHidden || !this.isHidden(i)){
7365 this.totalWidth += this.getColumnWidth(i);
7369 return this.totalWidth;
7373 * Returns the header for the specified column.
7374 * @param {Number} col The column index
7377 getColumnHeader : function(col){
7378 return this.config[col].header;
7382 * Sets the header for a column.
7383 * @param {Number} col The column index
7384 * @param {String} header The new header
7386 setColumnHeader : function(col, header){
7387 this.config[col].header = header;
7388 this.fireEvent("headerchange", this, col, header);
7392 * Returns the tooltip for the specified column.
7393 * @param {Number} col The column index
7396 getColumnTooltip : function(col){
7397 return this.config[col].tooltip;
7400 * Sets the tooltip for a column.
7401 * @param {Number} col The column index
7402 * @param {String} tooltip The new tooltip
7404 setColumnTooltip : function(col, tooltip){
7405 this.config[col].tooltip = tooltip;
7409 * Returns the dataIndex for the specified column.
7410 * @param {Number} col The column index
7413 getDataIndex : function(col){
7414 return this.config[col].dataIndex;
7418 * Sets the dataIndex for a column.
7419 * @param {Number} col The column index
7420 * @param {Number} dataIndex The new dataIndex
7422 setDataIndex : function(col, dataIndex){
7423 this.config[col].dataIndex = dataIndex;
7429 * Returns true if the cell is editable.
7430 * @param {Number} colIndex The column index
7431 * @param {Number} rowIndex The row index - this is nto actually used..?
7434 isCellEditable : function(colIndex, rowIndex){
7435 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439 * Returns the editor defined for the cell/column.
7440 * return false or null to disable editing.
7441 * @param {Number} colIndex The column index
7442 * @param {Number} rowIndex The row index
7445 getCellEditor : function(colIndex, rowIndex){
7446 return this.config[colIndex].editor;
7450 * Sets if a column is editable.
7451 * @param {Number} col The column index
7452 * @param {Boolean} editable True if the column is editable
7454 setEditable : function(col, editable){
7455 this.config[col].editable = editable;
7460 * Returns true if the column is hidden.
7461 * @param {Number} colIndex The column index
7464 isHidden : function(colIndex){
7465 return this.config[colIndex].hidden;
7470 * Returns true if the column width cannot be changed
7472 isFixed : function(colIndex){
7473 return this.config[colIndex].fixed;
7477 * Returns true if the column can be resized
7480 isResizable : function(colIndex){
7481 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7484 * Sets if a column is hidden.
7485 * @param {Number} colIndex The column index
7486 * @param {Boolean} hidden True if the column is hidden
7488 setHidden : function(colIndex, hidden){
7489 this.config[colIndex].hidden = hidden;
7490 this.totalWidth = null;
7491 this.fireEvent("hiddenchange", this, colIndex, hidden);
7495 * Sets the editor for a column.
7496 * @param {Number} col The column index
7497 * @param {Object} editor The editor object
7499 setEditor : function(col, editor){
7500 this.config[col].editor = editor;
7504 Roo.grid.ColumnModel.defaultRenderer = function(value)
7506 if(typeof value == "object") {
7509 if(typeof value == "string" && value.length < 1){
7513 return String.format("{0}", value);
7516 // Alias for backwards compatibility
7517 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7520 * Ext JS Library 1.1.1
7521 * Copyright(c) 2006-2007, Ext JS, LLC.
7523 * Originally Released Under LGPL - original licence link has changed is not relivant.
7526 * <script type="text/javascript">
7530 * @class Roo.LoadMask
7531 * A simple utility class for generically masking elements while loading data. If the element being masked has
7532 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7533 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7534 * element's UpdateManager load indicator and will be destroyed after the initial load.
7536 * Create a new LoadMask
7537 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7538 * @param {Object} config The config object
7540 Roo.LoadMask = function(el, config){
7541 this.el = Roo.get(el);
7542 Roo.apply(this, config);
7544 this.store.on('beforeload', this.onBeforeLoad, this);
7545 this.store.on('load', this.onLoad, this);
7546 this.store.on('loadexception', this.onLoadException, this);
7547 this.removeMask = false;
7549 var um = this.el.getUpdateManager();
7550 um.showLoadIndicator = false; // disable the default indicator
7551 um.on('beforeupdate', this.onBeforeLoad, this);
7552 um.on('update', this.onLoad, this);
7553 um.on('failure', this.onLoad, this);
7554 this.removeMask = true;
7558 Roo.LoadMask.prototype = {
7560 * @cfg {Boolean} removeMask
7561 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7562 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7566 * The text to display in a centered loading message box (defaults to 'Loading...')
7570 * @cfg {String} msgCls
7571 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7573 msgCls : 'x-mask-loading',
7576 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7582 * Disables the mask to prevent it from being displayed
7584 disable : function(){
7585 this.disabled = true;
7589 * Enables the mask so that it can be displayed
7591 enable : function(){
7592 this.disabled = false;
7595 onLoadException : function()
7599 if (typeof(arguments[3]) != 'undefined') {
7600 Roo.MessageBox.alert("Error loading",arguments[3]);
7604 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7605 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621 onBeforeLoad : function(){
7623 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7628 destroy : function(){
7630 this.store.un('beforeload', this.onBeforeLoad, this);
7631 this.store.un('load', this.onLoad, this);
7632 this.store.un('loadexception', this.onLoadException, this);
7634 var um = this.el.getUpdateManager();
7635 um.un('beforeupdate', this.onBeforeLoad, this);
7636 um.un('update', this.onLoad, this);
7637 um.un('failure', this.onLoad, this);
7648 * @class Roo.bootstrap.Table
7649 * @extends Roo.bootstrap.Component
7650 * Bootstrap Table class
7651 * @cfg {String} cls table class
7652 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7653 * @cfg {String} bgcolor Specifies the background color for a table
7654 * @cfg {Number} border Specifies whether the table cells should have borders or not
7655 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7656 * @cfg {Number} cellspacing Specifies the space between cells
7657 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7658 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7659 * @cfg {String} sortable Specifies that the table should be sortable
7660 * @cfg {String} summary Specifies a summary of the content of a table
7661 * @cfg {Number} width Specifies the width of a table
7662 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7664 * @cfg {boolean} striped Should the rows be alternative striped
7665 * @cfg {boolean} bordered Add borders to the table
7666 * @cfg {boolean} hover Add hover highlighting
7667 * @cfg {boolean} condensed Format condensed
7668 * @cfg {boolean} responsive Format condensed
7669 * @cfg {Boolean} loadMask (true|false) default false
7670 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7671 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7672 * @cfg {Boolean} rowSelection (true|false) default false
7673 * @cfg {Boolean} cellSelection (true|false) default false
7674 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7675 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7676 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7677 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7681 * Create a new Table
7682 * @param {Object} config The config object
7685 Roo.bootstrap.Table = function(config){
7686 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7691 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7692 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7693 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7694 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7696 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7698 this.sm.grid = this;
7699 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7700 this.sm = this.selModel;
7701 this.sm.xmodule = this.xmodule || false;
7704 if (this.cm && typeof(this.cm.config) == 'undefined') {
7705 this.colModel = new Roo.grid.ColumnModel(this.cm);
7706 this.cm = this.colModel;
7707 this.cm.xmodule = this.xmodule || false;
7710 this.store= Roo.factory(this.store, Roo.data);
7711 this.ds = this.store;
7712 this.ds.xmodule = this.xmodule || false;
7715 if (this.footer && this.store) {
7716 this.footer.dataSource = this.ds;
7717 this.footer = Roo.factory(this.footer);
7724 * Fires when a cell is clicked
7725 * @param {Roo.bootstrap.Table} this
7726 * @param {Roo.Element} el
7727 * @param {Number} rowIndex
7728 * @param {Number} columnIndex
7729 * @param {Roo.EventObject} e
7733 * @event celldblclick
7734 * Fires when a cell is double clicked
7735 * @param {Roo.bootstrap.Table} this
7736 * @param {Roo.Element} el
7737 * @param {Number} rowIndex
7738 * @param {Number} columnIndex
7739 * @param {Roo.EventObject} e
7741 "celldblclick" : true,
7744 * Fires when a row is clicked
7745 * @param {Roo.bootstrap.Table} this
7746 * @param {Roo.Element} el
7747 * @param {Number} rowIndex
7748 * @param {Roo.EventObject} e
7752 * @event rowdblclick
7753 * Fires when a row is double clicked
7754 * @param {Roo.bootstrap.Table} this
7755 * @param {Roo.Element} el
7756 * @param {Number} rowIndex
7757 * @param {Roo.EventObject} e
7759 "rowdblclick" : true,
7762 * Fires when a mouseover occur
7763 * @param {Roo.bootstrap.Table} this
7764 * @param {Roo.Element} el
7765 * @param {Number} rowIndex
7766 * @param {Number} columnIndex
7767 * @param {Roo.EventObject} e
7772 * Fires when a mouseout occur
7773 * @param {Roo.bootstrap.Table} this
7774 * @param {Roo.Element} el
7775 * @param {Number} rowIndex
7776 * @param {Number} columnIndex
7777 * @param {Roo.EventObject} e
7782 * Fires when a row is rendered, so you can change add a style to it.
7783 * @param {Roo.bootstrap.Table} this
7784 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7788 * @event rowsrendered
7789 * Fires when all the rows have been rendered
7790 * @param {Roo.bootstrap.Table} this
7792 'rowsrendered' : true,
7794 * @event contextmenu
7795 * The raw contextmenu event for the entire grid.
7796 * @param {Roo.EventObject} e
7798 "contextmenu" : true,
7800 * @event rowcontextmenu
7801 * Fires when a row is right clicked
7802 * @param {Roo.bootstrap.Table} this
7803 * @param {Number} rowIndex
7804 * @param {Roo.EventObject} e
7806 "rowcontextmenu" : true,
7808 * @event cellcontextmenu
7809 * Fires when a cell is right clicked
7810 * @param {Roo.bootstrap.Table} this
7811 * @param {Number} rowIndex
7812 * @param {Number} cellIndex
7813 * @param {Roo.EventObject} e
7815 "cellcontextmenu" : true,
7817 * @event headercontextmenu
7818 * Fires when a header is right clicked
7819 * @param {Roo.bootstrap.Table} this
7820 * @param {Number} columnIndex
7821 * @param {Roo.EventObject} e
7823 "headercontextmenu" : true
7827 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7853 rowSelection : false,
7854 cellSelection : false,
7857 // Roo.Element - the tbody
7859 // Roo.Element - thead element
7862 container: false, // used by gridpanel...
7868 auto_hide_footer : false,
7870 getAutoCreate : function()
7872 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879 if (this.scrollBody) {
7880 cfg.cls += ' table-body-fixed';
7883 cfg.cls += ' table-striped';
7887 cfg.cls += ' table-hover';
7889 if (this.bordered) {
7890 cfg.cls += ' table-bordered';
7892 if (this.condensed) {
7893 cfg.cls += ' table-condensed';
7895 if (this.responsive) {
7896 cfg.cls += ' table-responsive';
7900 cfg.cls+= ' ' +this.cls;
7903 // this lot should be simplifed...
7916 ].forEach(function(k) {
7924 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7927 if(this.store || this.cm){
7928 if(this.headerShow){
7929 cfg.cn.push(this.renderHeader());
7932 cfg.cn.push(this.renderBody());
7934 if(this.footerShow){
7935 cfg.cn.push(this.renderFooter());
7937 // where does this come from?
7938 //cfg.cls+= ' TableGrid';
7941 return { cn : [ cfg ] };
7944 initEvents : function()
7946 if(!this.store || !this.cm){
7949 if (this.selModel) {
7950 this.selModel.initEvents();
7954 //Roo.log('initEvents with ds!!!!');
7956 this.mainBody = this.el.select('tbody', true).first();
7957 this.mainHead = this.el.select('thead', true).first();
7958 this.mainFoot = this.el.select('tfoot', true).first();
7964 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7965 e.on('click', _this.sort, _this);
7968 this.mainBody.on("click", this.onClick, this);
7969 this.mainBody.on("dblclick", this.onDblClick, this);
7971 // why is this done????? = it breaks dialogs??
7972 //this.parent().el.setStyle('position', 'relative');
7976 this.footer.parentId = this.id;
7977 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7980 this.el.select('tfoot tr td').first().addClass('hide');
7985 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7988 this.store.on('load', this.onLoad, this);
7989 this.store.on('beforeload', this.onBeforeLoad, this);
7990 this.store.on('update', this.onUpdate, this);
7991 this.store.on('add', this.onAdd, this);
7992 this.store.on("clear", this.clear, this);
7994 this.el.on("contextmenu", this.onContextMenu, this);
7996 this.mainBody.on('scroll', this.onBodyScroll, this);
7998 this.cm.on("headerchange", this.onHeaderChange, this);
8000 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004 onContextMenu : function(e, t)
8006 this.processEvent("contextmenu", e);
8009 processEvent : function(name, e)
8011 if (name != 'touchstart' ) {
8012 this.fireEvent(name, e);
8015 var t = e.getTarget();
8017 var cell = Roo.get(t);
8023 if(cell.findParent('tfoot', false, true)){
8027 if(cell.findParent('thead', false, true)){
8029 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8030 cell = Roo.get(t).findParent('th', false, true);
8032 Roo.log("failed to find th in thead?");
8033 Roo.log(e.getTarget());
8038 var cellIndex = cell.dom.cellIndex;
8040 var ename = name == 'touchstart' ? 'click' : name;
8041 this.fireEvent("header" + ename, this, cellIndex, e);
8046 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8047 cell = Roo.get(t).findParent('td', false, true);
8049 Roo.log("failed to find th in tbody?");
8050 Roo.log(e.getTarget());
8055 var row = cell.findParent('tr', false, true);
8056 var cellIndex = cell.dom.cellIndex;
8057 var rowIndex = row.dom.rowIndex - 1;
8061 this.fireEvent("row" + name, this, rowIndex, e);
8065 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8071 onMouseover : function(e, el)
8073 var cell = Roo.get(el);
8079 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8080 cell = cell.findParent('td', false, true);
8083 var row = cell.findParent('tr', false, true);
8084 var cellIndex = cell.dom.cellIndex;
8085 var rowIndex = row.dom.rowIndex - 1; // start from 0
8087 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091 onMouseout : function(e, el)
8093 var cell = Roo.get(el);
8099 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8100 cell = cell.findParent('td', false, true);
8103 var row = cell.findParent('tr', false, true);
8104 var cellIndex = cell.dom.cellIndex;
8105 var rowIndex = row.dom.rowIndex - 1; // start from 0
8107 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111 onClick : function(e, el)
8113 var cell = Roo.get(el);
8115 if(!cell || (!this.cellSelection && !this.rowSelection)){
8119 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120 cell = cell.findParent('td', false, true);
8123 if(!cell || typeof(cell) == 'undefined'){
8127 var row = cell.findParent('tr', false, true);
8129 if(!row || typeof(row) == 'undefined'){
8133 var cellIndex = cell.dom.cellIndex;
8134 var rowIndex = this.getRowIndex(row);
8136 // why??? - should these not be based on SelectionModel?
8137 if(this.cellSelection){
8138 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8141 if(this.rowSelection){
8142 this.fireEvent('rowclick', this, row, rowIndex, e);
8148 onDblClick : function(e,el)
8150 var cell = Roo.get(el);
8152 if(!cell || (!this.cellSelection && !this.rowSelection)){
8156 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8157 cell = cell.findParent('td', false, true);
8160 if(!cell || typeof(cell) == 'undefined'){
8164 var row = cell.findParent('tr', false, true);
8166 if(!row || typeof(row) == 'undefined'){
8170 var cellIndex = cell.dom.cellIndex;
8171 var rowIndex = this.getRowIndex(row);
8173 if(this.cellSelection){
8174 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8177 if(this.rowSelection){
8178 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182 sort : function(e,el)
8184 var col = Roo.get(el);
8186 if(!col.hasClass('sortable')){
8190 var sort = col.attr('sort');
8193 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197 this.store.sortInfo = {field : sort, direction : dir};
8200 Roo.log("calling footer first");
8201 this.footer.onClick('first');
8204 this.store.load({ params : { start : 0 } });
8208 renderHeader : function()
8216 this.totalWidth = 0;
8218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8220 var config = cm.config[i];
8224 cls : 'x-hcol-' + i,
8226 html: cm.getColumnHeader(i)
8231 if(typeof(config.sortable) != 'undefined' && config.sortable){
8233 c.html = '<i class="glyphicon"></i>' + c.html;
8236 // could use BS4 hidden-..-down
8238 if(typeof(config.lgHeader) != 'undefined'){
8239 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8242 if(typeof(config.mdHeader) != 'undefined'){
8243 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8246 if(typeof(config.smHeader) != 'undefined'){
8247 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8250 if(typeof(config.xsHeader) != 'undefined'){
8251 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258 if(typeof(config.tooltip) != 'undefined'){
8259 c.tooltip = config.tooltip;
8262 if(typeof(config.colspan) != 'undefined'){
8263 c.colspan = config.colspan;
8266 if(typeof(config.hidden) != 'undefined' && config.hidden){
8267 c.style += ' display:none;';
8270 if(typeof(config.dataIndex) != 'undefined'){
8271 c.sort = config.dataIndex;
8276 if(typeof(config.align) != 'undefined' && config.align.length){
8277 c.style += ' text-align:' + config.align + ';';
8280 if(typeof(config.width) != 'undefined'){
8281 c.style += ' width:' + config.width + 'px;';
8282 this.totalWidth += config.width;
8284 this.totalWidth += 100; // assume minimum of 100 per column?
8287 if(typeof(config.cls) != 'undefined'){
8288 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8291 ['xs','sm','md','lg'].map(function(size){
8293 if(typeof(config[size]) == 'undefined'){
8297 if (!config[size]) { // 0 = hidden
8298 // BS 4 '0' is treated as hide that column and below.
8299 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303 c.cls += ' col-' + size + '-' + config[size] + (
8304 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8316 renderBody : function()
8326 colspan : this.cm.getColumnCount()
8336 renderFooter : function()
8346 colspan : this.cm.getColumnCount()
8360 // Roo.log('ds onload');
8365 var ds = this.store;
8367 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8368 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8369 if (_this.store.sortInfo) {
8371 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8372 e.select('i', true).addClass(['glyphicon-arrow-up']);
8375 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8376 e.select('i', true).addClass(['glyphicon-arrow-down']);
8381 var tbody = this.mainBody;
8383 if(ds.getCount() > 0){
8384 ds.data.each(function(d,rowIndex){
8385 var row = this.renderRow(cm, ds, rowIndex);
8387 tbody.createChild(row);
8391 if(row.cellObjects.length){
8392 Roo.each(row.cellObjects, function(r){
8393 _this.renderCellObject(r);
8400 var tfoot = this.el.select('tfoot', true).first();
8402 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8404 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8406 var total = this.ds.getTotalCount();
8408 if(this.footer.pageSize < total){
8409 this.mainFoot.show();
8413 Roo.each(this.el.select('tbody td', true).elements, function(e){
8414 e.on('mouseover', _this.onMouseover, _this);
8417 Roo.each(this.el.select('tbody td', true).elements, function(e){
8418 e.on('mouseout', _this.onMouseout, _this);
8420 this.fireEvent('rowsrendered', this);
8426 onUpdate : function(ds,record)
8428 this.refreshRow(record);
8432 onRemove : function(ds, record, index, isUpdate){
8433 if(isUpdate !== true){
8434 this.fireEvent("beforerowremoved", this, index, record);
8436 var bt = this.mainBody.dom;
8438 var rows = this.el.select('tbody > tr', true).elements;
8440 if(typeof(rows[index]) != 'undefined'){
8441 bt.removeChild(rows[index].dom);
8444 // if(bt.rows[index]){
8445 // bt.removeChild(bt.rows[index]);
8448 if(isUpdate !== true){
8449 //this.stripeRows(index);
8450 //this.syncRowHeights(index, index);
8452 this.fireEvent("rowremoved", this, index, record);
8456 onAdd : function(ds, records, rowIndex)
8458 //Roo.log('on Add called');
8459 // - note this does not handle multiple adding very well..
8460 var bt = this.mainBody.dom;
8461 for (var i =0 ; i < records.length;i++) {
8462 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8463 //Roo.log(records[i]);
8464 //Roo.log(this.store.getAt(rowIndex+i));
8465 this.insertRow(this.store, rowIndex + i, false);
8472 refreshRow : function(record){
8473 var ds = this.store, index;
8474 if(typeof record == 'number'){
8476 record = ds.getAt(index);
8478 index = ds.indexOf(record);
8480 return; // should not happen - but seems to
8483 this.insertRow(ds, index, true);
8485 this.onRemove(ds, record, index+1, true);
8487 //this.syncRowHeights(index, index);
8489 this.fireEvent("rowupdated", this, index, record);
8492 insertRow : function(dm, rowIndex, isUpdate){
8495 this.fireEvent("beforerowsinserted", this, rowIndex);
8497 //var s = this.getScrollState();
8498 var row = this.renderRow(this.cm, this.store, rowIndex);
8499 // insert before rowIndex..
8500 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504 if(row.cellObjects.length){
8505 Roo.each(row.cellObjects, function(r){
8506 _this.renderCellObject(r);
8511 this.fireEvent("rowsinserted", this, rowIndex);
8512 //this.syncRowHeights(firstRow, lastRow);
8513 //this.stripeRows(firstRow);
8520 getRowDom : function(rowIndex)
8522 var rows = this.el.select('tbody > tr', true).elements;
8524 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8527 // returns the object tree for a tr..
8530 renderRow : function(cm, ds, rowIndex)
8532 var d = ds.getAt(rowIndex);
8536 cls : 'x-row-' + rowIndex,
8540 var cellObjects = [];
8542 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8543 var config = cm.config[i];
8545 var renderer = cm.getRenderer(i);
8549 if(typeof(renderer) !== 'undefined'){
8550 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8552 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8553 // and are rendered into the cells after the row is rendered - using the id for the element.
8555 if(typeof(value) === 'object'){
8565 rowIndex : rowIndex,
8570 this.fireEvent('rowclass', this, rowcfg);
8574 cls : rowcfg.rowClass + ' x-col-' + i,
8576 html: (typeof(value) === 'object') ? '' : value
8583 if(typeof(config.colspan) != 'undefined'){
8584 td.colspan = config.colspan;
8587 if(typeof(config.hidden) != 'undefined' && config.hidden){
8588 td.style += ' display:none;';
8591 if(typeof(config.align) != 'undefined' && config.align.length){
8592 td.style += ' text-align:' + config.align + ';';
8594 if(typeof(config.valign) != 'undefined' && config.valign.length){
8595 td.style += ' vertical-align:' + config.valign + ';';
8598 if(typeof(config.width) != 'undefined'){
8599 td.style += ' width:' + config.width + 'px;';
8602 if(typeof(config.cursor) != 'undefined'){
8603 td.style += ' cursor:' + config.cursor + ';';
8606 if(typeof(config.cls) != 'undefined'){
8607 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8610 ['xs','sm','md','lg'].map(function(size){
8612 if(typeof(config[size]) == 'undefined'){
8618 if (!config[size]) { // 0 = hidden
8619 // BS 4 '0' is treated as hide that column and below.
8620 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624 td.cls += ' col-' + size + '-' + config[size] + (
8625 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8635 row.cellObjects = cellObjects;
8643 onBeforeLoad : function()
8652 this.el.select('tbody', true).first().dom.innerHTML = '';
8655 * Show or hide a row.
8656 * @param {Number} rowIndex to show or hide
8657 * @param {Boolean} state hide
8659 setRowVisibility : function(rowIndex, state)
8661 var bt = this.mainBody.dom;
8663 var rows = this.el.select('tbody > tr', true).elements;
8665 if(typeof(rows[rowIndex]) == 'undefined'){
8668 rows[rowIndex].dom.style.display = state ? '' : 'none';
8672 getSelectionModel : function(){
8674 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8676 return this.selModel;
8679 * Render the Roo.bootstrap object from renderder
8681 renderCellObject : function(r)
8685 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8687 var t = r.cfg.render(r.container);
8690 Roo.each(r.cfg.cn, function(c){
8692 container: t.getChildContainer(),
8695 _this.renderCellObject(child);
8700 getRowIndex : function(row)
8704 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8715 * Returns the grid's underlying element = used by panel.Grid
8716 * @return {Element} The element
8718 getGridEl : function(){
8722 * Forces a resize - used by panel.Grid
8723 * @return {Element} The element
8725 autoSize : function()
8727 //var ctr = Roo.get(this.container.dom.parentElement);
8728 var ctr = Roo.get(this.el.dom);
8730 var thd = this.getGridEl().select('thead',true).first();
8731 var tbd = this.getGridEl().select('tbody', true).first();
8732 var tfd = this.getGridEl().select('tfoot', true).first();
8734 var cw = ctr.getWidth();
8735 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8739 tbd.setWidth(ctr.getWidth());
8740 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8741 // this needs fixing for various usage - currently only hydra job advers I think..
8743 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8745 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8748 cw = Math.max(cw, this.totalWidth);
8749 this.getGridEl().select('tbody tr',true).setWidth(cw);
8751 // resize 'expandable coloumn?
8753 return; // we doe not have a view in this design..
8756 onBodyScroll: function()
8758 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8760 this.mainHead.setStyle({
8761 'position' : 'relative',
8762 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8768 var scrollHeight = this.mainBody.dom.scrollHeight;
8770 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8772 var height = this.mainBody.getHeight();
8774 if(scrollHeight - height == scrollTop) {
8776 var total = this.ds.getTotalCount();
8778 if(this.footer.cursor + this.footer.pageSize < total){
8780 this.footer.ds.load({
8782 start : this.footer.cursor + this.footer.pageSize,
8783 limit : this.footer.pageSize
8793 onHeaderChange : function()
8795 var header = this.renderHeader();
8796 var table = this.el.select('table', true).first();
8798 this.mainHead.remove();
8799 this.mainHead = table.createChild(header, this.mainBody, false);
8802 onHiddenChange : function(colModel, colIndex, hidden)
8804 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8805 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8807 this.CSS.updateRule(thSelector, "display", "");
8808 this.CSS.updateRule(tdSelector, "display", "");
8811 this.CSS.updateRule(thSelector, "display", "none");
8812 this.CSS.updateRule(tdSelector, "display", "none");
8815 this.onHeaderChange();
8819 setColumnWidth: function(col_index, width)
8821 // width = "md-2 xs-2..."
8822 if(!this.colModel.config[col_index]) {
8826 var w = width.split(" ");
8828 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8830 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8833 for(var j = 0; j < w.length; j++) {
8839 var size_cls = w[j].split("-");
8841 if(!Number.isInteger(size_cls[1] * 1)) {
8845 if(!this.colModel.config[col_index][size_cls[0]]) {
8849 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853 h_row[0].classList.replace(
8854 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8855 "col-"+size_cls[0]+"-"+size_cls[1]
8858 for(var i = 0; i < rows.length; i++) {
8860 var size_cls = w[j].split("-");
8862 if(!Number.isInteger(size_cls[1] * 1)) {
8866 if(!this.colModel.config[col_index][size_cls[0]]) {
8870 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874 rows[i].classList.replace(
8875 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8876 "col-"+size_cls[0]+"-"+size_cls[1]
8880 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8895 * @class Roo.bootstrap.TableCell
8896 * @extends Roo.bootstrap.Component
8897 * Bootstrap TableCell class
8898 * @cfg {String} html cell contain text
8899 * @cfg {String} cls cell class
8900 * @cfg {String} tag cell tag (td|th) default td
8901 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8902 * @cfg {String} align Aligns the content in a cell
8903 * @cfg {String} axis Categorizes cells
8904 * @cfg {String} bgcolor Specifies the background color of a cell
8905 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8906 * @cfg {Number} colspan Specifies the number of columns a cell should span
8907 * @cfg {String} headers Specifies one or more header cells a cell is related to
8908 * @cfg {Number} height Sets the height of a cell
8909 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8910 * @cfg {Number} rowspan Sets the number of rows a cell should span
8911 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8912 * @cfg {String} valign Vertical aligns the content in a cell
8913 * @cfg {Number} width Specifies the width of a cell
8916 * Create a new TableCell
8917 * @param {Object} config The config object
8920 Roo.bootstrap.TableCell = function(config){
8921 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8924 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8944 getAutoCreate : function(){
8945 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8965 cfg.align=this.align
8971 cfg.bgcolor=this.bgcolor
8974 cfg.charoff=this.charoff
8977 cfg.colspan=this.colspan
8980 cfg.headers=this.headers
8983 cfg.height=this.height
8986 cfg.nowrap=this.nowrap
8989 cfg.rowspan=this.rowspan
8992 cfg.scope=this.scope
8995 cfg.valign=this.valign
8998 cfg.width=this.width
9017 * @class Roo.bootstrap.TableRow
9018 * @extends Roo.bootstrap.Component
9019 * Bootstrap TableRow class
9020 * @cfg {String} cls row class
9021 * @cfg {String} align Aligns the content in a table row
9022 * @cfg {String} bgcolor Specifies a background color for a table row
9023 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9024 * @cfg {String} valign Vertical aligns the content in a table row
9027 * Create a new TableRow
9028 * @param {Object} config The config object
9031 Roo.bootstrap.TableRow = function(config){
9032 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9035 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9043 getAutoCreate : function(){
9044 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9054 cfg.align = this.align;
9057 cfg.bgcolor = this.bgcolor;
9060 cfg.charoff = this.charoff;
9063 cfg.valign = this.valign;
9081 * @class Roo.bootstrap.TableBody
9082 * @extends Roo.bootstrap.Component
9083 * Bootstrap TableBody class
9084 * @cfg {String} cls element class
9085 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9086 * @cfg {String} align Aligns the content inside the element
9087 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9088 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9091 * Create a new TableBody
9092 * @param {Object} config The config object
9095 Roo.bootstrap.TableBody = function(config){
9096 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9099 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9107 getAutoCreate : function(){
9108 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9122 cfg.align = this.align;
9125 cfg.charoff = this.charoff;
9128 cfg.valign = this.valign;
9135 // initEvents : function()
9142 // this.store = Roo.factory(this.store, Roo.data);
9143 // this.store.on('load', this.onLoad, this);
9145 // this.store.load();
9149 // onLoad: function ()
9151 // this.fireEvent('load', this);
9161 * Ext JS Library 1.1.1
9162 * Copyright(c) 2006-2007, Ext JS, LLC.
9164 * Originally Released Under LGPL - original licence link has changed is not relivant.
9167 * <script type="text/javascript">
9170 // as we use this in bootstrap.
9171 Roo.namespace('Roo.form');
9173 * @class Roo.form.Action
9174 * Internal Class used to handle form actions
9176 * @param {Roo.form.BasicForm} el The form element or its id
9177 * @param {Object} config Configuration options
9182 // define the action interface
9183 Roo.form.Action = function(form, options){
9185 this.options = options || {};
9188 * Client Validation Failed
9191 Roo.form.Action.CLIENT_INVALID = 'client';
9193 * Server Validation Failed
9196 Roo.form.Action.SERVER_INVALID = 'server';
9198 * Connect to Server Failed
9201 Roo.form.Action.CONNECT_FAILURE = 'connect';
9203 * Reading Data from Server Failed
9206 Roo.form.Action.LOAD_FAILURE = 'load';
9208 Roo.form.Action.prototype = {
9210 failureType : undefined,
9211 response : undefined,
9215 run : function(options){
9220 success : function(response){
9225 handleResponse : function(response){
9229 // default connection failure
9230 failure : function(response){
9232 this.response = response;
9233 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9234 this.form.afterAction(this, false);
9237 processResponse : function(response){
9238 this.response = response;
9239 if(!response.responseText){
9242 this.result = this.handleResponse(response);
9246 // utility functions used internally
9247 getUrl : function(appendParams){
9248 var url = this.options.url || this.form.url || this.form.el.dom.action;
9250 var p = this.getParams();
9252 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9258 getMethod : function(){
9259 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9262 getParams : function(){
9263 var bp = this.form.baseParams;
9264 var p = this.options.params;
9266 if(typeof p == "object"){
9267 p = Roo.urlEncode(Roo.applyIf(p, bp));
9268 }else if(typeof p == 'string' && bp){
9269 p += '&' + Roo.urlEncode(bp);
9272 p = Roo.urlEncode(bp);
9277 createCallback : function(){
9279 success: this.success,
9280 failure: this.failure,
9282 timeout: (this.form.timeout*1000),
9283 upload: this.form.fileUpload ? this.success : undefined
9288 Roo.form.Action.Submit = function(form, options){
9289 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9292 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9295 haveProgress : false,
9296 uploadComplete : false,
9298 // uploadProgress indicator.
9299 uploadProgress : function()
9301 if (!this.form.progressUrl) {
9305 if (!this.haveProgress) {
9306 Roo.MessageBox.progress("Uploading", "Uploading");
9308 if (this.uploadComplete) {
9309 Roo.MessageBox.hide();
9313 this.haveProgress = true;
9315 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9317 var c = new Roo.data.Connection();
9319 url : this.form.progressUrl,
9324 success : function(req){
9325 //console.log(data);
9329 rdata = Roo.decode(req.responseText)
9331 Roo.log("Invalid data from server..");
9335 if (!rdata || !rdata.success) {
9337 Roo.MessageBox.alert(Roo.encode(rdata));
9340 var data = rdata.data;
9342 if (this.uploadComplete) {
9343 Roo.MessageBox.hide();
9348 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9349 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9352 this.uploadProgress.defer(2000,this);
9355 failure: function(data) {
9356 Roo.log('progress url failed ');
9367 // run get Values on the form, so it syncs any secondary forms.
9368 this.form.getValues();
9370 var o = this.options;
9371 var method = this.getMethod();
9372 var isPost = method == 'POST';
9373 if(o.clientValidation === false || this.form.isValid()){
9375 if (this.form.progressUrl) {
9376 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9377 (new Date() * 1) + '' + Math.random());
9382 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9383 form:this.form.el.dom,
9384 url:this.getUrl(!isPost),
9386 params:isPost ? this.getParams() : null,
9387 isUpload: this.form.fileUpload,
9388 formData : this.form.formData
9391 this.uploadProgress();
9393 }else if (o.clientValidation !== false){ // client validation failed
9394 this.failureType = Roo.form.Action.CLIENT_INVALID;
9395 this.form.afterAction(this, false);
9399 success : function(response)
9401 this.uploadComplete= true;
9402 if (this.haveProgress) {
9403 Roo.MessageBox.hide();
9407 var result = this.processResponse(response);
9408 if(result === true || result.success){
9409 this.form.afterAction(this, true);
9413 this.form.markInvalid(result.errors);
9414 this.failureType = Roo.form.Action.SERVER_INVALID;
9416 this.form.afterAction(this, false);
9418 failure : function(response)
9420 this.uploadComplete= true;
9421 if (this.haveProgress) {
9422 Roo.MessageBox.hide();
9425 this.response = response;
9426 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9427 this.form.afterAction(this, false);
9430 handleResponse : function(response){
9431 if(this.form.errorReader){
9432 var rs = this.form.errorReader.read(response);
9435 for(var i = 0, len = rs.records.length; i < len; i++) {
9436 var r = rs.records[i];
9440 if(errors.length < 1){
9444 success : rs.success,
9450 ret = Roo.decode(response.responseText);
9454 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9464 Roo.form.Action.Load = function(form, options){
9465 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9466 this.reader = this.form.reader;
9469 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9474 Roo.Ajax.request(Roo.apply(
9475 this.createCallback(), {
9476 method:this.getMethod(),
9477 url:this.getUrl(false),
9478 params:this.getParams()
9482 success : function(response){
9484 var result = this.processResponse(response);
9485 if(result === true || !result.success || !result.data){
9486 this.failureType = Roo.form.Action.LOAD_FAILURE;
9487 this.form.afterAction(this, false);
9490 this.form.clearInvalid();
9491 this.form.setValues(result.data);
9492 this.form.afterAction(this, true);
9495 handleResponse : function(response){
9496 if(this.form.reader){
9497 var rs = this.form.reader.read(response);
9498 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9500 success : rs.success,
9504 return Roo.decode(response.responseText);
9508 Roo.form.Action.ACTION_TYPES = {
9509 'load' : Roo.form.Action.Load,
9510 'submit' : Roo.form.Action.Submit
9519 * @class Roo.bootstrap.Form
9520 * @extends Roo.bootstrap.Component
9521 * Bootstrap Form class
9522 * @cfg {String} method GET | POST (default POST)
9523 * @cfg {String} labelAlign top | left (default top)
9524 * @cfg {String} align left | right - for navbars
9525 * @cfg {Boolean} loadMask load mask when submit (default true)
9530 * @param {Object} config The config object
9534 Roo.bootstrap.Form = function(config){
9536 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9538 Roo.bootstrap.Form.popover.apply();
9542 * @event clientvalidation
9543 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9544 * @param {Form} this
9545 * @param {Boolean} valid true if the form has passed client-side validation
9547 clientvalidation: true,
9549 * @event beforeaction
9550 * Fires before any action is performed. Return false to cancel the action.
9551 * @param {Form} this
9552 * @param {Action} action The action to be performed
9556 * @event actionfailed
9557 * Fires when an action fails.
9558 * @param {Form} this
9559 * @param {Action} action The action that failed
9561 actionfailed : true,
9563 * @event actioncomplete
9564 * Fires when an action is completed.
9565 * @param {Form} this
9566 * @param {Action} action The action that completed
9568 actioncomplete : true
9572 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9575 * @cfg {String} method
9576 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9581 * The URL to use for form actions if one isn't supplied in the action options.
9584 * @cfg {Boolean} fileUpload
9585 * Set to true if this form is a file upload.
9589 * @cfg {Object} baseParams
9590 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598 * @cfg {Sting} align (left|right) for navbar forms
9603 activeAction : null,
9606 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9607 * element by passing it or its id or mask the form itself by passing in true.
9610 waitMsgTarget : false,
9615 * @cfg {Boolean} errorMask (true|false) default false
9620 * @cfg {Number} maskOffset Default 100
9625 * @cfg {Boolean} maskBody
9629 getAutoCreate : function(){
9633 method : this.method || 'POST',
9634 id : this.id || Roo.id(),
9637 if (this.parent().xtype.match(/^Nav/)) {
9638 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642 if (this.labelAlign == 'left' ) {
9643 cfg.cls += ' form-horizontal';
9649 initEvents : function()
9651 this.el.on('submit', this.onSubmit, this);
9652 // this was added as random key presses on the form where triggering form submit.
9653 this.el.on('keypress', function(e) {
9654 if (e.getCharCode() != 13) {
9657 // we might need to allow it for textareas.. and some other items.
9658 // check e.getTarget().
9660 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664 Roo.log("keypress blocked");
9672 onSubmit : function(e){
9677 * Returns true if client-side validation on the form is successful.
9680 isValid : function(){
9681 var items = this.getItems();
9685 items.each(function(f){
9691 Roo.log('invalid field: ' + f.name);
9695 if(!target && f.el.isVisible(true)){
9701 if(this.errorMask && !valid){
9702 Roo.bootstrap.Form.popover.mask(this, target);
9709 * Returns true if any fields in this form have changed since their original load.
9712 isDirty : function(){
9714 var items = this.getItems();
9715 items.each(function(f){
9725 * Performs a predefined action (submit or load) or custom actions you define on this form.
9726 * @param {String} actionName The name of the action type
9727 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9728 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9729 * accept other config options):
9731 Property Type Description
9732 ---------------- --------------- ----------------------------------------------------------------------------------
9733 url String The url for the action (defaults to the form's url)
9734 method String The form method to use (defaults to the form's method, or POST if not defined)
9735 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9736 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9737 validate the form on the client (defaults to false)
9739 * @return {BasicForm} this
9741 doAction : function(action, options){
9742 if(typeof action == 'string'){
9743 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9745 if(this.fireEvent('beforeaction', this, action) !== false){
9746 this.beforeAction(action);
9747 action.run.defer(100, action);
9753 beforeAction : function(action){
9754 var o = action.options;
9759 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9761 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9764 // not really supported yet.. ??
9766 //if(this.waitMsgTarget === true){
9767 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768 //}else if(this.waitMsgTarget){
9769 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9770 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9772 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9778 afterAction : function(action, success){
9779 this.activeAction = null;
9780 var o = action.options;
9785 Roo.get(document.body).unmask();
9791 //if(this.waitMsgTarget === true){
9792 // this.el.unmask();
9793 //}else if(this.waitMsgTarget){
9794 // this.waitMsgTarget.unmask();
9796 // Roo.MessageBox.updateProgress(1);
9797 // Roo.MessageBox.hide();
9804 Roo.callback(o.success, o.scope, [this, action]);
9805 this.fireEvent('actioncomplete', this, action);
9809 // failure condition..
9810 // we have a scenario where updates need confirming.
9811 // eg. if a locking scenario exists..
9812 // we look for { errors : { needs_confirm : true }} in the response.
9814 (typeof(action.result) != 'undefined') &&
9815 (typeof(action.result.errors) != 'undefined') &&
9816 (typeof(action.result.errors.needs_confirm) != 'undefined')
9819 Roo.log("not supported yet");
9822 Roo.MessageBox.confirm(
9823 "Change requires confirmation",
9824 action.result.errorMsg,
9829 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9839 Roo.callback(o.failure, o.scope, [this, action]);
9840 // show an error message if no failed handler is set..
9841 if (!this.hasListener('actionfailed')) {
9842 Roo.log("need to add dialog support");
9844 Roo.MessageBox.alert("Error",
9845 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9846 action.result.errorMsg :
9847 "Saving Failed, please check your entries or try again"
9852 this.fireEvent('actionfailed', this, action);
9857 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9858 * @param {String} id The value to search for
9861 findField : function(id){
9862 var items = this.getItems();
9863 var field = items.get(id);
9865 items.each(function(f){
9866 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873 return field || null;
9876 * Mark fields in this form invalid in bulk.
9877 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9878 * @return {BasicForm} this
9880 markInvalid : function(errors){
9881 if(errors instanceof Array){
9882 for(var i = 0, len = errors.length; i < len; i++){
9883 var fieldError = errors[i];
9884 var f = this.findField(fieldError.id);
9886 f.markInvalid(fieldError.msg);
9892 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9893 field.markInvalid(errors[id]);
9897 //Roo.each(this.childForms || [], function (f) {
9898 // f.markInvalid(errors);
9905 * Set values for fields in this form in bulk.
9906 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9907 * @return {BasicForm} this
9909 setValues : function(values){
9910 if(values instanceof Array){ // array of objects
9911 for(var i = 0, len = values.length; i < len; i++){
9913 var f = this.findField(v.id);
9915 f.setValue(v.value);
9916 if(this.trackResetOnLoad){
9917 f.originalValue = f.getValue();
9921 }else{ // object hash
9924 if(typeof values[id] != 'function' && (field = this.findField(id))){
9926 if (field.setFromData &&
9928 field.displayField &&
9929 // combos' with local stores can
9930 // be queried via setValue()
9931 // to set their value..
9932 (field.store && !field.store.isLocal)
9936 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9937 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9938 field.setFromData(sd);
9940 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9942 field.setFromData(values);
9945 field.setValue(values[id]);
9949 if(this.trackResetOnLoad){
9950 field.originalValue = field.getValue();
9956 //Roo.each(this.childForms || [], function (f) {
9957 // f.setValues(values);
9964 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9965 * they are returned as an array.
9966 * @param {Boolean} asString
9969 getValues : function(asString){
9970 //if (this.childForms) {
9971 // copy values from the child forms
9972 // Roo.each(this.childForms, function (f) {
9973 // this.setValues(f.getValues());
9979 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9980 if(asString === true){
9983 return Roo.urlDecode(fs);
9987 * Returns the fields in this form as an object with key/value pairs.
9988 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9991 getFieldValues : function(with_hidden)
9993 var items = this.getItems();
9995 items.each(function(f){
10001 var v = f.getValue();
10003 if (f.inputType =='radio') {
10004 if (typeof(ret[f.getName()]) == 'undefined') {
10005 ret[f.getName()] = ''; // empty..
10008 if (!f.el.dom.checked) {
10012 v = f.el.dom.value;
10016 if(f.xtype == 'MoneyField'){
10017 ret[f.currencyName] = f.getCurrency();
10020 // not sure if this supported any more..
10021 if ((typeof(v) == 'object') && f.getRawValue) {
10022 v = f.getRawValue() ; // dates..
10024 // combo boxes where name != hiddenName...
10025 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10026 ret[f.name] = f.getRawValue();
10028 ret[f.getName()] = v;
10035 * Clears all invalid messages in this form.
10036 * @return {BasicForm} this
10038 clearInvalid : function(){
10039 var items = this.getItems();
10041 items.each(function(f){
10049 * Resets this form.
10050 * @return {BasicForm} this
10052 reset : function(){
10053 var items = this.getItems();
10054 items.each(function(f){
10058 Roo.each(this.childForms || [], function (f) {
10066 getItems : function()
10068 var r=new Roo.util.MixedCollection(false, function(o){
10069 return o.id || (o.id = Roo.id());
10071 var iter = function(el) {
10078 Roo.each(el.items,function(e) {
10087 hideFields : function(items)
10089 Roo.each(items, function(i){
10091 var f = this.findField(i);
10102 showFields : function(items)
10104 Roo.each(items, function(i){
10106 var f = this.findField(i);
10119 Roo.apply(Roo.bootstrap.Form, {
10135 intervalID : false,
10141 if(this.isApplied){
10146 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10147 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10148 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10149 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10152 this.maskEl.top.enableDisplayMode("block");
10153 this.maskEl.left.enableDisplayMode("block");
10154 this.maskEl.bottom.enableDisplayMode("block");
10155 this.maskEl.right.enableDisplayMode("block");
10157 this.toolTip = new Roo.bootstrap.Tooltip({
10158 cls : 'roo-form-error-popover',
10160 'left' : ['r-l', [-2,0], 'right'],
10161 'right' : ['l-r', [2,0], 'left'],
10162 'bottom' : ['tl-bl', [0,2], 'top'],
10163 'top' : [ 'bl-tl', [0,-2], 'bottom']
10167 this.toolTip.render(Roo.get(document.body));
10169 this.toolTip.el.enableDisplayMode("block");
10171 Roo.get(document.body).on('click', function(){
10175 Roo.get(document.body).on('touchstart', function(){
10179 this.isApplied = true
10182 mask : function(form, target)
10186 this.target = target;
10188 if(!this.form.errorMask || !target.el){
10192 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10194 Roo.log(scrollable);
10196 var ot = this.target.el.calcOffsetsTo(scrollable);
10198 var scrollTo = ot[1] - this.form.maskOffset;
10200 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10202 scrollable.scrollTo('top', scrollTo);
10204 var box = this.target.el.getBox();
10206 var zIndex = Roo.bootstrap.Modal.zIndex++;
10209 this.maskEl.top.setStyle('position', 'absolute');
10210 this.maskEl.top.setStyle('z-index', zIndex);
10211 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10212 this.maskEl.top.setLeft(0);
10213 this.maskEl.top.setTop(0);
10214 this.maskEl.top.show();
10216 this.maskEl.left.setStyle('position', 'absolute');
10217 this.maskEl.left.setStyle('z-index', zIndex);
10218 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10219 this.maskEl.left.setLeft(0);
10220 this.maskEl.left.setTop(box.y - this.padding);
10221 this.maskEl.left.show();
10223 this.maskEl.bottom.setStyle('position', 'absolute');
10224 this.maskEl.bottom.setStyle('z-index', zIndex);
10225 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10226 this.maskEl.bottom.setLeft(0);
10227 this.maskEl.bottom.setTop(box.bottom + this.padding);
10228 this.maskEl.bottom.show();
10230 this.maskEl.right.setStyle('position', 'absolute');
10231 this.maskEl.right.setStyle('z-index', zIndex);
10232 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10233 this.maskEl.right.setLeft(box.right + this.padding);
10234 this.maskEl.right.setTop(box.y - this.padding);
10235 this.maskEl.right.show();
10237 this.toolTip.bindEl = this.target.el;
10239 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10241 var tip = this.target.blankText;
10243 if(this.target.getValue() !== '' ) {
10245 if (this.target.invalidText.length) {
10246 tip = this.target.invalidText;
10247 } else if (this.target.regexText.length){
10248 tip = this.target.regexText;
10252 this.toolTip.show(tip);
10254 this.intervalID = window.setInterval(function() {
10255 Roo.bootstrap.Form.popover.unmask();
10258 window.onwheel = function(){ return false;};
10260 (function(){ this.isMasked = true; }).defer(500, this);
10264 unmask : function()
10266 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270 this.maskEl.top.setStyle('position', 'absolute');
10271 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10272 this.maskEl.top.hide();
10274 this.maskEl.left.setStyle('position', 'absolute');
10275 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10276 this.maskEl.left.hide();
10278 this.maskEl.bottom.setStyle('position', 'absolute');
10279 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10280 this.maskEl.bottom.hide();
10282 this.maskEl.right.setStyle('position', 'absolute');
10283 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10284 this.maskEl.right.hide();
10286 this.toolTip.hide();
10288 this.toolTip.el.hide();
10290 window.onwheel = function(){ return true;};
10292 if(this.intervalID){
10293 window.clearInterval(this.intervalID);
10294 this.intervalID = false;
10297 this.isMasked = false;
10307 * Ext JS Library 1.1.1
10308 * Copyright(c) 2006-2007, Ext JS, LLC.
10310 * Originally Released Under LGPL - original licence link has changed is not relivant.
10313 * <script type="text/javascript">
10316 * @class Roo.form.VTypes
10317 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10320 Roo.form.VTypes = function(){
10321 // closure these in so they are only created once.
10322 var alpha = /^[a-zA-Z_]+$/;
10323 var alphanum = /^[a-zA-Z0-9_]+$/;
10324 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10325 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10327 // All these messages and functions are configurable
10330 * The function used to validate email addresses
10331 * @param {String} value The email address
10333 'email' : function(v){
10334 return email.test(v);
10337 * The error text to display when the email validation function returns false
10340 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10342 * The keystroke filter mask to be applied on email input
10345 'emailMask' : /[a-z0-9_\.\-@]/i,
10348 * The function used to validate URLs
10349 * @param {String} value The URL
10351 'url' : function(v){
10352 return url.test(v);
10355 * The error text to display when the url validation function returns false
10358 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10361 * The function used to validate alpha values
10362 * @param {String} value The value
10364 'alpha' : function(v){
10365 return alpha.test(v);
10368 * The error text to display when the alpha validation function returns false
10371 'alphaText' : 'This field should only contain letters and _',
10373 * The keystroke filter mask to be applied on alpha input
10376 'alphaMask' : /[a-z_]/i,
10379 * The function used to validate alphanumeric values
10380 * @param {String} value The value
10382 'alphanum' : function(v){
10383 return alphanum.test(v);
10386 * The error text to display when the alphanumeric validation function returns false
10389 'alphanumText' : 'This field should only contain letters, numbers and _',
10391 * The keystroke filter mask to be applied on alphanumeric input
10394 'alphanumMask' : /[a-z0-9_]/i
10404 * @class Roo.bootstrap.Input
10405 * @extends Roo.bootstrap.Component
10406 * Bootstrap Input class
10407 * @cfg {Boolean} disabled is it disabled
10408 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10409 * @cfg {String} name name of the input
10410 * @cfg {string} fieldLabel - the label associated
10411 * @cfg {string} placeholder - placeholder to put in text.
10412 * @cfg {string} before - input group add on before
10413 * @cfg {string} after - input group add on after
10414 * @cfg {string} size - (lg|sm) or leave empty..
10415 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10416 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10417 * @cfg {Number} md colspan out of 12 for computer-sized screens
10418 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10419 * @cfg {string} value default value of the input
10420 * @cfg {Number} labelWidth set the width of label
10421 * @cfg {Number} labellg set the width of label (1-12)
10422 * @cfg {Number} labelmd set the width of label (1-12)
10423 * @cfg {Number} labelsm set the width of label (1-12)
10424 * @cfg {Number} labelxs set the width of label (1-12)
10425 * @cfg {String} labelAlign (top|left)
10426 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10427 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10428 * @cfg {String} indicatorpos (left|right) default left
10429 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10430 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10431 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10433 * @cfg {String} align (left|center|right) Default left
10434 * @cfg {Boolean} forceFeedback (true|false) Default false
10437 * Create a new Input
10438 * @param {Object} config The config object
10441 Roo.bootstrap.Input = function(config){
10443 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10448 * Fires when this field receives input focus.
10449 * @param {Roo.form.Field} this
10454 * Fires when this field loses input focus.
10455 * @param {Roo.form.Field} this
10459 * @event specialkey
10460 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10461 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10462 * @param {Roo.form.Field} this
10463 * @param {Roo.EventObject} e The event object
10468 * Fires just before the field blurs if the field value has changed.
10469 * @param {Roo.form.Field} this
10470 * @param {Mixed} newValue The new value
10471 * @param {Mixed} oldValue The original value
10476 * Fires after the field has been marked as invalid.
10477 * @param {Roo.form.Field} this
10478 * @param {String} msg The validation message
10483 * Fires after the field has been validated with no errors.
10484 * @param {Roo.form.Field} this
10489 * Fires after the key up
10490 * @param {Roo.form.Field} this
10491 * @param {Roo.EventObject} e The event Object
10497 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10499 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10500 automatic validation (defaults to "keyup").
10502 validationEvent : "keyup",
10504 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10506 validateOnBlur : true,
10508 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10510 validationDelay : 250,
10512 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10514 focusClass : "x-form-focus", // not needed???
10518 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10520 invalidClass : "has-warning",
10523 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525 validClass : "has-success",
10528 * @cfg {Boolean} hasFeedback (true|false) default true
10530 hasFeedback : true,
10533 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10535 invalidFeedbackClass : "glyphicon-warning-sign",
10538 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540 validFeedbackClass : "glyphicon-ok",
10543 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10545 selectOnFocus : false,
10548 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10557 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10559 disableKeyFilter : false,
10562 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10572 blankText : "Please complete this mandatory field",
10575 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10581 maxLength : Number.MAX_VALUE,
10583 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10585 minLengthText : "The minimum length for this field is {0}",
10587 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10589 maxLengthText : "The maximum length for this field is {0}",
10593 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10594 * If available, this function will be called only after the basic validators all return true, and will be passed the
10595 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10600 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10601 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10605 * @cfg {String} regexText -- Depricated - use Invalid Text
10610 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10616 autocomplete: false,
10620 inputType : 'text',
10623 placeholder: false,
10628 preventMark: false,
10629 isFormField : true,
10632 labelAlign : false,
10635 formatedValue : false,
10636 forceFeedback : false,
10638 indicatorpos : 'left',
10648 parentLabelAlign : function()
10651 while (parent.parent()) {
10652 parent = parent.parent();
10653 if (typeof(parent.labelAlign) !='undefined') {
10654 return parent.labelAlign;
10661 getAutoCreate : function()
10663 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10669 if(this.inputType != 'hidden'){
10670 cfg.cls = 'form-group' //input-group
10676 type : this.inputType,
10677 value : this.value,
10678 cls : 'form-control',
10679 placeholder : this.placeholder || '',
10680 autocomplete : this.autocomplete || 'new-password'
10682 if (this.inputType == 'file') {
10683 input.style = 'overflow:hidden'; // why not in CSS?
10686 if(this.capture.length){
10687 input.capture = this.capture;
10690 if(this.accept.length){
10691 input.accept = this.accept + "/*";
10695 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10698 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10699 input.maxLength = this.maxLength;
10702 if (this.disabled) {
10703 input.disabled=true;
10706 if (this.readOnly) {
10707 input.readonly=true;
10711 input.name = this.name;
10715 input.cls += ' input-' + this.size;
10719 ['xs','sm','md','lg'].map(function(size){
10720 if (settings[size]) {
10721 cfg.cls += ' col-' + size + '-' + settings[size];
10725 var inputblock = input;
10729 cls: 'glyphicon form-control-feedback'
10732 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10735 cls : 'has-feedback',
10743 if (this.before || this.after) {
10746 cls : 'input-group',
10750 if (this.before && typeof(this.before) == 'string') {
10752 inputblock.cn.push({
10754 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758 if (this.before && typeof(this.before) == 'object') {
10759 this.before = Roo.factory(this.before);
10761 inputblock.cn.push({
10763 cls : 'roo-input-before input-group-prepend input-group-' +
10764 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10768 inputblock.cn.push(input);
10770 if (this.after && typeof(this.after) == 'string') {
10771 inputblock.cn.push({
10773 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777 if (this.after && typeof(this.after) == 'object') {
10778 this.after = Roo.factory(this.after);
10780 inputblock.cn.push({
10782 cls : 'roo-input-after input-group-append input-group-' +
10783 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10788 inputblock.cls += ' has-feedback';
10789 inputblock.cn.push(feedback);
10794 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10795 tooltip : 'This field is required'
10797 if (this.allowBlank ) {
10798 indicator.style = this.allowBlank ? ' display:none' : '';
10800 if (align ==='left' && this.fieldLabel.length) {
10802 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10809 cls : 'control-label col-form-label',
10810 html : this.fieldLabel
10821 var labelCfg = cfg.cn[1];
10822 var contentCfg = cfg.cn[2];
10824 if(this.indicatorpos == 'right'){
10829 cls : 'control-label col-form-label',
10833 html : this.fieldLabel
10847 labelCfg = cfg.cn[0];
10848 contentCfg = cfg.cn[1];
10852 if(this.labelWidth > 12){
10853 labelCfg.style = "width: " + this.labelWidth + 'px';
10856 if(this.labelWidth < 13 && this.labelmd == 0){
10857 this.labelmd = this.labelWidth;
10860 if(this.labellg > 0){
10861 labelCfg.cls += ' col-lg-' + this.labellg;
10862 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10865 if(this.labelmd > 0){
10866 labelCfg.cls += ' col-md-' + this.labelmd;
10867 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10870 if(this.labelsm > 0){
10871 labelCfg.cls += ' col-sm-' + this.labelsm;
10872 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10875 if(this.labelxs > 0){
10876 labelCfg.cls += ' col-xs-' + this.labelxs;
10877 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881 } else if ( this.fieldLabel.length) {
10888 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10889 tooltip : 'This field is required',
10890 style : this.allowBlank ? ' display:none' : ''
10894 //cls : 'input-group-addon',
10895 html : this.fieldLabel
10903 if(this.indicatorpos == 'right'){
10908 //cls : 'input-group-addon',
10909 html : this.fieldLabel
10914 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10915 tooltip : 'This field is required',
10916 style : this.allowBlank ? ' display:none' : ''
10936 if (this.parentType === 'Navbar' && this.parent().bar) {
10937 cfg.cls += ' navbar-form';
10940 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10941 // on BS4 we do this only if not form
10942 cfg.cls += ' navbar-form';
10950 * return the real input element.
10952 inputEl: function ()
10954 return this.el.select('input.form-control',true).first();
10957 tooltipEl : function()
10959 return this.inputEl();
10962 indicatorEl : function()
10964 if (Roo.bootstrap.version == 4) {
10965 return false; // not enabled in v4 yet.
10968 var indicator = this.el.select('i.roo-required-indicator',true).first();
10978 setDisabled : function(v)
10980 var i = this.inputEl().dom;
10982 i.removeAttribute('disabled');
10986 i.setAttribute('disabled','true');
10988 initEvents : function()
10991 this.inputEl().on("keydown" , this.fireKey, this);
10992 this.inputEl().on("focus", this.onFocus, this);
10993 this.inputEl().on("blur", this.onBlur, this);
10995 this.inputEl().relayEvent('keyup', this);
10997 this.indicator = this.indicatorEl();
10999 if(this.indicator){
11000 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11003 // reference to original value for reset
11004 this.originalValue = this.getValue();
11005 //Roo.form.TextField.superclass.initEvents.call(this);
11006 if(this.validationEvent == 'keyup'){
11007 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11008 this.inputEl().on('keyup', this.filterValidation, this);
11010 else if(this.validationEvent !== false){
11011 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11014 if(this.selectOnFocus){
11015 this.on("focus", this.preFocus, this);
11018 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11019 this.inputEl().on("keypress", this.filterKeys, this);
11021 this.inputEl().relayEvent('keypress', this);
11024 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11025 this.el.on("click", this.autoSize, this);
11028 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11029 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11032 if (typeof(this.before) == 'object') {
11033 this.before.render(this.el.select('.roo-input-before',true).first());
11035 if (typeof(this.after) == 'object') {
11036 this.after.render(this.el.select('.roo-input-after',true).first());
11039 this.inputEl().on('change', this.onChange, this);
11042 filterValidation : function(e){
11043 if(!e.isNavKeyPress()){
11044 this.validationTask.delay(this.validationDelay);
11048 * Validates the field value
11049 * @return {Boolean} True if the value is valid, else false
11051 validate : function(){
11052 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11053 if(this.disabled || this.validateValue(this.getRawValue())){
11058 this.markInvalid();
11064 * Validates a value according to the field's validation rules and marks the field as invalid
11065 * if the validation fails
11066 * @param {Mixed} value The value to validate
11067 * @return {Boolean} True if the value is valid, else false
11069 validateValue : function(value)
11071 if(this.getVisibilityEl().hasClass('hidden')){
11075 if(value.length < 1) { // if it's blank
11076 if(this.allowBlank){
11082 if(value.length < this.minLength){
11085 if(value.length > this.maxLength){
11089 var vt = Roo.form.VTypes;
11090 if(!vt[this.vtype](value, this)){
11094 if(typeof this.validator == "function"){
11095 var msg = this.validator(value);
11099 if (typeof(msg) == 'string') {
11100 this.invalidText = msg;
11104 if(this.regex && !this.regex.test(value)){
11112 fireKey : function(e){
11113 //Roo.log('field ' + e.getKey());
11114 if(e.isNavKeyPress()){
11115 this.fireEvent("specialkey", this, e);
11118 focus : function (selectText){
11120 this.inputEl().focus();
11121 if(selectText === true){
11122 this.inputEl().dom.select();
11128 onFocus : function(){
11129 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11130 // this.el.addClass(this.focusClass);
11132 if(!this.hasFocus){
11133 this.hasFocus = true;
11134 this.startValue = this.getValue();
11135 this.fireEvent("focus", this);
11139 beforeBlur : Roo.emptyFn,
11143 onBlur : function(){
11145 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11146 //this.el.removeClass(this.focusClass);
11148 this.hasFocus = false;
11149 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11152 var v = this.getValue();
11153 if(String(v) !== String(this.startValue)){
11154 this.fireEvent('change', this, v, this.startValue);
11156 this.fireEvent("blur", this);
11159 onChange : function(e)
11161 var v = this.getValue();
11162 if(String(v) !== String(this.startValue)){
11163 this.fireEvent('change', this, v, this.startValue);
11169 * Resets the current field value to the originally loaded value and clears any validation messages
11171 reset : function(){
11172 this.setValue(this.originalValue);
11176 * Returns the name of the field
11177 * @return {Mixed} name The name field
11179 getName: function(){
11183 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11184 * @return {Mixed} value The field value
11186 getValue : function(){
11188 var v = this.inputEl().getValue();
11193 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11194 * @return {Mixed} value The field value
11196 getRawValue : function(){
11197 var v = this.inputEl().getValue();
11203 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11204 * @param {Mixed} value The value to set
11206 setRawValue : function(v){
11207 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11210 selectText : function(start, end){
11211 var v = this.getRawValue();
11213 start = start === undefined ? 0 : start;
11214 end = end === undefined ? v.length : end;
11215 var d = this.inputEl().dom;
11216 if(d.setSelectionRange){
11217 d.setSelectionRange(start, end);
11218 }else if(d.createTextRange){
11219 var range = d.createTextRange();
11220 range.moveStart("character", start);
11221 range.moveEnd("character", v.length-end);
11228 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11229 * @param {Mixed} value The value to set
11231 setValue : function(v){
11234 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11240 processValue : function(value){
11241 if(this.stripCharsRe){
11242 var newValue = value.replace(this.stripCharsRe, '');
11243 if(newValue !== value){
11244 this.setRawValue(newValue);
11251 preFocus : function(){
11253 if(this.selectOnFocus){
11254 this.inputEl().dom.select();
11257 filterKeys : function(e){
11258 var k = e.getKey();
11259 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11262 var c = e.getCharCode(), cc = String.fromCharCode(c);
11263 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11266 if(!this.maskRe.test(cc)){
11271 * Clear any invalid styles/messages for this field
11273 clearInvalid : function(){
11275 if(!this.el || this.preventMark){ // not rendered
11280 this.el.removeClass([this.invalidClass, 'is-invalid']);
11282 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11284 var feedback = this.el.select('.form-control-feedback', true).first();
11287 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11292 if(this.indicator){
11293 this.indicator.removeClass('visible');
11294 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11297 this.fireEvent('valid', this);
11301 * Mark this field as valid
11303 markValid : function()
11305 if(!this.el || this.preventMark){ // not rendered...
11309 this.el.removeClass([this.invalidClass, this.validClass]);
11310 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11312 var feedback = this.el.select('.form-control-feedback', true).first();
11315 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11318 if(this.indicator){
11319 this.indicator.removeClass('visible');
11320 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11328 if(this.allowBlank && !this.getRawValue().length){
11331 if (Roo.bootstrap.version == 3) {
11332 this.el.addClass(this.validClass);
11334 this.inputEl().addClass('is-valid');
11337 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11339 var feedback = this.el.select('.form-control-feedback', true).first();
11342 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11343 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11348 this.fireEvent('valid', this);
11352 * Mark this field as invalid
11353 * @param {String} msg The validation message
11355 markInvalid : function(msg)
11357 if(!this.el || this.preventMark){ // not rendered
11361 this.el.removeClass([this.invalidClass, this.validClass]);
11362 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11364 var feedback = this.el.select('.form-control-feedback', true).first();
11367 this.el.select('.form-control-feedback', true).first().removeClass(
11368 [this.invalidFeedbackClass, this.validFeedbackClass]);
11375 if(this.allowBlank && !this.getRawValue().length){
11379 if(this.indicator){
11380 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11381 this.indicator.addClass('visible');
11383 if (Roo.bootstrap.version == 3) {
11384 this.el.addClass(this.invalidClass);
11386 this.inputEl().addClass('is-invalid');
11391 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11393 var feedback = this.el.select('.form-control-feedback', true).first();
11396 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11398 if(this.getValue().length || this.forceFeedback){
11399 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406 this.fireEvent('invalid', this, msg);
11409 SafariOnKeyDown : function(event)
11411 // this is a workaround for a password hang bug on chrome/ webkit.
11412 if (this.inputEl().dom.type != 'password') {
11416 var isSelectAll = false;
11418 if(this.inputEl().dom.selectionEnd > 0){
11419 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11421 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11422 event.preventDefault();
11427 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11429 event.preventDefault();
11430 // this is very hacky as keydown always get's upper case.
11432 var cc = String.fromCharCode(event.getCharCode());
11433 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11437 adjustWidth : function(tag, w){
11438 tag = tag.toLowerCase();
11439 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11440 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11441 if(tag == 'input'){
11444 if(tag == 'textarea'){
11447 }else if(Roo.isOpera){
11448 if(tag == 'input'){
11451 if(tag == 'textarea'){
11459 setFieldLabel : function(v)
11461 if(!this.rendered){
11465 if(this.indicatorEl()){
11466 var ar = this.el.select('label > span',true);
11468 if (ar.elements.length) {
11469 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11470 this.fieldLabel = v;
11474 var br = this.el.select('label',true);
11476 if(br.elements.length) {
11477 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11478 this.fieldLabel = v;
11482 Roo.log('Cannot Found any of label > span || label in input');
11486 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11487 this.fieldLabel = v;
11502 * @class Roo.bootstrap.TextArea
11503 * @extends Roo.bootstrap.Input
11504 * Bootstrap TextArea class
11505 * @cfg {Number} cols Specifies the visible width of a text area
11506 * @cfg {Number} rows Specifies the visible number of lines in a text area
11507 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11508 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11509 * @cfg {string} html text
11512 * Create a new TextArea
11513 * @param {Object} config The config object
11516 Roo.bootstrap.TextArea = function(config){
11517 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11531 getAutoCreate : function(){
11533 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11539 if(this.inputType != 'hidden'){
11540 cfg.cls = 'form-group' //input-group
11548 value : this.value || '',
11549 html: this.html || '',
11550 cls : 'form-control',
11551 placeholder : this.placeholder || ''
11555 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11556 input.maxLength = this.maxLength;
11560 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564 input.cols = this.cols;
11567 if (this.readOnly) {
11568 input.readonly = true;
11572 input.name = this.name;
11576 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580 ['xs','sm','md','lg'].map(function(size){
11581 if (settings[size]) {
11582 cfg.cls += ' col-' + size + '-' + settings[size];
11586 var inputblock = input;
11588 if(this.hasFeedback && !this.allowBlank){
11592 cls: 'glyphicon form-control-feedback'
11596 cls : 'has-feedback',
11605 if (this.before || this.after) {
11608 cls : 'input-group',
11612 inputblock.cn.push({
11614 cls : 'input-group-addon',
11619 inputblock.cn.push(input);
11621 if(this.hasFeedback && !this.allowBlank){
11622 inputblock.cls += ' has-feedback';
11623 inputblock.cn.push(feedback);
11627 inputblock.cn.push({
11629 cls : 'input-group-addon',
11636 if (align ==='left' && this.fieldLabel.length) {
11641 cls : 'control-label',
11642 html : this.fieldLabel
11653 if(this.labelWidth > 12){
11654 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11657 if(this.labelWidth < 13 && this.labelmd == 0){
11658 this.labelmd = this.labelWidth;
11661 if(this.labellg > 0){
11662 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11663 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11666 if(this.labelmd > 0){
11667 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11668 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11671 if(this.labelsm > 0){
11672 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11673 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11676 if(this.labelxs > 0){
11677 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11678 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11681 } else if ( this.fieldLabel.length) {
11686 //cls : 'input-group-addon',
11687 html : this.fieldLabel
11705 if (this.disabled) {
11706 input.disabled=true;
11713 * return the real textarea element.
11715 inputEl: function ()
11717 return this.el.select('textarea.form-control',true).first();
11721 * Clear any invalid styles/messages for this field
11723 clearInvalid : function()
11726 if(!this.el || this.preventMark){ // not rendered
11730 var label = this.el.select('label', true).first();
11731 var icon = this.el.select('i.fa-star', true).first();
11736 this.el.removeClass( this.validClass);
11737 this.inputEl().removeClass('is-invalid');
11739 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11741 var feedback = this.el.select('.form-control-feedback', true).first();
11744 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11749 this.fireEvent('valid', this);
11753 * Mark this field as valid
11755 markValid : function()
11757 if(!this.el || this.preventMark){ // not rendered
11761 this.el.removeClass([this.invalidClass, this.validClass]);
11762 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11764 var feedback = this.el.select('.form-control-feedback', true).first();
11767 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11770 if(this.disabled || this.allowBlank){
11774 var label = this.el.select('label', true).first();
11775 var icon = this.el.select('i.fa-star', true).first();
11780 if (Roo.bootstrap.version == 3) {
11781 this.el.addClass(this.validClass);
11783 this.inputEl().addClass('is-valid');
11787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11789 var feedback = this.el.select('.form-control-feedback', true).first();
11792 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11793 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11798 this.fireEvent('valid', this);
11802 * Mark this field as invalid
11803 * @param {String} msg The validation message
11805 markInvalid : function(msg)
11807 if(!this.el || this.preventMark){ // not rendered
11811 this.el.removeClass([this.invalidClass, this.validClass]);
11812 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11814 var feedback = this.el.select('.form-control-feedback', true).first();
11817 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11820 if(this.disabled || this.allowBlank){
11824 var label = this.el.select('label', true).first();
11825 var icon = this.el.select('i.fa-star', true).first();
11827 if(!this.getValue().length && label && !icon){
11828 this.el.createChild({
11830 cls : 'text-danger fa fa-lg fa-star',
11831 tooltip : 'This field is required',
11832 style : 'margin-right:5px;'
11836 if (Roo.bootstrap.version == 3) {
11837 this.el.addClass(this.invalidClass);
11839 this.inputEl().addClass('is-invalid');
11842 // fixme ... this may be depricated need to test..
11843 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11845 var feedback = this.el.select('.form-control-feedback', true).first();
11848 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11850 if(this.getValue().length || this.forceFeedback){
11851 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858 this.fireEvent('invalid', this, msg);
11866 * trigger field - base class for combo..
11871 * @class Roo.bootstrap.TriggerField
11872 * @extends Roo.bootstrap.Input
11873 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11874 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11875 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11876 * for which you can provide a custom implementation. For example:
11878 var trigger = new Roo.bootstrap.TriggerField();
11879 trigger.onTriggerClick = myTriggerFn;
11880 trigger.applyTo('my-field');
11883 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11884 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11885 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11886 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11887 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11890 * Create a new TriggerField.
11891 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11892 * to the base TextField)
11894 Roo.bootstrap.TriggerField = function(config){
11895 this.mimicing = false;
11896 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11901 * @cfg {String} triggerClass A CSS class to apply to the trigger
11904 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11909 * @cfg {Boolean} removable (true|false) special filter default false
11913 /** @cfg {Boolean} grow @hide */
11914 /** @cfg {Number} growMin @hide */
11915 /** @cfg {Number} growMax @hide */
11921 autoSize: Roo.emptyFn,
11925 deferHeight : true,
11928 actionMode : 'wrap',
11933 getAutoCreate : function(){
11935 var align = this.labelAlign || this.parentLabelAlign();
11940 cls: 'form-group' //input-group
11947 type : this.inputType,
11948 cls : 'form-control',
11949 autocomplete: 'new-password',
11950 placeholder : this.placeholder || ''
11954 input.name = this.name;
11957 input.cls += ' input-' + this.size;
11960 if (this.disabled) {
11961 input.disabled=true;
11964 var inputblock = input;
11966 if(this.hasFeedback && !this.allowBlank){
11970 cls: 'glyphicon form-control-feedback'
11973 if(this.removable && !this.editable ){
11975 cls : 'has-feedback',
11981 cls : 'roo-combo-removable-btn close'
11988 cls : 'has-feedback',
11997 if(this.removable && !this.editable ){
11999 cls : 'roo-removable',
12005 cls : 'roo-combo-removable-btn close'
12012 if (this.before || this.after) {
12015 cls : 'input-group',
12019 inputblock.cn.push({
12021 cls : 'input-group-addon input-group-prepend input-group-text',
12026 inputblock.cn.push(input);
12028 if(this.hasFeedback && !this.allowBlank){
12029 inputblock.cls += ' has-feedback';
12030 inputblock.cn.push(feedback);
12034 inputblock.cn.push({
12036 cls : 'input-group-addon input-group-append input-group-text',
12045 var ibwrap = inputblock;
12050 cls: 'roo-select2-choices',
12054 cls: 'roo-select2-search-field',
12066 cls: 'roo-select2-container input-group',
12071 cls: 'form-hidden-field'
12077 if(!this.multiple && this.showToggleBtn){
12083 if (this.caret != false) {
12086 cls: 'fa fa-' + this.caret
12093 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12095 Roo.bootstrap.version == 3 ? caret : '',
12098 cls: 'combobox-clear',
12112 combobox.cls += ' roo-select2-container-multi';
12116 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12117 tooltip : 'This field is required'
12119 if (Roo.bootstrap.version == 4) {
12122 style : 'display:none'
12127 if (align ==='left' && this.fieldLabel.length) {
12129 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12136 cls : 'control-label',
12137 html : this.fieldLabel
12149 var labelCfg = cfg.cn[1];
12150 var contentCfg = cfg.cn[2];
12152 if(this.indicatorpos == 'right'){
12157 cls : 'control-label',
12161 html : this.fieldLabel
12175 labelCfg = cfg.cn[0];
12176 contentCfg = cfg.cn[1];
12179 if(this.labelWidth > 12){
12180 labelCfg.style = "width: " + this.labelWidth + 'px';
12183 if(this.labelWidth < 13 && this.labelmd == 0){
12184 this.labelmd = this.labelWidth;
12187 if(this.labellg > 0){
12188 labelCfg.cls += ' col-lg-' + this.labellg;
12189 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12192 if(this.labelmd > 0){
12193 labelCfg.cls += ' col-md-' + this.labelmd;
12194 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12197 if(this.labelsm > 0){
12198 labelCfg.cls += ' col-sm-' + this.labelsm;
12199 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12202 if(this.labelxs > 0){
12203 labelCfg.cls += ' col-xs-' + this.labelxs;
12204 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12207 } else if ( this.fieldLabel.length) {
12208 // Roo.log(" label");
12213 //cls : 'input-group-addon',
12214 html : this.fieldLabel
12222 if(this.indicatorpos == 'right'){
12230 html : this.fieldLabel
12244 // Roo.log(" no label && no align");
12251 ['xs','sm','md','lg'].map(function(size){
12252 if (settings[size]) {
12253 cfg.cls += ' col-' + size + '-' + settings[size];
12264 onResize : function(w, h){
12265 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12266 // if(typeof w == 'number'){
12267 // var x = w - this.trigger.getWidth();
12268 // this.inputEl().setWidth(this.adjustWidth('input', x));
12269 // this.trigger.setStyle('left', x+'px');
12274 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12277 getResizeEl : function(){
12278 return this.inputEl();
12282 getPositionEl : function(){
12283 return this.inputEl();
12287 alignErrorIcon : function(){
12288 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292 initEvents : function(){
12296 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12297 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12298 if(!this.multiple && this.showToggleBtn){
12299 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12300 if(this.hideTrigger){
12301 this.trigger.setDisplayed(false);
12303 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12310 if(this.removable && !this.editable && !this.tickable){
12311 var close = this.closeTriggerEl();
12314 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12315 close.on('click', this.removeBtnClick, this, close);
12319 //this.trigger.addClassOnOver('x-form-trigger-over');
12320 //this.trigger.addClassOnClick('x-form-trigger-click');
12323 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327 closeTriggerEl : function()
12329 var close = this.el.select('.roo-combo-removable-btn', true).first();
12330 return close ? close : false;
12333 removeBtnClick : function(e, h, el)
12335 e.preventDefault();
12337 if(this.fireEvent("remove", this) !== false){
12339 this.fireEvent("afterremove", this)
12343 createList : function()
12345 this.list = Roo.get(document.body).createChild({
12346 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12347 cls: 'typeahead typeahead-long dropdown-menu shadow',
12348 style: 'display:none'
12351 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12356 initTrigger : function(){
12361 onDestroy : function(){
12363 this.trigger.removeAllListeners();
12364 // this.trigger.remove();
12367 // this.wrap.remove();
12369 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373 onFocus : function(){
12374 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12376 if(!this.mimicing){
12377 this.wrap.addClass('x-trigger-wrap-focus');
12378 this.mimicing = true;
12379 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12380 if(this.monitorTab){
12381 this.el.on("keydown", this.checkTab, this);
12388 checkTab : function(e){
12389 if(e.getKey() == e.TAB){
12390 this.triggerBlur();
12395 onBlur : function(){
12400 mimicBlur : function(e, t){
12402 if(!this.wrap.contains(t) && this.validateBlur()){
12403 this.triggerBlur();
12409 triggerBlur : function(){
12410 this.mimicing = false;
12411 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12412 if(this.monitorTab){
12413 this.el.un("keydown", this.checkTab, this);
12415 //this.wrap.removeClass('x-trigger-wrap-focus');
12416 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12421 validateBlur : function(e, t){
12426 onDisable : function(){
12427 this.inputEl().dom.disabled = true;
12428 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12430 // this.wrap.addClass('x-item-disabled');
12435 onEnable : function(){
12436 this.inputEl().dom.disabled = false;
12437 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12439 // this.el.removeClass('x-item-disabled');
12444 onShow : function(){
12445 var ae = this.getActionEl();
12448 ae.dom.style.display = '';
12449 ae.dom.style.visibility = 'visible';
12455 onHide : function(){
12456 var ae = this.getActionEl();
12457 ae.dom.style.display = 'none';
12461 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12462 * by an implementing function.
12464 * @param {EventObject} e
12466 onTriggerClick : Roo.emptyFn
12474 * @class Roo.bootstrap.CardUploader
12475 * @extends Roo.bootstrap.Button
12476 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12477 * @cfg {Number} errorTimeout default 3000
12478 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12479 * @cfg {Array} html The button text.
12483 * Create a new CardUploader
12484 * @param {Object} config The config object
12487 Roo.bootstrap.CardUploader = function(config){
12491 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12494 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12501 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12504 errorTimeout : 3000,
12508 fileCollection : false,
12511 getAutoCreate : function()
12515 cls :'form-group' ,
12520 //cls : 'input-group-addon',
12521 html : this.fieldLabel
12528 value : this.value,
12529 cls : 'd-none form-control'
12534 multiple : 'multiple',
12536 cls : 'd-none roo-card-upload-selector'
12540 cls : 'roo-card-uploader-button-container w-100 mb-2'
12543 cls : 'card-columns roo-card-uploader-container'
12553 getChildContainer : function() /// what children are added to.
12555 return this.containerEl;
12558 getButtonContainer : function() /// what children are added to.
12560 return this.el.select(".roo-card-uploader-button-container").first();
12563 initEvents : function()
12566 Roo.bootstrap.Input.prototype.initEvents.call(this);
12570 xns: Roo.bootstrap,
12573 container_method : 'getButtonContainer' ,
12574 html : this.html, // fix changable?
12577 'click' : function(btn, e) {
12586 this.urlAPI = (window.createObjectURL && window) ||
12587 (window.URL && URL.revokeObjectURL && URL) ||
12588 (window.webkitURL && webkitURL);
12593 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12595 this.selectorEl.on('change', this.onFileSelected, this);
12598 this.images.forEach(function(img) {
12601 this.images = false;
12603 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12609 onClick : function(e)
12611 e.preventDefault();
12613 this.selectorEl.dom.click();
12617 onFileSelected : function(e)
12619 e.preventDefault();
12621 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625 Roo.each(this.selectorEl.dom.files, function(file){
12626 this.addFile(file);
12635 addFile : function(file)
12638 if(typeof(file) === 'string'){
12639 throw "Add file by name?"; // should not happen
12643 if(!file || !this.urlAPI){
12653 var url = _this.urlAPI.createObjectURL( file);
12656 id : Roo.bootstrap.CardUploader.ID--,
12657 is_uploaded : false,
12660 mimetype : file.type,
12667 addCard : function (data)
12669 // hidden input element?
12670 // if the file is not an image...
12671 //then we need to use something other that and header_image
12676 xns : Roo.bootstrap,
12677 xtype : 'CardFooter',
12680 xns : Roo.bootstrap,
12686 xns : Roo.bootstrap,
12688 html : String.format("<small>{0}</small>", data.title),
12689 cls : 'col-11 text-left',
12694 click : function() {
12695 this.downloadCard(data.id)
12701 xns : Roo.bootstrap,
12709 click : function() {
12710 t.removeCard(data.id)
12722 var cn = this.addxtype(
12725 xns : Roo.bootstrap,
12728 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12729 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12730 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12735 initEvents : function() {
12736 Roo.bootstrap.Card.prototype.initEvents.call(this);
12737 this.imgEl = this.el.select('.card-img-top').first();
12739 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12740 this.imgEl.set({ 'pointer' : 'cursor' });
12749 // dont' really need ot update items.
12750 // this.items.push(cn);
12751 this.fileCollection.add(cn);
12752 this.updateInput();
12755 removeCard : function(id)
12758 var card = this.fileCollection.get(id);
12759 card.data.is_deleted = 1;
12760 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12761 this.fileCollection.remove(card);
12762 //this.items = this.items.filter(function(e) { return e != card });
12763 // dont' really need ot update items.
12764 card.el.dom.parentNode.removeChild(card.el.dom);
12769 this.fileCollection.each(function(card) {
12770 card.el.dom.parentNode.removeChild(card.el.dom);
12772 this.fileCollection.clear();
12773 this.updateInput();
12776 updateInput : function()
12779 this.fileCollection.each(function(e) {
12783 this.inputEl().dom.value = JSON.stringify(data);
12790 Roo.bootstrap.CardUploader.ID = -1;/*
12792 * Ext JS Library 1.1.1
12793 * Copyright(c) 2006-2007, Ext JS, LLC.
12795 * Originally Released Under LGPL - original licence link has changed is not relivant.
12798 * <script type="text/javascript">
12803 * @class Roo.data.SortTypes
12805 * Defines the default sorting (casting?) comparison functions used when sorting data.
12807 Roo.data.SortTypes = {
12809 * Default sort that does nothing
12810 * @param {Mixed} s The value being converted
12811 * @return {Mixed} The comparison value
12813 none : function(s){
12818 * The regular expression used to strip tags
12822 stripTagsRE : /<\/?[^>]+>/gi,
12825 * Strips all HTML tags to sort on text only
12826 * @param {Mixed} s The value being converted
12827 * @return {String} The comparison value
12829 asText : function(s){
12830 return String(s).replace(this.stripTagsRE, "");
12834 * Strips all HTML tags to sort on text only - Case insensitive
12835 * @param {Mixed} s The value being converted
12836 * @return {String} The comparison value
12838 asUCText : function(s){
12839 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843 * Case insensitive string
12844 * @param {Mixed} s The value being converted
12845 * @return {String} The comparison value
12847 asUCString : function(s) {
12848 return String(s).toUpperCase();
12853 * @param {Mixed} s The value being converted
12854 * @return {Number} The comparison value
12856 asDate : function(s) {
12860 if(s instanceof Date){
12861 return s.getTime();
12863 return Date.parse(String(s));
12868 * @param {Mixed} s The value being converted
12869 * @return {Float} The comparison value
12871 asFloat : function(s) {
12872 var val = parseFloat(String(s).replace(/,/g, ""));
12881 * @param {Mixed} s The value being converted
12882 * @return {Number} The comparison value
12884 asInt : function(s) {
12885 var val = parseInt(String(s).replace(/,/g, ""));
12893 * Ext JS Library 1.1.1
12894 * Copyright(c) 2006-2007, Ext JS, LLC.
12896 * Originally Released Under LGPL - original licence link has changed is not relivant.
12899 * <script type="text/javascript">
12903 * @class Roo.data.Record
12904 * Instances of this class encapsulate both record <em>definition</em> information, and record
12905 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12906 * to access Records cached in an {@link Roo.data.Store} object.<br>
12908 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12909 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12912 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12914 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12915 * {@link #create}. The parameters are the same.
12916 * @param {Array} data An associative Array of data values keyed by the field name.
12917 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12918 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12919 * not specified an integer id is generated.
12921 Roo.data.Record = function(data, id){
12922 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12927 * Generate a constructor for a specific record layout.
12928 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12929 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12930 * Each field definition object may contain the following properties: <ul>
12931 * <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,
12932 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12933 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12934 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12935 * is being used, then this is a string containing the javascript expression to reference the data relative to
12936 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12937 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12938 * this may be omitted.</p></li>
12939 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12940 * <ul><li>auto (Default, implies no conversion)</li>
12945 * <li>date</li></ul></p></li>
12946 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12947 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12948 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12949 * by the Reader into an object that will be stored in the Record. It is passed the
12950 * following parameters:<ul>
12951 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12953 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12955 * <br>usage:<br><pre><code>
12956 var TopicRecord = Roo.data.Record.create(
12957 {name: 'title', mapping: 'topic_title'},
12958 {name: 'author', mapping: 'username'},
12959 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12960 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12961 {name: 'lastPoster', mapping: 'user2'},
12962 {name: 'excerpt', mapping: 'post_text'}
12965 var myNewRecord = new TopicRecord({
12966 title: 'Do my job please',
12969 lastPost: new Date(),
12970 lastPoster: 'Animal',
12971 excerpt: 'No way dude!'
12973 myStore.add(myNewRecord);
12978 Roo.data.Record.create = function(o){
12979 var f = function(){
12980 f.superclass.constructor.apply(this, arguments);
12982 Roo.extend(f, Roo.data.Record);
12983 var p = f.prototype;
12984 p.fields = new Roo.util.MixedCollection(false, function(field){
12987 for(var i = 0, len = o.length; i < len; i++){
12988 p.fields.add(new Roo.data.Field(o[i]));
12990 f.getField = function(name){
12991 return p.fields.get(name);
12996 Roo.data.Record.AUTO_ID = 1000;
12997 Roo.data.Record.EDIT = 'edit';
12998 Roo.data.Record.REJECT = 'reject';
12999 Roo.data.Record.COMMIT = 'commit';
13001 Roo.data.Record.prototype = {
13003 * Readonly flag - true if this record has been modified.
13012 join : function(store){
13013 this.store = store;
13017 * Set the named field to the specified value.
13018 * @param {String} name The name of the field to set.
13019 * @param {Object} value The value to set the field to.
13021 set : function(name, value){
13022 if(this.data[name] == value){
13026 if(!this.modified){
13027 this.modified = {};
13029 if(typeof this.modified[name] == 'undefined'){
13030 this.modified[name] = this.data[name];
13032 this.data[name] = value;
13033 if(!this.editing && this.store){
13034 this.store.afterEdit(this);
13039 * Get the value of the named field.
13040 * @param {String} name The name of the field to get the value of.
13041 * @return {Object} The value of the field.
13043 get : function(name){
13044 return this.data[name];
13048 beginEdit : function(){
13049 this.editing = true;
13050 this.modified = {};
13054 cancelEdit : function(){
13055 this.editing = false;
13056 delete this.modified;
13060 endEdit : function(){
13061 this.editing = false;
13062 if(this.dirty && this.store){
13063 this.store.afterEdit(this);
13068 * Usually called by the {@link Roo.data.Store} which owns the Record.
13069 * Rejects all changes made to the Record since either creation, or the last commit operation.
13070 * Modified fields are reverted to their original values.
13072 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13073 * of reject operations.
13075 reject : function(){
13076 var m = this.modified;
13078 if(typeof m[n] != "function"){
13079 this.data[n] = m[n];
13082 this.dirty = false;
13083 delete this.modified;
13084 this.editing = false;
13086 this.store.afterReject(this);
13091 * Usually called by the {@link Roo.data.Store} which owns the Record.
13092 * Commits all changes made to the Record since either creation, or the last commit operation.
13094 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095 * of commit operations.
13097 commit : function(){
13098 this.dirty = false;
13099 delete this.modified;
13100 this.editing = false;
13102 this.store.afterCommit(this);
13107 hasError : function(){
13108 return this.error != null;
13112 clearError : function(){
13117 * Creates a copy of this record.
13118 * @param {String} id (optional) A new record id if you don't want to use this record's id
13121 copy : function(newId) {
13122 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126 * Ext JS Library 1.1.1
13127 * Copyright(c) 2006-2007, Ext JS, LLC.
13129 * Originally Released Under LGPL - original licence link has changed is not relivant.
13132 * <script type="text/javascript">
13138 * @class Roo.data.Store
13139 * @extends Roo.util.Observable
13140 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13141 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13143 * 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
13144 * has no knowledge of the format of the data returned by the Proxy.<br>
13146 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13147 * instances from the data object. These records are cached and made available through accessor functions.
13149 * Creates a new Store.
13150 * @param {Object} config A config object containing the objects needed for the Store to access data,
13151 * and read the data into Records.
13153 Roo.data.Store = function(config){
13154 this.data = new Roo.util.MixedCollection(false);
13155 this.data.getKey = function(o){
13158 this.baseParams = {};
13160 this.paramNames = {
13165 "multisort" : "_multisort"
13168 if(config && config.data){
13169 this.inlineData = config.data;
13170 delete config.data;
13173 Roo.apply(this, config);
13175 if(this.reader){ // reader passed
13176 this.reader = Roo.factory(this.reader, Roo.data);
13177 this.reader.xmodule = this.xmodule || false;
13178 if(!this.recordType){
13179 this.recordType = this.reader.recordType;
13181 if(this.reader.onMetaChange){
13182 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186 if(this.recordType){
13187 this.fields = this.recordType.prototype.fields;
13189 this.modified = [];
13193 * @event datachanged
13194 * Fires when the data cache has changed, and a widget which is using this Store
13195 * as a Record cache should refresh its view.
13196 * @param {Store} this
13198 datachanged : true,
13200 * @event metachange
13201 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13202 * @param {Store} this
13203 * @param {Object} meta The JSON metadata
13208 * Fires when Records have been added to the Store
13209 * @param {Store} this
13210 * @param {Roo.data.Record[]} records The array of Records added
13211 * @param {Number} index The index at which the record(s) were added
13216 * Fires when a Record has been removed from the Store
13217 * @param {Store} this
13218 * @param {Roo.data.Record} record The Record that was removed
13219 * @param {Number} index The index at which the record was removed
13224 * Fires when a Record has been updated
13225 * @param {Store} this
13226 * @param {Roo.data.Record} record The Record that was updated
13227 * @param {String} operation The update operation being performed. Value may be one of:
13229 Roo.data.Record.EDIT
13230 Roo.data.Record.REJECT
13231 Roo.data.Record.COMMIT
13237 * Fires when the data cache has been cleared.
13238 * @param {Store} this
13242 * @event beforeload
13243 * Fires before a request is made for a new data object. If the beforeload handler returns false
13244 * the load action will be canceled.
13245 * @param {Store} this
13246 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250 * @event beforeloadadd
13251 * Fires after a new set of Records has been loaded.
13252 * @param {Store} this
13253 * @param {Roo.data.Record[]} records The Records that were loaded
13254 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13256 beforeloadadd : true,
13259 * Fires after a new set of Records has been loaded, before they are added to the store.
13260 * @param {Store} this
13261 * @param {Roo.data.Record[]} records The Records that were loaded
13262 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13263 * @params {Object} return from reader
13267 * @event loadexception
13268 * Fires if an exception occurs in the Proxy during loading.
13269 * Called with the signature of the Proxy's "loadexception" event.
13270 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13273 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13274 * @param {Object} load options
13275 * @param {Object} jsonData from your request (normally this contains the Exception)
13277 loadexception : true
13281 this.proxy = Roo.factory(this.proxy, Roo.data);
13282 this.proxy.xmodule = this.xmodule || false;
13283 this.relayEvents(this.proxy, ["loadexception"]);
13285 this.sortToggle = {};
13286 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13288 Roo.data.Store.superclass.constructor.call(this);
13290 if(this.inlineData){
13291 this.loadData(this.inlineData);
13292 delete this.inlineData;
13296 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13298 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13299 * without a remote query - used by combo/forms at present.
13303 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13306 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13309 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13310 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13313 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13314 * on any HTTP request
13317 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13320 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13325 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13327 remoteSort : false,
13330 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13331 * loaded or when a record is removed. (defaults to false).
13333 pruneModifiedRecords : false,
13336 lastOptions : null,
13339 * Add Records to the Store and fires the add event.
13340 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13342 add : function(records){
13343 records = [].concat(records);
13344 for(var i = 0, len = records.length; i < len; i++){
13345 records[i].join(this);
13347 var index = this.data.length;
13348 this.data.addAll(records);
13349 this.fireEvent("add", this, records, index);
13353 * Remove a Record from the Store and fires the remove event.
13354 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13356 remove : function(record){
13357 var index = this.data.indexOf(record);
13358 this.data.removeAt(index);
13360 if(this.pruneModifiedRecords){
13361 this.modified.remove(record);
13363 this.fireEvent("remove", this, record, index);
13367 * Remove all Records from the Store and fires the clear event.
13369 removeAll : function(){
13371 if(this.pruneModifiedRecords){
13372 this.modified = [];
13374 this.fireEvent("clear", this);
13378 * Inserts Records to the Store at the given index and fires the add event.
13379 * @param {Number} index The start index at which to insert the passed Records.
13380 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13382 insert : function(index, records){
13383 records = [].concat(records);
13384 for(var i = 0, len = records.length; i < len; i++){
13385 this.data.insert(index, records[i]);
13386 records[i].join(this);
13388 this.fireEvent("add", this, records, index);
13392 * Get the index within the cache of the passed Record.
13393 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13394 * @return {Number} The index of the passed Record. Returns -1 if not found.
13396 indexOf : function(record){
13397 return this.data.indexOf(record);
13401 * Get the index within the cache of the Record with the passed id.
13402 * @param {String} id The id of the Record to find.
13403 * @return {Number} The index of the Record. Returns -1 if not found.
13405 indexOfId : function(id){
13406 return this.data.indexOfKey(id);
13410 * Get the Record with the specified id.
13411 * @param {String} id The id of the Record to find.
13412 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13414 getById : function(id){
13415 return this.data.key(id);
13419 * Get the Record at the specified index.
13420 * @param {Number} index The index of the Record to find.
13421 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13423 getAt : function(index){
13424 return this.data.itemAt(index);
13428 * Returns a range of Records between specified indices.
13429 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13430 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13431 * @return {Roo.data.Record[]} An array of Records
13433 getRange : function(start, end){
13434 return this.data.getRange(start, end);
13438 storeOptions : function(o){
13439 o = Roo.apply({}, o);
13442 this.lastOptions = o;
13446 * Loads the Record cache from the configured Proxy using the configured Reader.
13448 * If using remote paging, then the first load call must specify the <em>start</em>
13449 * and <em>limit</em> properties in the options.params property to establish the initial
13450 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13452 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13453 * and this call will return before the new data has been loaded. Perform any post-processing
13454 * in a callback function, or in a "load" event handler.</strong>
13456 * @param {Object} options An object containing properties which control loading options:<ul>
13457 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13458 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13459 * passed the following arguments:<ul>
13460 * <li>r : Roo.data.Record[]</li>
13461 * <li>options: Options object from the load call</li>
13462 * <li>success: Boolean success indicator</li></ul></li>
13463 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13464 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13467 load : function(options){
13468 options = options || {};
13469 if(this.fireEvent("beforeload", this, options) !== false){
13470 this.storeOptions(options);
13471 var p = Roo.apply(options.params || {}, this.baseParams);
13472 // if meta was not loaded from remote source.. try requesting it.
13473 if (!this.reader.metaFromRemote) {
13474 p._requestMeta = 1;
13476 if(this.sortInfo && this.remoteSort){
13477 var pn = this.paramNames;
13478 p[pn["sort"]] = this.sortInfo.field;
13479 p[pn["dir"]] = this.sortInfo.direction;
13481 if (this.multiSort) {
13482 var pn = this.paramNames;
13483 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13486 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13491 * Reloads the Record cache from the configured Proxy using the configured Reader and
13492 * the options from the last load operation performed.
13493 * @param {Object} options (optional) An object containing properties which may override the options
13494 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13495 * the most recently used options are reused).
13497 reload : function(options){
13498 this.load(Roo.applyIf(options||{}, this.lastOptions));
13502 // Called as a callback by the Reader during a load operation.
13503 loadRecords : function(o, options, success){
13504 if(!o || success === false){
13505 if(success !== false){
13506 this.fireEvent("load", this, [], options, o);
13508 if(options.callback){
13509 options.callback.call(options.scope || this, [], options, false);
13513 // if data returned failure - throw an exception.
13514 if (o.success === false) {
13515 // show a message if no listener is registered.
13516 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13517 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13519 // loadmask wil be hooked into this..
13520 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13523 var r = o.records, t = o.totalRecords || r.length;
13525 this.fireEvent("beforeloadadd", this, r, options, o);
13527 if(!options || options.add !== true){
13528 if(this.pruneModifiedRecords){
13529 this.modified = [];
13531 for(var i = 0, len = r.length; i < len; i++){
13535 this.data = this.snapshot;
13536 delete this.snapshot;
13539 this.data.addAll(r);
13540 this.totalLength = t;
13542 this.fireEvent("datachanged", this);
13544 this.totalLength = Math.max(t, this.data.length+r.length);
13548 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13550 var e = new Roo.data.Record({});
13552 e.set(this.parent.displayField, this.parent.emptyTitle);
13553 e.set(this.parent.valueField, '');
13558 this.fireEvent("load", this, r, options, o);
13559 if(options.callback){
13560 options.callback.call(options.scope || this, r, options, true);
13566 * Loads data from a passed data block. A Reader which understands the format of the data
13567 * must have been configured in the constructor.
13568 * @param {Object} data The data block from which to read the Records. The format of the data expected
13569 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13570 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13572 loadData : function(o, append){
13573 var r = this.reader.readRecords(o);
13574 this.loadRecords(r, {add: append}, true);
13578 * using 'cn' the nested child reader read the child array into it's child stores.
13579 * @param {Object} rec The record with a 'children array
13581 loadDataFromChildren : function(rec)
13583 this.loadData(this.reader.toLoadData(rec));
13588 * Gets the number of cached records.
13590 * <em>If using paging, this may not be the total size of the dataset. If the data object
13591 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13592 * the data set size</em>
13594 getCount : function(){
13595 return this.data.length || 0;
13599 * Gets the total number of records in the dataset as returned by the server.
13601 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13602 * the dataset size</em>
13604 getTotalCount : function(){
13605 return this.totalLength || 0;
13609 * Returns the sort state of the Store as an object with two properties:
13611 field {String} The name of the field by which the Records are sorted
13612 direction {String} The sort order, "ASC" or "DESC"
13615 getSortState : function(){
13616 return this.sortInfo;
13620 applySort : function(){
13621 if(this.sortInfo && !this.remoteSort){
13622 var s = this.sortInfo, f = s.field;
13623 var st = this.fields.get(f).sortType;
13624 var fn = function(r1, r2){
13625 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13626 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13628 this.data.sort(s.direction, fn);
13629 if(this.snapshot && this.snapshot != this.data){
13630 this.snapshot.sort(s.direction, fn);
13636 * Sets the default sort column and order to be used by the next load operation.
13637 * @param {String} fieldName The name of the field to sort by.
13638 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13640 setDefaultSort : function(field, dir){
13641 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645 * Sort the Records.
13646 * If remote sorting is used, the sort is performed on the server, and the cache is
13647 * reloaded. If local sorting is used, the cache is sorted internally.
13648 * @param {String} fieldName The name of the field to sort by.
13649 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13651 sort : function(fieldName, dir){
13652 var f = this.fields.get(fieldName);
13654 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13656 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13657 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13662 this.sortToggle[f.name] = dir;
13663 this.sortInfo = {field: f.name, direction: dir};
13664 if(!this.remoteSort){
13666 this.fireEvent("datachanged", this);
13668 this.load(this.lastOptions);
13673 * Calls the specified function for each of the Records in the cache.
13674 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13675 * Returning <em>false</em> aborts and exits the iteration.
13676 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13678 each : function(fn, scope){
13679 this.data.each(fn, scope);
13683 * Gets all records modified since the last commit. Modified records are persisted across load operations
13684 * (e.g., during paging).
13685 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13687 getModifiedRecords : function(){
13688 return this.modified;
13692 createFilterFn : function(property, value, anyMatch){
13693 if(!value.exec){ // not a regex
13694 value = String(value);
13695 if(value.length == 0){
13698 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13700 return function(r){
13701 return value.test(r.data[property]);
13706 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13707 * @param {String} property A field on your records
13708 * @param {Number} start The record index to start at (defaults to 0)
13709 * @param {Number} end The last record index to include (defaults to length - 1)
13710 * @return {Number} The sum
13712 sum : function(property, start, end){
13713 var rs = this.data.items, v = 0;
13714 start = start || 0;
13715 end = (end || end === 0) ? end : rs.length-1;
13717 for(var i = start; i <= end; i++){
13718 v += (rs[i].data[property] || 0);
13724 * Filter the records by a specified property.
13725 * @param {String} field A field on your records
13726 * @param {String/RegExp} value Either a string that the field
13727 * should start with or a RegExp to test against the field
13728 * @param {Boolean} anyMatch True to match any part not just the beginning
13730 filter : function(property, value, anyMatch){
13731 var fn = this.createFilterFn(property, value, anyMatch);
13732 return fn ? this.filterBy(fn) : this.clearFilter();
13736 * Filter by a function. The specified function will be called with each
13737 * record in this data source. If the function returns true the record is included,
13738 * otherwise it is filtered.
13739 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13740 * @param {Object} scope (optional) The scope of the function (defaults to this)
13742 filterBy : function(fn, scope){
13743 this.snapshot = this.snapshot || this.data;
13744 this.data = this.queryBy(fn, scope||this);
13745 this.fireEvent("datachanged", this);
13749 * Query the records by a specified property.
13750 * @param {String} field A field on your records
13751 * @param {String/RegExp} value Either a string that the field
13752 * should start with or a RegExp to test against the field
13753 * @param {Boolean} anyMatch True to match any part not just the beginning
13754 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13756 query : function(property, value, anyMatch){
13757 var fn = this.createFilterFn(property, value, anyMatch);
13758 return fn ? this.queryBy(fn) : this.data.clone();
13762 * Query by a function. The specified function will be called with each
13763 * record in this data source. If the function returns true the record is included
13765 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766 * @param {Object} scope (optional) The scope of the function (defaults to this)
13767 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13769 queryBy : function(fn, scope){
13770 var data = this.snapshot || this.data;
13771 return data.filterBy(fn, scope||this);
13775 * Collects unique values for a particular dataIndex from this store.
13776 * @param {String} dataIndex The property to collect
13777 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13778 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13779 * @return {Array} An array of the unique values
13781 collect : function(dataIndex, allowNull, bypassFilter){
13782 var d = (bypassFilter === true && this.snapshot) ?
13783 this.snapshot.items : this.data.items;
13784 var v, sv, r = [], l = {};
13785 for(var i = 0, len = d.length; i < len; i++){
13786 v = d[i].data[dataIndex];
13788 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13797 * Revert to a view of the Record cache with no filtering applied.
13798 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13800 clearFilter : function(suppressEvent){
13801 if(this.snapshot && this.snapshot != this.data){
13802 this.data = this.snapshot;
13803 delete this.snapshot;
13804 if(suppressEvent !== true){
13805 this.fireEvent("datachanged", this);
13811 afterEdit : function(record){
13812 if(this.modified.indexOf(record) == -1){
13813 this.modified.push(record);
13815 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819 afterReject : function(record){
13820 this.modified.remove(record);
13821 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825 afterCommit : function(record){
13826 this.modified.remove(record);
13827 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13832 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13834 commitChanges : function(){
13835 var m = this.modified.slice(0);
13836 this.modified = [];
13837 for(var i = 0, len = m.length; i < len; i++){
13843 * Cancel outstanding changes on all changed records.
13845 rejectChanges : function(){
13846 var m = this.modified.slice(0);
13847 this.modified = [];
13848 for(var i = 0, len = m.length; i < len; i++){
13853 onMetaChange : function(meta, rtype, o){
13854 this.recordType = rtype;
13855 this.fields = rtype.prototype.fields;
13856 delete this.snapshot;
13857 this.sortInfo = meta.sortInfo || this.sortInfo;
13858 this.modified = [];
13859 this.fireEvent('metachange', this, this.reader.meta);
13862 moveIndex : function(data, type)
13864 var index = this.indexOf(data);
13866 var newIndex = index + type;
13870 this.insert(newIndex, data);
13875 * Ext JS Library 1.1.1
13876 * Copyright(c) 2006-2007, Ext JS, LLC.
13878 * Originally Released Under LGPL - original licence link has changed is not relivant.
13881 * <script type="text/javascript">
13885 * @class Roo.data.SimpleStore
13886 * @extends Roo.data.Store
13887 * Small helper class to make creating Stores from Array data easier.
13888 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13889 * @cfg {Array} fields An array of field definition objects, or field name strings.
13890 * @cfg {Object} an existing reader (eg. copied from another store)
13891 * @cfg {Array} data The multi-dimensional array of data
13893 * @param {Object} config
13895 Roo.data.SimpleStore = function(config)
13897 Roo.data.SimpleStore.superclass.constructor.call(this, {
13899 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13902 Roo.data.Record.create(config.fields)
13904 proxy : new Roo.data.MemoryProxy(config.data)
13908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13910 * Ext JS Library 1.1.1
13911 * Copyright(c) 2006-2007, Ext JS, LLC.
13913 * Originally Released Under LGPL - original licence link has changed is not relivant.
13916 * <script type="text/javascript">
13921 * @extends Roo.data.Store
13922 * @class Roo.data.JsonStore
13923 * Small helper class to make creating Stores for JSON data easier. <br/>
13925 var store = new Roo.data.JsonStore({
13926 url: 'get-images.php',
13928 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13931 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13932 * JsonReader and HttpProxy (unless inline data is provided).</b>
13933 * @cfg {Array} fields An array of field definition objects, or field name strings.
13935 * @param {Object} config
13937 Roo.data.JsonStore = function(c){
13938 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13939 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13940 reader: new Roo.data.JsonReader(c, c.fields)
13943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13945 * Ext JS Library 1.1.1
13946 * Copyright(c) 2006-2007, Ext JS, LLC.
13948 * Originally Released Under LGPL - original licence link has changed is not relivant.
13951 * <script type="text/javascript">
13955 Roo.data.Field = function(config){
13956 if(typeof config == "string"){
13957 config = {name: config};
13959 Roo.apply(this, config);
13962 this.type = "auto";
13965 var st = Roo.data.SortTypes;
13966 // named sortTypes are supported, here we look them up
13967 if(typeof this.sortType == "string"){
13968 this.sortType = st[this.sortType];
13971 // set default sortType for strings and dates
13972 if(!this.sortType){
13975 this.sortType = st.asUCString;
13978 this.sortType = st.asDate;
13981 this.sortType = st.none;
13986 var stripRe = /[\$,%]/g;
13988 // prebuilt conversion function for this field, instead of
13989 // switching every time we're reading a value
13991 var cv, dateFormat = this.dateFormat;
13996 cv = function(v){ return v; };
13999 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003 return v !== undefined && v !== null && v !== '' ?
14004 parseInt(String(v).replace(stripRe, ""), 10) : '';
14009 return v !== undefined && v !== null && v !== '' ?
14010 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14015 cv = function(v){ return v === true || v === "true" || v == 1; };
14022 if(v instanceof Date){
14026 if(dateFormat == "timestamp"){
14027 return new Date(v*1000);
14029 return Date.parseDate(v, dateFormat);
14031 var parsed = Date.parse(v);
14032 return parsed ? new Date(parsed) : null;
14041 Roo.data.Field.prototype = {
14049 * Ext JS Library 1.1.1
14050 * Copyright(c) 2006-2007, Ext JS, LLC.
14052 * Originally Released Under LGPL - original licence link has changed is not relivant.
14055 * <script type="text/javascript">
14058 // Base class for reading structured data from a data source. This class is intended to be
14059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14062 * @class Roo.data.DataReader
14063 * Base class for reading structured data from a data source. This class is intended to be
14064 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14067 Roo.data.DataReader = function(meta, recordType){
14071 this.recordType = recordType instanceof Array ?
14072 Roo.data.Record.create(recordType) : recordType;
14075 Roo.data.DataReader.prototype = {
14078 readerType : 'Data',
14080 * Create an empty record
14081 * @param {Object} data (optional) - overlay some values
14082 * @return {Roo.data.Record} record created.
14084 newRow : function(d) {
14086 this.recordType.prototype.fields.each(function(c) {
14088 case 'int' : da[c.name] = 0; break;
14089 case 'date' : da[c.name] = new Date(); break;
14090 case 'float' : da[c.name] = 0.0; break;
14091 case 'boolean' : da[c.name] = false; break;
14092 default : da[c.name] = ""; break;
14096 return new this.recordType(Roo.apply(da, d));
14102 * Ext JS Library 1.1.1
14103 * Copyright(c) 2006-2007, Ext JS, LLC.
14105 * Originally Released Under LGPL - original licence link has changed is not relivant.
14108 * <script type="text/javascript">
14112 * @class Roo.data.DataProxy
14113 * @extends Roo.data.Observable
14114 * This class is an abstract base class for implementations which provide retrieval of
14115 * unformatted data objects.<br>
14117 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14118 * (of the appropriate type which knows how to parse the data object) to provide a block of
14119 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14121 * Custom implementations must implement the load method as described in
14122 * {@link Roo.data.HttpProxy#load}.
14124 Roo.data.DataProxy = function(){
14127 * @event beforeload
14128 * Fires before a network request is made to retrieve a data object.
14129 * @param {Object} This DataProxy object.
14130 * @param {Object} params The params parameter to the load function.
14135 * Fires before the load method's callback is called.
14136 * @param {Object} This DataProxy object.
14137 * @param {Object} o The data object.
14138 * @param {Object} arg The callback argument object passed to the load function.
14142 * @event loadexception
14143 * Fires if an Exception occurs during data retrieval.
14144 * @param {Object} This DataProxy object.
14145 * @param {Object} o The data object.
14146 * @param {Object} arg The callback argument object passed to the load function.
14147 * @param {Object} e The Exception.
14149 loadexception : true
14151 Roo.data.DataProxy.superclass.constructor.call(this);
14154 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14157 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161 * Ext JS Library 1.1.1
14162 * Copyright(c) 2006-2007, Ext JS, LLC.
14164 * Originally Released Under LGPL - original licence link has changed is not relivant.
14167 * <script type="text/javascript">
14170 * @class Roo.data.MemoryProxy
14171 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14172 * to the Reader when its load method is called.
14174 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14176 Roo.data.MemoryProxy = function(data){
14180 Roo.data.MemoryProxy.superclass.constructor.call(this);
14184 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14187 * Load data from the requested source (in this case an in-memory
14188 * data object passed to the constructor), read the data object into
14189 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14190 * process that block using the passed callback.
14191 * @param {Object} params This parameter is not used by the MemoryProxy class.
14192 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14193 * object into a block of Roo.data.Records.
14194 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14195 * The function must be passed <ul>
14196 * <li>The Record block object</li>
14197 * <li>The "arg" argument from the load function</li>
14198 * <li>A boolean success indicator</li>
14200 * @param {Object} scope The scope in which to call the callback
14201 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14203 load : function(params, reader, callback, scope, arg){
14204 params = params || {};
14207 result = reader.readRecords(params.data ? params.data :this.data);
14209 this.fireEvent("loadexception", this, arg, null, e);
14210 callback.call(scope, null, arg, false);
14213 callback.call(scope, result, arg, true);
14217 update : function(params, records){
14222 * Ext JS Library 1.1.1
14223 * Copyright(c) 2006-2007, Ext JS, LLC.
14225 * Originally Released Under LGPL - original licence link has changed is not relivant.
14228 * <script type="text/javascript">
14231 * @class Roo.data.HttpProxy
14232 * @extends Roo.data.DataProxy
14233 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14234 * configured to reference a certain URL.<br><br>
14236 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14237 * from which the running page was served.<br><br>
14239 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14241 * Be aware that to enable the browser to parse an XML document, the server must set
14242 * the Content-Type header in the HTTP response to "text/xml".
14244 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14245 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14246 * will be used to make the request.
14248 Roo.data.HttpProxy = function(conn){
14249 Roo.data.HttpProxy.superclass.constructor.call(this);
14250 // is conn a conn config or a real conn?
14252 this.useAjax = !conn || !conn.events;
14256 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14257 // thse are take from connection...
14260 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14263 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14264 * extra parameters to each request made by this object. (defaults to undefined)
14267 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14268 * to each request made by this object. (defaults to undefined)
14271 * @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)
14274 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14277 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14283 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14288 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14289 * a finer-grained basis than the DataProxy events.
14291 getConnection : function(){
14292 return this.useAjax ? Roo.Ajax : this.conn;
14296 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14297 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14298 * process that block using the passed callback.
14299 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14300 * for the request to the remote server.
14301 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14302 * object into a block of Roo.data.Records.
14303 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14304 * The function must be passed <ul>
14305 * <li>The Record block object</li>
14306 * <li>The "arg" argument from the load function</li>
14307 * <li>A boolean success indicator</li>
14309 * @param {Object} scope The scope in which to call the callback
14310 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14312 load : function(params, reader, callback, scope, arg){
14313 if(this.fireEvent("beforeload", this, params) !== false){
14315 params : params || {},
14317 callback : callback,
14322 callback : this.loadResponse,
14326 Roo.applyIf(o, this.conn);
14327 if(this.activeRequest){
14328 Roo.Ajax.abort(this.activeRequest);
14330 this.activeRequest = Roo.Ajax.request(o);
14332 this.conn.request(o);
14335 callback.call(scope||this, null, arg, false);
14340 loadResponse : function(o, success, response){
14341 delete this.activeRequest;
14343 this.fireEvent("loadexception", this, o, response);
14344 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14349 result = o.reader.read(response);
14351 this.fireEvent("loadexception", this, o, response, e);
14352 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356 this.fireEvent("load", this, o, o.request.arg);
14357 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361 update : function(dataSet){
14366 updateResponse : function(dataSet){
14371 * Ext JS Library 1.1.1
14372 * Copyright(c) 2006-2007, Ext JS, LLC.
14374 * Originally Released Under LGPL - original licence link has changed is not relivant.
14377 * <script type="text/javascript">
14381 * @class Roo.data.ScriptTagProxy
14382 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14383 * other than the originating domain of the running page.<br><br>
14385 * <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
14386 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14388 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14389 * source code that is used as the source inside a <script> tag.<br><br>
14391 * In order for the browser to process the returned data, the server must wrap the data object
14392 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14393 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14394 * depending on whether the callback name was passed:
14397 boolean scriptTag = false;
14398 String cb = request.getParameter("callback");
14401 response.setContentType("text/javascript");
14403 response.setContentType("application/x-json");
14405 Writer out = response.getWriter();
14407 out.write(cb + "(");
14409 out.print(dataBlock.toJsonString());
14416 * @param {Object} config A configuration object.
14418 Roo.data.ScriptTagProxy = function(config){
14419 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14420 Roo.apply(this, config);
14421 this.head = document.getElementsByTagName("head")[0];
14424 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14426 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14428 * @cfg {String} url The URL from which to request the data object.
14431 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14436 * the server the name of the callback function set up by the load call to process the returned data object.
14437 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14438 * javascript output which calls this named function passing the data object as its only parameter.
14440 callbackParam : "callback",
14442 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14443 * name to the request.
14448 * Load data from the configured URL, read the data object into
14449 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14450 * process that block using the passed callback.
14451 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14452 * for the request to the remote server.
14453 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14454 * object into a block of Roo.data.Records.
14455 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14456 * The function must be passed <ul>
14457 * <li>The Record block object</li>
14458 * <li>The "arg" argument from the load function</li>
14459 * <li>A boolean success indicator</li>
14461 * @param {Object} scope The scope in which to call the callback
14462 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14464 load : function(params, reader, callback, scope, arg){
14465 if(this.fireEvent("beforeload", this, params) !== false){
14467 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14469 var url = this.url;
14470 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14472 url += "&_dc=" + (new Date().getTime());
14474 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14477 cb : "stcCallback"+transId,
14478 scriptId : "stcScript"+transId,
14482 callback : callback,
14488 window[trans.cb] = function(o){
14489 conn.handleResponse(o, trans);
14492 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14494 if(this.autoAbort !== false){
14498 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14500 var script = document.createElement("script");
14501 script.setAttribute("src", url);
14502 script.setAttribute("type", "text/javascript");
14503 script.setAttribute("id", trans.scriptId);
14504 this.head.appendChild(script);
14506 this.trans = trans;
14508 callback.call(scope||this, null, arg, false);
14513 isLoading : function(){
14514 return this.trans ? true : false;
14518 * Abort the current server request.
14520 abort : function(){
14521 if(this.isLoading()){
14522 this.destroyTrans(this.trans);
14527 destroyTrans : function(trans, isLoaded){
14528 this.head.removeChild(document.getElementById(trans.scriptId));
14529 clearTimeout(trans.timeoutId);
14531 window[trans.cb] = undefined;
14533 delete window[trans.cb];
14536 // if hasn't been loaded, wait for load to remove it to prevent script error
14537 window[trans.cb] = function(){
14538 window[trans.cb] = undefined;
14540 delete window[trans.cb];
14547 handleResponse : function(o, trans){
14548 this.trans = false;
14549 this.destroyTrans(trans, true);
14552 result = trans.reader.readRecords(o);
14554 this.fireEvent("loadexception", this, o, trans.arg, e);
14555 trans.callback.call(trans.scope||window, null, trans.arg, false);
14558 this.fireEvent("load", this, o, trans.arg);
14559 trans.callback.call(trans.scope||window, result, trans.arg, true);
14563 handleFailure : function(trans){
14564 this.trans = false;
14565 this.destroyTrans(trans, false);
14566 this.fireEvent("loadexception", this, null, trans.arg);
14567 trans.callback.call(trans.scope||window, null, trans.arg, false);
14571 * Ext JS Library 1.1.1
14572 * Copyright(c) 2006-2007, Ext JS, LLC.
14574 * Originally Released Under LGPL - original licence link has changed is not relivant.
14577 * <script type="text/javascript">
14581 * @class Roo.data.JsonReader
14582 * @extends Roo.data.DataReader
14583 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14584 * based on mappings in a provided Roo.data.Record constructor.
14586 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14587 * in the reply previously.
14592 var RecordDef = Roo.data.Record.create([
14593 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14594 {name: 'occupation'} // This field will use "occupation" as the mapping.
14596 var myReader = new Roo.data.JsonReader({
14597 totalProperty: "results", // The property which contains the total dataset size (optional)
14598 root: "rows", // The property which contains an Array of row objects
14599 id: "id" // The property within each row object that provides an ID for the record (optional)
14603 * This would consume a JSON file like this:
14605 { 'results': 2, 'rows': [
14606 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14607 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14610 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14611 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14612 * paged from the remote server.
14613 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14614 * @cfg {String} root name of the property which contains the Array of row objects.
14615 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14616 * @cfg {Array} fields Array of field definition objects
14618 * Create a new JsonReader
14619 * @param {Object} meta Metadata configuration options
14620 * @param {Object} recordType Either an Array of field definition objects,
14621 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14623 Roo.data.JsonReader = function(meta, recordType){
14626 // set some defaults:
14627 Roo.applyIf(meta, {
14628 totalProperty: 'total',
14629 successProperty : 'success',
14634 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14638 readerType : 'Json',
14641 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14642 * Used by Store query builder to append _requestMeta to params.
14645 metaFromRemote : false,
14647 * This method is only used by a DataProxy which has retrieved data from a remote server.
14648 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14649 * @return {Object} data A data block which is used by an Roo.data.Store object as
14650 * a cache of Roo.data.Records.
14652 read : function(response){
14653 var json = response.responseText;
14655 var o = /* eval:var:o */ eval("("+json+")");
14657 throw {message: "JsonReader.read: Json object not found"};
14663 this.metaFromRemote = true;
14664 this.meta = o.metaData;
14665 this.recordType = Roo.data.Record.create(o.metaData.fields);
14666 this.onMetaChange(this.meta, this.recordType, o);
14668 return this.readRecords(o);
14671 // private function a store will implement
14672 onMetaChange : function(meta, recordType, o){
14679 simpleAccess: function(obj, subsc) {
14686 getJsonAccessor: function(){
14688 return function(expr) {
14690 return(re.test(expr))
14691 ? new Function("obj", "return obj." + expr)
14696 return Roo.emptyFn;
14701 * Create a data block containing Roo.data.Records from an XML document.
14702 * @param {Object} o An object which contains an Array of row objects in the property specified
14703 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14704 * which contains the total size of the dataset.
14705 * @return {Object} data A data block which is used by an Roo.data.Store object as
14706 * a cache of Roo.data.Records.
14708 readRecords : function(o){
14710 * After any data loads, the raw JSON data is available for further custom processing.
14714 var s = this.meta, Record = this.recordType,
14715 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14717 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14719 if(s.totalProperty) {
14720 this.getTotal = this.getJsonAccessor(s.totalProperty);
14722 if(s.successProperty) {
14723 this.getSuccess = this.getJsonAccessor(s.successProperty);
14725 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14727 var g = this.getJsonAccessor(s.id);
14728 this.getId = function(rec) {
14730 return (r === undefined || r === "") ? null : r;
14733 this.getId = function(){return null;};
14736 for(var jj = 0; jj < fl; jj++){
14738 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14739 this.ef[jj] = this.getJsonAccessor(map);
14743 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14744 if(s.totalProperty){
14745 var vt = parseInt(this.getTotal(o), 10);
14750 if(s.successProperty){
14751 var vs = this.getSuccess(o);
14752 if(vs === false || vs === 'false'){
14757 for(var i = 0; i < c; i++){
14760 var id = this.getId(n);
14761 for(var j = 0; j < fl; j++){
14763 var v = this.ef[j](n);
14765 Roo.log('missing convert for ' + f.name);
14769 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14771 var record = new Record(values, id);
14773 records[i] = record;
14779 totalRecords : totalRecords
14782 // used when loading children.. @see loadDataFromChildren
14783 toLoadData: function(rec)
14785 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14786 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14787 return { data : data, total : data.length };
14792 * Ext JS Library 1.1.1
14793 * Copyright(c) 2006-2007, Ext JS, LLC.
14795 * Originally Released Under LGPL - original licence link has changed is not relivant.
14798 * <script type="text/javascript">
14802 * @class Roo.data.ArrayReader
14803 * @extends Roo.data.DataReader
14804 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14805 * Each element of that Array represents a row of data fields. The
14806 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14807 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811 var RecordDef = Roo.data.Record.create([
14812 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14813 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14815 var myReader = new Roo.data.ArrayReader({
14816 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14820 * This would consume an Array like this:
14822 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826 * Create a new JsonReader
14827 * @param {Object} meta Metadata configuration options.
14828 * @param {Object|Array} recordType Either an Array of field definition objects
14830 * @cfg {Array} fields Array of field definition objects
14831 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14832 * as specified to {@link Roo.data.Record#create},
14833 * or an {@link Roo.data.Record} object
14836 * created using {@link Roo.data.Record#create}.
14838 Roo.data.ArrayReader = function(meta, recordType)
14840 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14843 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14846 * Create a data block containing Roo.data.Records from an XML document.
14847 * @param {Object} o An Array of row objects which represents the dataset.
14848 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14849 * a cache of Roo.data.Records.
14851 readRecords : function(o)
14853 var sid = this.meta ? this.meta.id : null;
14854 var recordType = this.recordType, fields = recordType.prototype.fields;
14857 for(var i = 0; i < root.length; i++){
14860 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14861 for(var j = 0, jlen = fields.length; j < jlen; j++){
14862 var f = fields.items[j];
14863 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14864 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14866 values[f.name] = v;
14868 var record = new recordType(values, id);
14870 records[records.length] = record;
14874 totalRecords : records.length
14877 // used when loading children.. @see loadDataFromChildren
14878 toLoadData: function(rec)
14880 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14881 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14892 * @class Roo.bootstrap.ComboBox
14893 * @extends Roo.bootstrap.TriggerField
14894 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14895 * @cfg {Boolean} append (true|false) default false
14896 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14897 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14898 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14899 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14900 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14901 * @cfg {Boolean} animate default true
14902 * @cfg {Boolean} emptyResultText only for touch device
14903 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14904 * @cfg {String} emptyTitle default ''
14905 * @cfg {Number} width fixed with? experimental
14907 * Create a new ComboBox.
14908 * @param {Object} config Configuration options
14910 Roo.bootstrap.ComboBox = function(config){
14911 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915 * Fires when the dropdown list is expanded
14916 * @param {Roo.bootstrap.ComboBox} combo This combo box
14921 * Fires when the dropdown list is collapsed
14922 * @param {Roo.bootstrap.ComboBox} combo This combo box
14926 * @event beforeselect
14927 * Fires before a list item is selected. Return false to cancel the selection.
14928 * @param {Roo.bootstrap.ComboBox} combo This combo box
14929 * @param {Roo.data.Record} record The data record returned from the underlying store
14930 * @param {Number} index The index of the selected item in the dropdown list
14932 'beforeselect' : true,
14935 * Fires when a list item is selected
14936 * @param {Roo.bootstrap.ComboBox} combo This combo box
14937 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14938 * @param {Number} index The index of the selected item in the dropdown list
14942 * @event beforequery
14943 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14944 * The event object passed has these properties:
14945 * @param {Roo.bootstrap.ComboBox} combo This combo box
14946 * @param {String} query The query
14947 * @param {Boolean} forceAll true to force "all" query
14948 * @param {Boolean} cancel true to cancel the query
14949 * @param {Object} e The query event object
14951 'beforequery': true,
14954 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14955 * @param {Roo.bootstrap.ComboBox} combo This combo box
14960 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14961 * @param {Roo.bootstrap.ComboBox} combo This combo box
14962 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14967 * Fires when the remove value from the combobox array
14968 * @param {Roo.bootstrap.ComboBox} combo This combo box
14972 * @event afterremove
14973 * Fires when the remove value from the combobox array
14974 * @param {Roo.bootstrap.ComboBox} combo This combo box
14976 'afterremove' : true,
14978 * @event specialfilter
14979 * Fires when specialfilter
14980 * @param {Roo.bootstrap.ComboBox} combo This combo box
14982 'specialfilter' : true,
14985 * Fires when tick the element
14986 * @param {Roo.bootstrap.ComboBox} combo This combo box
14990 * @event touchviewdisplay
14991 * Fires when touch view require special display (default is using displayField)
14992 * @param {Roo.bootstrap.ComboBox} combo This combo box
14993 * @param {Object} cfg set html .
14995 'touchviewdisplay' : true
15000 this.tickItems = [];
15002 this.selectedIndex = -1;
15003 if(this.mode == 'local'){
15004 if(config.queryDelay === undefined){
15005 this.queryDelay = 10;
15007 if(config.minChars === undefined){
15013 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15016 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15017 * rendering into an Roo.Editor, defaults to false)
15020 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15021 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15024 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15027 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15028 * the dropdown list (defaults to undefined, with no header element)
15032 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15036 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15038 listWidth: undefined,
15040 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15041 * mode = 'remote' or 'text' if mode = 'local')
15043 displayField: undefined,
15046 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15047 * mode = 'remote' or 'value' if mode = 'local').
15048 * Note: use of a valueField requires the user make a selection
15049 * in order for a value to be mapped.
15051 valueField: undefined,
15053 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15058 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15059 * field's data value (defaults to the underlying DOM element's name)
15061 hiddenName: undefined,
15063 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15069 selectedClass: 'active',
15072 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15077 * anchor positions (defaults to 'tl-bl')
15079 listAlign: 'tl-bl?',
15081 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15086 * query specified by the allQuery config option (defaults to 'query')
15088 triggerAction: 'query',
15090 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15091 * (defaults to 4, does not apply if editable = false)
15095 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15096 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15101 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15106 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15110 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15111 * when editable = true (defaults to false)
15113 selectOnFocus:false,
15115 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15117 queryParam: 'query',
15119 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15120 * when mode = 'remote' (defaults to 'Loading...')
15122 loadingText: 'Loading...',
15124 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15133 * traditional select (defaults to true)
15137 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15146 * listWidth has a higher value)
15150 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15151 * allow the user to set arbitrary text into the field (defaults to false)
15153 forceSelection:false,
15155 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15156 * if typeAhead = true (defaults to 250)
15158 typeAheadDelay : 250,
15160 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15161 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15163 valueNotFoundText : undefined,
15165 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15167 blockFocus : false,
15170 * @cfg {Boolean} disableClear Disable showing of clear button.
15172 disableClear : false,
15174 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15176 alwaysQuery : false,
15179 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15184 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15186 invalidClass : "has-warning",
15189 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15191 validClass : "has-success",
15194 * @cfg {Boolean} specialFilter (true|false) special filter default false
15196 specialFilter : false,
15199 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15201 mobileTouchView : true,
15204 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15206 useNativeIOS : false,
15209 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15211 mobile_restrict_height : false,
15213 ios_options : false,
15225 btnPosition : 'right',
15226 triggerList : true,
15227 showToggleBtn : true,
15229 emptyResultText: 'Empty',
15230 triggerText : 'Select',
15234 // element that contains real text value.. (when hidden is used..)
15236 getAutoCreate : function()
15241 * Render classic select for iso
15244 if(Roo.isIOS && this.useNativeIOS){
15245 cfg = this.getAutoCreateNativeIOS();
15253 if(Roo.isTouch && this.mobileTouchView){
15254 cfg = this.getAutoCreateTouchView();
15261 if(!this.tickable){
15262 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15267 * ComboBox with tickable selections
15270 var align = this.labelAlign || this.parentLabelAlign();
15273 cls : 'form-group roo-combobox-tickable' //input-group
15276 var btn_text_select = '';
15277 var btn_text_done = '';
15278 var btn_text_cancel = '';
15280 if (this.btn_text_show) {
15281 btn_text_select = 'Select';
15282 btn_text_done = 'Done';
15283 btn_text_cancel = 'Cancel';
15288 cls : 'tickable-buttons',
15293 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15294 //html : this.triggerText
15295 html: btn_text_select
15301 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15303 html: btn_text_done
15309 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15311 html: btn_text_cancel
15317 buttons.cn.unshift({
15319 cls: 'roo-select2-search-field-input'
15325 Roo.each(buttons.cn, function(c){
15327 c.cls += ' btn-' + _this.size;
15330 if (_this.disabled) {
15337 style : 'display: contents',
15342 cls: 'form-hidden-field'
15346 cls: 'roo-select2-choices',
15350 cls: 'roo-select2-search-field',
15361 cls: 'roo-select2-container input-group roo-select2-container-multi',
15367 // cls: 'typeahead typeahead-long dropdown-menu',
15368 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15373 if(this.hasFeedback && !this.allowBlank){
15377 cls: 'glyphicon form-control-feedback'
15380 combobox.cn.push(feedback);
15387 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15388 tooltip : 'This field is required'
15390 if (Roo.bootstrap.version == 4) {
15393 style : 'display:none'
15396 if (align ==='left' && this.fieldLabel.length) {
15398 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15405 cls : 'control-label col-form-label',
15406 html : this.fieldLabel
15418 var labelCfg = cfg.cn[1];
15419 var contentCfg = cfg.cn[2];
15422 if(this.indicatorpos == 'right'){
15428 cls : 'control-label col-form-label',
15432 html : this.fieldLabel
15448 labelCfg = cfg.cn[0];
15449 contentCfg = cfg.cn[1];
15453 if(this.labelWidth > 12){
15454 labelCfg.style = "width: " + this.labelWidth + 'px';
15456 if(this.width * 1 > 0){
15457 contentCfg.style = "width: " + this.width + 'px';
15459 if(this.labelWidth < 13 && this.labelmd == 0){
15460 this.labelmd = this.labelWidth;
15463 if(this.labellg > 0){
15464 labelCfg.cls += ' col-lg-' + this.labellg;
15465 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15468 if(this.labelmd > 0){
15469 labelCfg.cls += ' col-md-' + this.labelmd;
15470 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15473 if(this.labelsm > 0){
15474 labelCfg.cls += ' col-sm-' + this.labelsm;
15475 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15478 if(this.labelxs > 0){
15479 labelCfg.cls += ' col-xs-' + this.labelxs;
15480 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484 } else if ( this.fieldLabel.length) {
15485 // Roo.log(" label");
15490 //cls : 'input-group-addon',
15491 html : this.fieldLabel
15496 if(this.indicatorpos == 'right'){
15500 //cls : 'input-group-addon',
15501 html : this.fieldLabel
15511 // Roo.log(" no label && no align");
15518 ['xs','sm','md','lg'].map(function(size){
15519 if (settings[size]) {
15520 cfg.cls += ' col-' + size + '-' + settings[size];
15528 _initEventsCalled : false,
15531 initEvents: function()
15533 if (this._initEventsCalled) { // as we call render... prevent looping...
15536 this._initEventsCalled = true;
15539 throw "can not find store for combo";
15542 this.indicator = this.indicatorEl();
15544 this.store = Roo.factory(this.store, Roo.data);
15545 this.store.parent = this;
15547 // if we are building from html. then this element is so complex, that we can not really
15548 // use the rendered HTML.
15549 // so we have to trash and replace the previous code.
15550 if (Roo.XComponent.build_from_html) {
15551 // remove this element....
15552 var e = this.el.dom, k=0;
15553 while (e ) { e = e.previousSibling; ++k;}
15558 this.rendered = false;
15560 this.render(this.parent().getChildContainer(true), k);
15563 if(Roo.isIOS && this.useNativeIOS){
15564 this.initIOSView();
15572 if(Roo.isTouch && this.mobileTouchView){
15573 this.initTouchView();
15578 this.initTickableEvents();
15582 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15584 if(this.hiddenName){
15586 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15588 this.hiddenField.dom.value =
15589 this.hiddenValue !== undefined ? this.hiddenValue :
15590 this.value !== undefined ? this.value : '';
15592 // prevent input submission
15593 this.el.dom.removeAttribute('name');
15594 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15599 // this.el.dom.setAttribute('autocomplete', 'off');
15602 var cls = 'x-combo-list';
15604 //this.list = new Roo.Layer({
15605 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15611 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15612 _this.list.setWidth(lw);
15615 this.list.on('mouseover', this.onViewOver, this);
15616 this.list.on('mousemove', this.onViewMove, this);
15617 this.list.on('scroll', this.onViewScroll, this);
15620 this.list.swallowEvent('mousewheel');
15621 this.assetHeight = 0;
15624 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15625 this.assetHeight += this.header.getHeight();
15628 this.innerList = this.list.createChild({cls:cls+'-inner'});
15629 this.innerList.on('mouseover', this.onViewOver, this);
15630 this.innerList.on('mousemove', this.onViewMove, this);
15631 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15633 if(this.allowBlank && !this.pageSize && !this.disableClear){
15634 this.footer = this.list.createChild({cls:cls+'-ft'});
15635 this.pageTb = new Roo.Toolbar(this.footer);
15639 this.footer = this.list.createChild({cls:cls+'-ft'});
15640 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15641 {pageSize: this.pageSize});
15645 if (this.pageTb && this.allowBlank && !this.disableClear) {
15647 this.pageTb.add(new Roo.Toolbar.Fill(), {
15648 cls: 'x-btn-icon x-btn-clear',
15650 handler: function()
15653 _this.clearValue();
15654 _this.onSelect(false, -1);
15659 this.assetHeight += this.footer.getHeight();
15664 this.tpl = Roo.bootstrap.version == 4 ?
15665 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15666 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15669 this.view = new Roo.View(this.list, this.tpl, {
15670 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15672 //this.view.wrapEl.setDisplayed(false);
15673 this.view.on('click', this.onViewClick, this);
15676 this.store.on('beforeload', this.onBeforeLoad, this);
15677 this.store.on('load', this.onLoad, this);
15678 this.store.on('loadexception', this.onLoadException, this);
15680 if(this.resizable){
15681 this.resizer = new Roo.Resizable(this.list, {
15682 pinned:true, handles:'se'
15684 this.resizer.on('resize', function(r, w, h){
15685 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15686 this.listWidth = w;
15687 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15688 this.restrictHeight();
15690 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15693 if(!this.editable){
15694 this.editable = true;
15695 this.setEditable(false);
15700 if (typeof(this.events.add.listeners) != 'undefined') {
15702 this.addicon = this.wrap.createChild(
15703 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15705 this.addicon.on('click', function(e) {
15706 this.fireEvent('add', this);
15709 if (typeof(this.events.edit.listeners) != 'undefined') {
15711 this.editicon = this.wrap.createChild(
15712 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15713 if (this.addicon) {
15714 this.editicon.setStyle('margin-left', '40px');
15716 this.editicon.on('click', function(e) {
15718 // we fire even if inothing is selected..
15719 this.fireEvent('edit', this, this.lastData );
15725 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15726 "up" : function(e){
15727 this.inKeyMode = true;
15731 "down" : function(e){
15732 if(!this.isExpanded()){
15733 this.onTriggerClick();
15735 this.inKeyMode = true;
15740 "enter" : function(e){
15741 // this.onViewClick();
15745 if(this.fireEvent("specialkey", this, e)){
15746 this.onViewClick(false);
15752 "esc" : function(e){
15756 "tab" : function(e){
15759 if(this.fireEvent("specialkey", this, e)){
15760 this.onViewClick(false);
15768 doRelay : function(foo, bar, hname){
15769 if(hname == 'down' || this.scope.isExpanded()){
15770 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15779 this.queryDelay = Math.max(this.queryDelay || 10,
15780 this.mode == 'local' ? 10 : 250);
15783 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15785 if(this.typeAhead){
15786 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788 if(this.editable !== false){
15789 this.inputEl().on("keyup", this.onKeyUp, this);
15791 if(this.forceSelection){
15792 this.inputEl().on('blur', this.doForce, this);
15796 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15797 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801 initTickableEvents: function()
15805 if(this.hiddenName){
15807 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15809 this.hiddenField.dom.value =
15810 this.hiddenValue !== undefined ? this.hiddenValue :
15811 this.value !== undefined ? this.value : '';
15813 // prevent input submission
15814 this.el.dom.removeAttribute('name');
15815 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15820 // this.list = this.el.select('ul.dropdown-menu',true).first();
15822 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15824 if(this.triggerList){
15825 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15828 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15829 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15831 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15832 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15834 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15835 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15837 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15838 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15839 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842 this.cancelBtn.hide();
15847 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15848 _this.list.setWidth(lw);
15851 this.list.on('mouseover', this.onViewOver, this);
15852 this.list.on('mousemove', this.onViewMove, this);
15854 this.list.on('scroll', this.onViewScroll, this);
15857 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15858 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15861 this.view = new Roo.View(this.list, this.tpl, {
15866 selectedClass: this.selectedClass
15869 //this.view.wrapEl.setDisplayed(false);
15870 this.view.on('click', this.onViewClick, this);
15874 this.store.on('beforeload', this.onBeforeLoad, this);
15875 this.store.on('load', this.onLoad, this);
15876 this.store.on('loadexception', this.onLoadException, this);
15879 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15880 "up" : function(e){
15881 this.inKeyMode = true;
15885 "down" : function(e){
15886 this.inKeyMode = true;
15890 "enter" : function(e){
15891 if(this.fireEvent("specialkey", this, e)){
15892 this.onViewClick(false);
15898 "esc" : function(e){
15899 this.onTickableFooterButtonClick(e, false, false);
15902 "tab" : function(e){
15903 this.fireEvent("specialkey", this, e);
15905 this.onTickableFooterButtonClick(e, false, false);
15912 doRelay : function(e, fn, key){
15913 if(this.scope.isExpanded()){
15914 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15923 this.queryDelay = Math.max(this.queryDelay || 10,
15924 this.mode == 'local' ? 10 : 250);
15927 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15929 if(this.typeAhead){
15930 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15933 if(this.editable !== false){
15934 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15937 this.indicator = this.indicatorEl();
15939 if(this.indicator){
15940 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15941 this.indicator.hide();
15946 onDestroy : function(){
15948 this.view.setStore(null);
15949 this.view.el.removeAllListeners();
15950 this.view.el.remove();
15951 this.view.purgeListeners();
15954 this.list.dom.innerHTML = '';
15958 this.store.un('beforeload', this.onBeforeLoad, this);
15959 this.store.un('load', this.onLoad, this);
15960 this.store.un('loadexception', this.onLoadException, this);
15962 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966 fireKey : function(e){
15967 if(e.isNavKeyPress() && !this.list.isVisible()){
15968 this.fireEvent("specialkey", this, e);
15973 onResize: function(w, h)
15977 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15979 // if(typeof w != 'number'){
15980 // // we do not handle it!?!?
15983 // var tw = this.trigger.getWidth();
15984 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15985 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15987 // this.inputEl().setWidth( this.adjustWidth('input', x));
15989 // //this.trigger.setStyle('left', x+'px');
15991 // if(this.list && this.listWidth === undefined){
15992 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15993 // this.list.setWidth(lw);
15994 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16002 * Allow or prevent the user from directly editing the field text. If false is passed,
16003 * the user will only be able to select from the items defined in the dropdown list. This method
16004 * is the runtime equivalent of setting the 'editable' config option at config time.
16005 * @param {Boolean} value True to allow the user to directly edit the field text
16007 setEditable : function(value){
16008 if(value == this.editable){
16011 this.editable = value;
16013 this.inputEl().dom.setAttribute('readOnly', true);
16014 this.inputEl().on('mousedown', this.onTriggerClick, this);
16015 this.inputEl().addClass('x-combo-noedit');
16017 this.inputEl().dom.setAttribute('readOnly', false);
16018 this.inputEl().un('mousedown', this.onTriggerClick, this);
16019 this.inputEl().removeClass('x-combo-noedit');
16025 onBeforeLoad : function(combo,opts){
16026 if(!this.hasFocus){
16030 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16032 this.restrictHeight();
16033 this.selectedIndex = -1;
16037 onLoad : function(){
16039 this.hasQuery = false;
16041 if(!this.hasFocus){
16045 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16046 this.loading.hide();
16049 if(this.store.getCount() > 0){
16052 this.restrictHeight();
16053 if(this.lastQuery == this.allQuery){
16054 if(this.editable && !this.tickable){
16055 this.inputEl().dom.select();
16059 !this.selectByValue(this.value, true) &&
16062 !this.store.lastOptions ||
16063 typeof(this.store.lastOptions.add) == 'undefined' ||
16064 this.store.lastOptions.add != true
16067 this.select(0, true);
16070 if(this.autoFocus){
16073 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16074 this.taTask.delay(this.typeAheadDelay);
16078 this.onEmptyResults();
16084 onLoadException : function()
16086 this.hasQuery = false;
16088 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16089 this.loading.hide();
16092 if(this.tickable && this.editable){
16097 // only causes errors at present
16098 //Roo.log(this.store.reader.jsonData);
16099 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16101 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16107 onTypeAhead : function(){
16108 if(this.store.getCount() > 0){
16109 var r = this.store.getAt(0);
16110 var newValue = r.data[this.displayField];
16111 var len = newValue.length;
16112 var selStart = this.getRawValue().length;
16114 if(selStart != len){
16115 this.setRawValue(newValue);
16116 this.selectText(selStart, newValue.length);
16122 onSelect : function(record, index){
16124 if(this.fireEvent('beforeselect', this, record, index) !== false){
16126 this.setFromData(index > -1 ? record.data : false);
16129 this.fireEvent('select', this, record, index);
16134 * Returns the currently selected field value or empty string if no value is set.
16135 * @return {String} value The selected value
16137 getValue : function()
16139 if(Roo.isIOS && this.useNativeIOS){
16140 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16147 if(this.valueField){
16148 return typeof this.value != 'undefined' ? this.value : '';
16150 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154 getRawValue : function()
16156 if(Roo.isIOS && this.useNativeIOS){
16157 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16160 var v = this.inputEl().getValue();
16166 * Clears any text/value currently set in the field
16168 clearValue : function(){
16170 if(this.hiddenField){
16171 this.hiddenField.dom.value = '';
16174 this.setRawValue('');
16175 this.lastSelectionText = '';
16176 this.lastData = false;
16178 var close = this.closeTriggerEl();
16189 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16190 * will be displayed in the field. If the value does not match the data value of an existing item,
16191 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16192 * Otherwise the field will be blank (although the value will still be set).
16193 * @param {String} value The value to match
16195 setValue : function(v)
16197 if(Roo.isIOS && this.useNativeIOS){
16198 this.setIOSValue(v);
16208 if(this.valueField){
16209 var r = this.findRecord(this.valueField, v);
16211 text = r.data[this.displayField];
16212 }else if(this.valueNotFoundText !== undefined){
16213 text = this.valueNotFoundText;
16216 this.lastSelectionText = text;
16217 if(this.hiddenField){
16218 this.hiddenField.dom.value = v;
16220 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16223 var close = this.closeTriggerEl();
16226 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16232 * @property {Object} the last set data for the element
16237 * Sets the value of the field based on a object which is related to the record format for the store.
16238 * @param {Object} value the value to set as. or false on reset?
16240 setFromData : function(o){
16247 var dv = ''; // display value
16248 var vv = ''; // value value..
16250 if (this.displayField) {
16251 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16253 // this is an error condition!!!
16254 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16257 if(this.valueField){
16258 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16261 var close = this.closeTriggerEl();
16264 if(dv.length || vv * 1 > 0){
16266 this.blockFocus=true;
16272 if(this.hiddenField){
16273 this.hiddenField.dom.value = vv;
16275 this.lastSelectionText = dv;
16276 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280 // no hidden field.. - we store the value in 'value', but still display
16281 // display field!!!!
16282 this.lastSelectionText = dv;
16283 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16290 reset : function(){
16291 // overridden so that last data is reset..
16298 this.setValue(this.originalValue);
16299 //this.clearInvalid();
16300 this.lastData = false;
16302 this.view.clearSelections();
16308 findRecord : function(prop, value){
16310 if(this.store.getCount() > 0){
16311 this.store.each(function(r){
16312 if(r.data[prop] == value){
16322 getName: function()
16324 // returns hidden if it's set..
16325 if (!this.rendered) {return ''};
16326 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16330 onViewMove : function(e, t){
16331 this.inKeyMode = false;
16335 onViewOver : function(e, t){
16336 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16339 var item = this.view.findItemFromChild(t);
16342 var index = this.view.indexOf(item);
16343 this.select(index, false);
16348 onViewClick : function(view, doFocus, el, e)
16350 var index = this.view.getSelectedIndexes()[0];
16352 var r = this.store.getAt(index);
16356 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16363 Roo.each(this.tickItems, function(v,k){
16365 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16367 _this.tickItems.splice(k, 1);
16369 if(typeof(e) == 'undefined' && view == false){
16370 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16382 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16383 this.tickItems.push(r.data);
16386 if(typeof(e) == 'undefined' && view == false){
16387 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16394 this.onSelect(r, index);
16396 if(doFocus !== false && !this.blockFocus){
16397 this.inputEl().focus();
16402 restrictHeight : function(){
16403 //this.innerList.dom.style.height = '';
16404 //var inner = this.innerList.dom;
16405 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16406 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16407 //this.list.beginUpdate();
16408 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16409 this.list.alignTo(this.inputEl(), this.listAlign);
16410 this.list.alignTo(this.inputEl(), this.listAlign);
16411 //this.list.endUpdate();
16415 onEmptyResults : function(){
16417 if(this.tickable && this.editable){
16418 this.hasFocus = false;
16419 this.restrictHeight();
16427 * Returns true if the dropdown list is expanded, else false.
16429 isExpanded : function(){
16430 return this.list.isVisible();
16434 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16435 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16436 * @param {String} value The data value of the item to select
16437 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16438 * selected item if it is not currently in view (defaults to true)
16439 * @return {Boolean} True if the value matched an item in the list, else false
16441 selectByValue : function(v, scrollIntoView){
16442 if(v !== undefined && v !== null){
16443 var r = this.findRecord(this.valueField || this.displayField, v);
16445 this.select(this.store.indexOf(r), scrollIntoView);
16453 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16454 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16455 * @param {Number} index The zero-based index of the list item to select
16456 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16457 * selected item if it is not currently in view (defaults to true)
16459 select : function(index, scrollIntoView){
16460 this.selectedIndex = index;
16461 this.view.select(index);
16462 if(scrollIntoView !== false){
16463 var el = this.view.getNode(index);
16465 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16468 this.list.scrollChildIntoView(el, false);
16474 selectNext : function(){
16475 var ct = this.store.getCount();
16477 if(this.selectedIndex == -1){
16479 }else if(this.selectedIndex < ct-1){
16480 this.select(this.selectedIndex+1);
16486 selectPrev : function(){
16487 var ct = this.store.getCount();
16489 if(this.selectedIndex == -1){
16491 }else if(this.selectedIndex != 0){
16492 this.select(this.selectedIndex-1);
16498 onKeyUp : function(e){
16499 if(this.editable !== false && !e.isSpecialKey()){
16500 this.lastKey = e.getKey();
16501 this.dqTask.delay(this.queryDelay);
16506 validateBlur : function(){
16507 return !this.list || !this.list.isVisible();
16511 initQuery : function(){
16513 var v = this.getRawValue();
16515 if(this.tickable && this.editable){
16516 v = this.tickableInputEl().getValue();
16523 doForce : function(){
16524 if(this.inputEl().dom.value.length > 0){
16525 this.inputEl().dom.value =
16526 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16532 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16533 * query allowing the query action to be canceled if needed.
16534 * @param {String} query The SQL query to execute
16535 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16536 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16537 * saved in the current store (defaults to false)
16539 doQuery : function(q, forceAll){
16541 if(q === undefined || q === null){
16546 forceAll: forceAll,
16550 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16555 forceAll = qe.forceAll;
16556 if(forceAll === true || (q.length >= this.minChars)){
16558 this.hasQuery = true;
16560 if(this.lastQuery != q || this.alwaysQuery){
16561 this.lastQuery = q;
16562 if(this.mode == 'local'){
16563 this.selectedIndex = -1;
16565 this.store.clearFilter();
16568 if(this.specialFilter){
16569 this.fireEvent('specialfilter', this);
16574 this.store.filter(this.displayField, q);
16577 this.store.fireEvent("datachanged", this.store);
16584 this.store.baseParams[this.queryParam] = q;
16586 var options = {params : this.getParams(q)};
16589 options.add = true;
16590 options.params.start = this.page * this.pageSize;
16593 this.store.load(options);
16596 * this code will make the page width larger, at the beginning, the list not align correctly,
16597 * we should expand the list on onLoad
16598 * so command out it
16603 this.selectedIndex = -1;
16608 this.loadNext = false;
16612 getParams : function(q){
16614 //p[this.queryParam] = q;
16618 p.limit = this.pageSize;
16624 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16626 collapse : function(){
16627 if(!this.isExpanded()){
16633 this.hasFocus = false;
16637 this.cancelBtn.hide();
16638 this.trigger.show();
16641 this.tickableInputEl().dom.value = '';
16642 this.tickableInputEl().blur();
16647 Roo.get(document).un('mousedown', this.collapseIf, this);
16648 Roo.get(document).un('mousewheel', this.collapseIf, this);
16649 if (!this.editable) {
16650 Roo.get(document).un('keydown', this.listKeyPress, this);
16652 this.fireEvent('collapse', this);
16658 collapseIf : function(e){
16659 var in_combo = e.within(this.el);
16660 var in_list = e.within(this.list);
16661 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16663 if (in_combo || in_list || is_list) {
16664 //e.stopPropagation();
16669 this.onTickableFooterButtonClick(e, false, false);
16677 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16679 expand : function(){
16681 if(this.isExpanded() || !this.hasFocus){
16685 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16686 this.list.setWidth(lw);
16692 this.restrictHeight();
16696 this.tickItems = Roo.apply([], this.item);
16699 this.cancelBtn.show();
16700 this.trigger.hide();
16703 this.tickableInputEl().focus();
16708 Roo.get(document).on('mousedown', this.collapseIf, this);
16709 Roo.get(document).on('mousewheel', this.collapseIf, this);
16710 if (!this.editable) {
16711 Roo.get(document).on('keydown', this.listKeyPress, this);
16714 this.fireEvent('expand', this);
16718 // Implements the default empty TriggerField.onTriggerClick function
16719 onTriggerClick : function(e)
16721 Roo.log('trigger click');
16723 if(this.disabled || !this.triggerList){
16728 this.loadNext = false;
16730 if(this.isExpanded()){
16732 if (!this.blockFocus) {
16733 this.inputEl().focus();
16737 this.hasFocus = true;
16738 if(this.triggerAction == 'all') {
16739 this.doQuery(this.allQuery, true);
16741 this.doQuery(this.getRawValue());
16743 if (!this.blockFocus) {
16744 this.inputEl().focus();
16749 onTickableTriggerClick : function(e)
16756 this.loadNext = false;
16757 this.hasFocus = true;
16759 if(this.triggerAction == 'all') {
16760 this.doQuery(this.allQuery, true);
16762 this.doQuery(this.getRawValue());
16766 onSearchFieldClick : function(e)
16768 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16769 this.onTickableFooterButtonClick(e, false, false);
16773 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16778 this.loadNext = false;
16779 this.hasFocus = true;
16781 if(this.triggerAction == 'all') {
16782 this.doQuery(this.allQuery, true);
16784 this.doQuery(this.getRawValue());
16788 listKeyPress : function(e)
16790 //Roo.log('listkeypress');
16791 // scroll to first matching element based on key pres..
16792 if (e.isSpecialKey()) {
16795 var k = String.fromCharCode(e.getKey()).toUpperCase();
16798 var csel = this.view.getSelectedNodes();
16799 var cselitem = false;
16801 var ix = this.view.indexOf(csel[0]);
16802 cselitem = this.store.getAt(ix);
16803 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16809 this.store.each(function(v) {
16811 // start at existing selection.
16812 if (cselitem.id == v.id) {
16818 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16819 match = this.store.indexOf(v);
16825 if (match === false) {
16826 return true; // no more action?
16829 this.view.select(match);
16830 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16831 sn.scrollIntoView(sn.dom.parentNode, false);
16834 onViewScroll : function(e, t){
16836 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){
16840 this.hasQuery = true;
16842 this.loading = this.list.select('.loading', true).first();
16844 if(this.loading === null){
16845 this.list.createChild({
16847 cls: 'loading roo-select2-more-results roo-select2-active',
16848 html: 'Loading more results...'
16851 this.loading = this.list.select('.loading', true).first();
16853 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16855 this.loading.hide();
16858 this.loading.show();
16863 this.loadNext = true;
16865 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16870 addItem : function(o)
16872 var dv = ''; // display value
16874 if (this.displayField) {
16875 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16877 // this is an error condition!!!
16878 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16885 var choice = this.choices.createChild({
16887 cls: 'roo-select2-search-choice',
16896 cls: 'roo-select2-search-choice-close fa fa-times',
16901 }, this.searchField);
16903 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16905 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16913 this.inputEl().dom.value = '';
16918 onRemoveItem : function(e, _self, o)
16920 e.preventDefault();
16922 this.lastItem = Roo.apply([], this.item);
16924 var index = this.item.indexOf(o.data) * 1;
16927 Roo.log('not this item?!');
16931 this.item.splice(index, 1);
16936 this.fireEvent('remove', this, e);
16942 syncValue : function()
16944 if(!this.item.length){
16951 Roo.each(this.item, function(i){
16952 if(_this.valueField){
16953 value.push(i[_this.valueField]);
16960 this.value = value.join(',');
16962 if(this.hiddenField){
16963 this.hiddenField.dom.value = this.value;
16966 this.store.fireEvent("datachanged", this.store);
16971 clearItem : function()
16973 if(!this.multiple){
16979 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16987 if(this.tickable && !Roo.isTouch){
16988 this.view.refresh();
16992 inputEl: function ()
16994 if(Roo.isIOS && this.useNativeIOS){
16995 return this.el.select('select.roo-ios-select', true).first();
16998 if(Roo.isTouch && this.mobileTouchView){
16999 return this.el.select('input.form-control',true).first();
17003 return this.searchField;
17006 return this.el.select('input.form-control',true).first();
17009 onTickableFooterButtonClick : function(e, btn, el)
17011 e.preventDefault();
17013 this.lastItem = Roo.apply([], this.item);
17015 if(btn && btn.name == 'cancel'){
17016 this.tickItems = Roo.apply([], this.item);
17025 Roo.each(this.tickItems, function(o){
17033 validate : function()
17035 if(this.getVisibilityEl().hasClass('hidden')){
17039 var v = this.getRawValue();
17042 v = this.getValue();
17045 if(this.disabled || this.allowBlank || v.length){
17050 this.markInvalid();
17054 tickableInputEl : function()
17056 if(!this.tickable || !this.editable){
17057 return this.inputEl();
17060 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064 getAutoCreateTouchView : function()
17069 cls: 'form-group' //input-group
17075 type : this.inputType,
17076 cls : 'form-control x-combo-noedit',
17077 autocomplete: 'new-password',
17078 placeholder : this.placeholder || '',
17083 input.name = this.name;
17087 input.cls += ' input-' + this.size;
17090 if (this.disabled) {
17091 input.disabled = true;
17095 cls : 'roo-combobox-wrap',
17102 inputblock.cls += ' input-group';
17104 inputblock.cn.unshift({
17106 cls : 'input-group-addon input-group-prepend input-group-text',
17111 if(this.removable && !this.multiple){
17112 inputblock.cls += ' roo-removable';
17114 inputblock.cn.push({
17117 cls : 'roo-combo-removable-btn close'
17121 if(this.hasFeedback && !this.allowBlank){
17123 inputblock.cls += ' has-feedback';
17125 inputblock.cn.push({
17127 cls: 'glyphicon form-control-feedback'
17134 inputblock.cls += (this.before) ? '' : ' input-group';
17136 inputblock.cn.push({
17138 cls : 'input-group-addon input-group-append input-group-text',
17144 var ibwrap = inputblock;
17149 cls: 'roo-select2-choices',
17153 cls: 'roo-select2-search-field',
17166 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17171 cls: 'form-hidden-field'
17177 if(!this.multiple && this.showToggleBtn){
17183 if (this.caret != false) {
17186 cls: 'fa fa-' + this.caret
17193 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17195 Roo.bootstrap.version == 3 ? caret : '',
17198 cls: 'combobox-clear',
17212 combobox.cls += ' roo-select2-container-multi';
17215 var align = this.labelAlign || this.parentLabelAlign();
17217 if (align ==='left' && this.fieldLabel.length) {
17222 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17223 tooltip : 'This field is required'
17227 cls : 'control-label col-form-label',
17228 html : this.fieldLabel
17232 cls : 'roo-combobox-wrap ',
17239 var labelCfg = cfg.cn[1];
17240 var contentCfg = cfg.cn[2];
17243 if(this.indicatorpos == 'right'){
17248 cls : 'control-label col-form-label',
17252 html : this.fieldLabel
17256 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17257 tooltip : 'This field is required'
17262 cls : "roo-combobox-wrap ",
17270 labelCfg = cfg.cn[0];
17271 contentCfg = cfg.cn[1];
17276 if(this.labelWidth > 12){
17277 labelCfg.style = "width: " + this.labelWidth + 'px';
17280 if(this.labelWidth < 13 && this.labelmd == 0){
17281 this.labelmd = this.labelWidth;
17284 if(this.labellg > 0){
17285 labelCfg.cls += ' col-lg-' + this.labellg;
17286 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17289 if(this.labelmd > 0){
17290 labelCfg.cls += ' col-md-' + this.labelmd;
17291 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17294 if(this.labelsm > 0){
17295 labelCfg.cls += ' col-sm-' + this.labelsm;
17296 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17299 if(this.labelxs > 0){
17300 labelCfg.cls += ' col-xs-' + this.labelxs;
17301 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305 } else if ( this.fieldLabel.length) {
17309 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17310 tooltip : 'This field is required'
17314 cls : 'control-label',
17315 html : this.fieldLabel
17326 if(this.indicatorpos == 'right'){
17330 cls : 'control-label',
17331 html : this.fieldLabel,
17335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17336 tooltip : 'This field is required'
17353 var settings = this;
17355 ['xs','sm','md','lg'].map(function(size){
17356 if (settings[size]) {
17357 cfg.cls += ' col-' + size + '-' + settings[size];
17364 initTouchView : function()
17366 this.renderTouchView();
17368 this.touchViewEl.on('scroll', function(){
17369 this.el.dom.scrollTop = 0;
17372 this.originalValue = this.getValue();
17374 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17376 this.inputEl().on("click", this.showTouchView, this);
17377 if (this.triggerEl) {
17378 this.triggerEl.on("click", this.showTouchView, this);
17382 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17383 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17385 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17387 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17388 this.store.on('load', this.onTouchViewLoad, this);
17389 this.store.on('loadexception', this.onTouchViewLoadException, this);
17391 if(this.hiddenName){
17393 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17395 this.hiddenField.dom.value =
17396 this.hiddenValue !== undefined ? this.hiddenValue :
17397 this.value !== undefined ? this.value : '';
17399 this.el.dom.removeAttribute('name');
17400 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17405 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17408 if(this.removable && !this.multiple){
17409 var close = this.closeTriggerEl();
17411 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17412 close.on('click', this.removeBtnClick, this, close);
17416 * fix the bug in Safari iOS8
17418 this.inputEl().on("focus", function(e){
17419 document.activeElement.blur();
17422 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17429 renderTouchView : function()
17431 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17432 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17435 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17438 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439 this.touchViewBodyEl.setStyle('overflow', 'auto');
17441 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17442 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17445 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449 showTouchView : function()
17455 this.touchViewHeaderEl.hide();
17457 if(this.modalTitle.length){
17458 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17459 this.touchViewHeaderEl.show();
17462 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17463 this.touchViewEl.show();
17465 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17467 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17468 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17470 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17472 if(this.modalTitle.length){
17473 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17476 this.touchViewBodyEl.setHeight(bodyHeight);
17480 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17482 this.touchViewEl.addClass(['in','show']);
17485 if(this._touchViewMask){
17486 Roo.get(document.body).addClass("x-body-masked");
17487 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17488 this._touchViewMask.setStyle('z-index', 10000);
17489 this._touchViewMask.addClass('show');
17492 this.doTouchViewQuery();
17496 hideTouchView : function()
17498 this.touchViewEl.removeClass(['in','show']);
17502 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17504 this.touchViewEl.setStyle('display', 'none');
17507 if(this._touchViewMask){
17508 this._touchViewMask.removeClass('show');
17509 Roo.get(document.body).removeClass("x-body-masked");
17513 setTouchViewValue : function()
17520 Roo.each(this.tickItems, function(o){
17525 this.hideTouchView();
17528 doTouchViewQuery : function()
17537 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541 if(!this.alwaysQuery || this.mode == 'local'){
17542 this.onTouchViewLoad();
17549 onTouchViewBeforeLoad : function(combo,opts)
17555 onTouchViewLoad : function()
17557 if(this.store.getCount() < 1){
17558 this.onTouchViewEmptyResults();
17562 this.clearTouchView();
17564 var rawValue = this.getRawValue();
17566 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17568 this.tickItems = [];
17570 this.store.data.each(function(d, rowIndex){
17571 var row = this.touchViewListGroup.createChild(template);
17573 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17574 row.addClass(d.data.cls);
17577 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17580 html : d.data[this.displayField]
17583 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17584 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17587 row.removeClass('selected');
17588 if(!this.multiple && this.valueField &&
17589 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17592 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17593 row.addClass('selected');
17596 if(this.multiple && this.valueField &&
17597 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17602 this.tickItems.push(d.data);
17605 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17611 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17613 if(this.modalTitle.length){
17614 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17617 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17619 if(this.mobile_restrict_height && listHeight < bodyHeight){
17620 this.touchViewBodyEl.setHeight(listHeight);
17625 if(firstChecked && listHeight > bodyHeight){
17626 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17631 onTouchViewLoadException : function()
17633 this.hideTouchView();
17636 onTouchViewEmptyResults : function()
17638 this.clearTouchView();
17640 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17642 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646 clearTouchView : function()
17648 this.touchViewListGroup.dom.innerHTML = '';
17651 onTouchViewClick : function(e, el, o)
17653 e.preventDefault();
17656 var rowIndex = o.rowIndex;
17658 var r = this.store.getAt(rowIndex);
17660 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17662 if(!this.multiple){
17663 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17664 c.dom.removeAttribute('checked');
17667 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17669 this.setFromData(r.data);
17671 var close = this.closeTriggerEl();
17677 this.hideTouchView();
17679 this.fireEvent('select', this, r, rowIndex);
17684 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17685 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17686 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691 this.addItem(r.data);
17692 this.tickItems.push(r.data);
17696 getAutoCreateNativeIOS : function()
17699 cls: 'form-group' //input-group,
17704 cls : 'roo-ios-select'
17708 combobox.name = this.name;
17711 if (this.disabled) {
17712 combobox.disabled = true;
17715 var settings = this;
17717 ['xs','sm','md','lg'].map(function(size){
17718 if (settings[size]) {
17719 cfg.cls += ' col-' + size + '-' + settings[size];
17729 initIOSView : function()
17731 this.store.on('load', this.onIOSViewLoad, this);
17736 onIOSViewLoad : function()
17738 if(this.store.getCount() < 1){
17742 this.clearIOSView();
17744 if(this.allowBlank) {
17746 var default_text = '-- SELECT --';
17748 if(this.placeholder.length){
17749 default_text = this.placeholder;
17752 if(this.emptyTitle.length){
17753 default_text += ' - ' + this.emptyTitle + ' -';
17756 var opt = this.inputEl().createChild({
17759 html : default_text
17763 o[this.valueField] = 0;
17764 o[this.displayField] = default_text;
17766 this.ios_options.push({
17773 this.store.data.each(function(d, rowIndex){
17777 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17778 html = d.data[this.displayField];
17783 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17784 value = d.data[this.valueField];
17793 if(this.value == d.data[this.valueField]){
17794 option['selected'] = true;
17797 var opt = this.inputEl().createChild(option);
17799 this.ios_options.push({
17806 this.inputEl().on('change', function(){
17807 this.fireEvent('select', this);
17812 clearIOSView: function()
17814 this.inputEl().dom.innerHTML = '';
17816 this.ios_options = [];
17819 setIOSValue: function(v)
17823 if(!this.ios_options){
17827 Roo.each(this.ios_options, function(opts){
17829 opts.el.dom.removeAttribute('selected');
17831 if(opts.data[this.valueField] != v){
17835 opts.el.dom.setAttribute('selected', true);
17841 * @cfg {Boolean} grow
17845 * @cfg {Number} growMin
17849 * @cfg {Number} growMax
17858 Roo.apply(Roo.bootstrap.ComboBox, {
17862 cls: 'modal-header',
17884 cls: 'list-group-item',
17888 cls: 'roo-combobox-list-group-item-value'
17892 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17906 listItemCheckbox : {
17908 cls: 'list-group-item',
17912 cls: 'roo-combobox-list-group-item-value'
17916 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17932 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17937 cls: 'modal-footer',
17945 cls: 'col-xs-6 text-left',
17948 cls: 'btn btn-danger roo-touch-view-cancel',
17954 cls: 'col-xs-6 text-right',
17957 cls: 'btn btn-success roo-touch-view-ok',
17968 Roo.apply(Roo.bootstrap.ComboBox, {
17970 touchViewTemplate : {
17972 cls: 'modal fade roo-combobox-touch-view',
17976 cls: 'modal-dialog',
17977 style : 'position:fixed', // we have to fix position....
17981 cls: 'modal-content',
17983 Roo.bootstrap.ComboBox.header,
17984 Roo.bootstrap.ComboBox.body,
17985 Roo.bootstrap.ComboBox.footer
17994 * Ext JS Library 1.1.1
17995 * Copyright(c) 2006-2007, Ext JS, LLC.
17997 * Originally Released Under LGPL - original licence link has changed is not relivant.
18000 * <script type="text/javascript">
18005 * @extends Roo.util.Observable
18006 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18007 * This class also supports single and multi selection modes. <br>
18008 * Create a data model bound view:
18010 var store = new Roo.data.Store(...);
18012 var view = new Roo.View({
18014 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18016 singleSelect: true,
18017 selectedClass: "ydataview-selected",
18021 // listen for node click?
18022 view.on("click", function(vw, index, node, e){
18023 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027 dataModel.load("foobar.xml");
18029 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18031 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18032 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18034 * Note: old style constructor is still suported (container, template, config)
18037 * Create a new View
18038 * @param {Object} config The config object
18041 Roo.View = function(config, depreciated_tpl, depreciated_config){
18043 this.parent = false;
18045 if (typeof(depreciated_tpl) == 'undefined') {
18046 // new way.. - universal constructor.
18047 Roo.apply(this, config);
18048 this.el = Roo.get(this.el);
18051 this.el = Roo.get(config);
18052 this.tpl = depreciated_tpl;
18053 Roo.apply(this, depreciated_config);
18055 this.wrapEl = this.el.wrap().wrap();
18056 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18059 if(typeof(this.tpl) == "string"){
18060 this.tpl = new Roo.Template(this.tpl);
18062 // support xtype ctors..
18063 this.tpl = new Roo.factory(this.tpl, Roo);
18067 this.tpl.compile();
18072 * @event beforeclick
18073 * Fires before a click is processed. Returns false to cancel the default action.
18074 * @param {Roo.View} this
18075 * @param {Number} index The index of the target node
18076 * @param {HTMLElement} node The target node
18077 * @param {Roo.EventObject} e The raw event object
18079 "beforeclick" : true,
18082 * Fires when a template node is clicked.
18083 * @param {Roo.View} this
18084 * @param {Number} index The index of the target node
18085 * @param {HTMLElement} node The target node
18086 * @param {Roo.EventObject} e The raw event object
18091 * Fires when a template node is double clicked.
18092 * @param {Roo.View} this
18093 * @param {Number} index The index of the target node
18094 * @param {HTMLElement} node The target node
18095 * @param {Roo.EventObject} e The raw event object
18099 * @event contextmenu
18100 * Fires when a template node is right clicked.
18101 * @param {Roo.View} this
18102 * @param {Number} index The index of the target node
18103 * @param {HTMLElement} node The target node
18104 * @param {Roo.EventObject} e The raw event object
18106 "contextmenu" : true,
18108 * @event selectionchange
18109 * Fires when the selected nodes change.
18110 * @param {Roo.View} this
18111 * @param {Array} selections Array of the selected nodes
18113 "selectionchange" : true,
18116 * @event beforeselect
18117 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18118 * @param {Roo.View} this
18119 * @param {HTMLElement} node The node to be selected
18120 * @param {Array} selections Array of currently selected nodes
18122 "beforeselect" : true,
18124 * @event preparedata
18125 * Fires on every row to render, to allow you to change the data.
18126 * @param {Roo.View} this
18127 * @param {Object} data to be rendered (change this)
18129 "preparedata" : true
18137 "click": this.onClick,
18138 "dblclick": this.onDblClick,
18139 "contextmenu": this.onContextMenu,
18143 this.selections = [];
18145 this.cmp = new Roo.CompositeElementLite([]);
18147 this.store = Roo.factory(this.store, Roo.data);
18148 this.setStore(this.store, true);
18151 if ( this.footer && this.footer.xtype) {
18153 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18155 this.footer.dataSource = this.store;
18156 this.footer.container = fctr;
18157 this.footer = Roo.factory(this.footer, Roo);
18158 fctr.insertFirst(this.el);
18160 // this is a bit insane - as the paging toolbar seems to detach the el..
18161 // dom.parentNode.parentNode.parentNode
18162 // they get detached?
18166 Roo.View.superclass.constructor.call(this);
18171 Roo.extend(Roo.View, Roo.util.Observable, {
18174 * @cfg {Roo.data.Store} store Data store to load data from.
18179 * @cfg {String|Roo.Element} el The container element.
18184 * @cfg {String|Roo.Template} tpl The template used by this View
18188 * @cfg {String} dataName the named area of the template to use as the data area
18189 * Works with domtemplates roo-name="name"
18193 * @cfg {String} selectedClass The css class to add to selected nodes
18195 selectedClass : "x-view-selected",
18197 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18202 * @cfg {String} text to display on mask (default Loading)
18206 * @cfg {Boolean} multiSelect Allow multiple selection
18208 multiSelect : false,
18210 * @cfg {Boolean} singleSelect Allow single selection
18212 singleSelect: false,
18215 * @cfg {Boolean} toggleSelect - selecting
18217 toggleSelect : false,
18220 * @cfg {Boolean} tickable - selecting
18225 * Returns the element this view is bound to.
18226 * @return {Roo.Element}
18228 getEl : function(){
18229 return this.wrapEl;
18235 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18237 refresh : function(){
18238 //Roo.log('refresh');
18241 // if we are using something like 'domtemplate', then
18242 // the what gets used is:
18243 // t.applySubtemplate(NAME, data, wrapping data..)
18244 // the outer template then get' applied with
18245 // the store 'extra data'
18246 // and the body get's added to the
18247 // roo-name="data" node?
18248 // <span class='roo-tpl-{name}'></span> ?????
18252 this.clearSelections();
18253 this.el.update("");
18255 var records = this.store.getRange();
18256 if(records.length < 1) {
18258 // is this valid?? = should it render a template??
18260 this.el.update(this.emptyText);
18264 if (this.dataName) {
18265 this.el.update(t.apply(this.store.meta)); //????
18266 el = this.el.child('.roo-tpl-' + this.dataName);
18269 for(var i = 0, len = records.length; i < len; i++){
18270 var data = this.prepareData(records[i].data, i, records[i]);
18271 this.fireEvent("preparedata", this, data, i, records[i]);
18273 var d = Roo.apply({}, data);
18276 Roo.apply(d, {'roo-id' : Roo.id()});
18280 Roo.each(this.parent.item, function(item){
18281 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18284 Roo.apply(d, {'roo-data-checked' : 'checked'});
18288 html[html.length] = Roo.util.Format.trim(
18290 t.applySubtemplate(this.dataName, d, this.store.meta) :
18297 el.update(html.join(""));
18298 this.nodes = el.dom.childNodes;
18299 this.updateIndexes(0);
18304 * Function to override to reformat the data that is sent to
18305 * the template for each node.
18306 * DEPRICATED - use the preparedata event handler.
18307 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18308 * a JSON object for an UpdateManager bound view).
18310 prepareData : function(data, index, record)
18312 this.fireEvent("preparedata", this, data, index, record);
18316 onUpdate : function(ds, record){
18317 // Roo.log('on update');
18318 this.clearSelections();
18319 var index = this.store.indexOf(record);
18320 var n = this.nodes[index];
18321 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18322 n.parentNode.removeChild(n);
18323 this.updateIndexes(index, index);
18329 onAdd : function(ds, records, index)
18331 //Roo.log(['on Add', ds, records, index] );
18332 this.clearSelections();
18333 if(this.nodes.length == 0){
18337 var n = this.nodes[index];
18338 for(var i = 0, len = records.length; i < len; i++){
18339 var d = this.prepareData(records[i].data, i, records[i]);
18341 this.tpl.insertBefore(n, d);
18344 this.tpl.append(this.el, d);
18347 this.updateIndexes(index);
18350 onRemove : function(ds, record, index){
18351 // Roo.log('onRemove');
18352 this.clearSelections();
18353 var el = this.dataName ?
18354 this.el.child('.roo-tpl-' + this.dataName) :
18357 el.dom.removeChild(this.nodes[index]);
18358 this.updateIndexes(index);
18362 * Refresh an individual node.
18363 * @param {Number} index
18365 refreshNode : function(index){
18366 this.onUpdate(this.store, this.store.getAt(index));
18369 updateIndexes : function(startIndex, endIndex){
18370 var ns = this.nodes;
18371 startIndex = startIndex || 0;
18372 endIndex = endIndex || ns.length - 1;
18373 for(var i = startIndex; i <= endIndex; i++){
18374 ns[i].nodeIndex = i;
18379 * Changes the data store this view uses and refresh the view.
18380 * @param {Store} store
18382 setStore : function(store, initial){
18383 if(!initial && this.store){
18384 this.store.un("datachanged", this.refresh);
18385 this.store.un("add", this.onAdd);
18386 this.store.un("remove", this.onRemove);
18387 this.store.un("update", this.onUpdate);
18388 this.store.un("clear", this.refresh);
18389 this.store.un("beforeload", this.onBeforeLoad);
18390 this.store.un("load", this.onLoad);
18391 this.store.un("loadexception", this.onLoad);
18395 store.on("datachanged", this.refresh, this);
18396 store.on("add", this.onAdd, this);
18397 store.on("remove", this.onRemove, this);
18398 store.on("update", this.onUpdate, this);
18399 store.on("clear", this.refresh, this);
18400 store.on("beforeload", this.onBeforeLoad, this);
18401 store.on("load", this.onLoad, this);
18402 store.on("loadexception", this.onLoad, this);
18410 * onbeforeLoad - masks the loading area.
18413 onBeforeLoad : function(store,opts)
18415 //Roo.log('onBeforeLoad');
18417 this.el.update("");
18419 this.el.mask(this.mask ? this.mask : "Loading" );
18421 onLoad : function ()
18428 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18429 * @param {HTMLElement} node
18430 * @return {HTMLElement} The template node
18432 findItemFromChild : function(node){
18433 var el = this.dataName ?
18434 this.el.child('.roo-tpl-' + this.dataName,true) :
18437 if(!node || node.parentNode == el){
18440 var p = node.parentNode;
18441 while(p && p != el){
18442 if(p.parentNode == el){
18451 onClick : function(e){
18452 var item = this.findItemFromChild(e.getTarget());
18454 var index = this.indexOf(item);
18455 if(this.onItemClick(item, index, e) !== false){
18456 this.fireEvent("click", this, index, item, e);
18459 this.clearSelections();
18464 onContextMenu : function(e){
18465 var item = this.findItemFromChild(e.getTarget());
18467 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18472 onDblClick : function(e){
18473 var item = this.findItemFromChild(e.getTarget());
18475 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479 onItemClick : function(item, index, e)
18481 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18484 if (this.toggleSelect) {
18485 var m = this.isSelected(item) ? 'unselect' : 'select';
18488 _t[m](item, true, false);
18491 if(this.multiSelect || this.singleSelect){
18492 if(this.multiSelect && e.shiftKey && this.lastSelection){
18493 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18495 this.select(item, this.multiSelect && e.ctrlKey);
18496 this.lastSelection = item;
18499 if(!this.tickable){
18500 e.preventDefault();
18508 * Get the number of selected nodes.
18511 getSelectionCount : function(){
18512 return this.selections.length;
18516 * Get the currently selected nodes.
18517 * @return {Array} An array of HTMLElements
18519 getSelectedNodes : function(){
18520 return this.selections;
18524 * Get the indexes of the selected nodes.
18527 getSelectedIndexes : function(){
18528 var indexes = [], s = this.selections;
18529 for(var i = 0, len = s.length; i < len; i++){
18530 indexes.push(s[i].nodeIndex);
18536 * Clear all selections
18537 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18539 clearSelections : function(suppressEvent){
18540 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18541 this.cmp.elements = this.selections;
18542 this.cmp.removeClass(this.selectedClass);
18543 this.selections = [];
18544 if(!suppressEvent){
18545 this.fireEvent("selectionchange", this, this.selections);
18551 * Returns true if the passed node is selected
18552 * @param {HTMLElement/Number} node The node or node index
18553 * @return {Boolean}
18555 isSelected : function(node){
18556 var s = this.selections;
18560 node = this.getNode(node);
18561 return s.indexOf(node) !== -1;
18566 * @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
18567 * @param {Boolean} keepExisting (optional) true to keep existing selections
18568 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18570 select : function(nodeInfo, keepExisting, suppressEvent){
18571 if(nodeInfo instanceof Array){
18573 this.clearSelections(true);
18575 for(var i = 0, len = nodeInfo.length; i < len; i++){
18576 this.select(nodeInfo[i], true, true);
18580 var node = this.getNode(nodeInfo);
18581 if(!node || this.isSelected(node)){
18582 return; // already selected.
18585 this.clearSelections(true);
18588 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18589 Roo.fly(node).addClass(this.selectedClass);
18590 this.selections.push(node);
18591 if(!suppressEvent){
18592 this.fireEvent("selectionchange", this, this.selections);
18600 * @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
18601 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18602 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18604 unselect : function(nodeInfo, keepExisting, suppressEvent)
18606 if(nodeInfo instanceof Array){
18607 Roo.each(this.selections, function(s) {
18608 this.unselect(s, nodeInfo);
18612 var node = this.getNode(nodeInfo);
18613 if(!node || !this.isSelected(node)){
18614 //Roo.log("not selected");
18615 return; // not selected.
18619 Roo.each(this.selections, function(s) {
18621 Roo.fly(node).removeClass(this.selectedClass);
18628 this.selections= ns;
18629 this.fireEvent("selectionchange", this, this.selections);
18633 * Gets a template node.
18634 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18635 * @return {HTMLElement} The node or null if it wasn't found
18637 getNode : function(nodeInfo){
18638 if(typeof nodeInfo == "string"){
18639 return document.getElementById(nodeInfo);
18640 }else if(typeof nodeInfo == "number"){
18641 return this.nodes[nodeInfo];
18647 * Gets a range template nodes.
18648 * @param {Number} startIndex
18649 * @param {Number} endIndex
18650 * @return {Array} An array of nodes
18652 getNodes : function(start, end){
18653 var ns = this.nodes;
18654 start = start || 0;
18655 end = typeof end == "undefined" ? ns.length - 1 : end;
18658 for(var i = start; i <= end; i++){
18662 for(var i = start; i >= end; i--){
18670 * Finds the index of the passed node
18671 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18672 * @return {Number} The index of the node or -1
18674 indexOf : function(node){
18675 node = this.getNode(node);
18676 if(typeof node.nodeIndex == "number"){
18677 return node.nodeIndex;
18679 var ns = this.nodes;
18680 for(var i = 0, len = ns.length; i < len; i++){
18691 * based on jquery fullcalendar
18695 Roo.bootstrap = Roo.bootstrap || {};
18697 * @class Roo.bootstrap.Calendar
18698 * @extends Roo.bootstrap.Component
18699 * Bootstrap Calendar class
18700 * @cfg {Boolean} loadMask (true|false) default false
18701 * @cfg {Object} header generate the user specific header of the calendar, default false
18704 * Create a new Container
18705 * @param {Object} config The config object
18710 Roo.bootstrap.Calendar = function(config){
18711 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715 * Fires when a date is selected
18716 * @param {DatePicker} this
18717 * @param {Date} date The selected date
18721 * @event monthchange
18722 * Fires when the displayed month changes
18723 * @param {DatePicker} this
18724 * @param {Date} date The selected month
18726 'monthchange': true,
18728 * @event evententer
18729 * Fires when mouse over an event
18730 * @param {Calendar} this
18731 * @param {event} Event
18733 'evententer': true,
18735 * @event eventleave
18736 * Fires when the mouse leaves an
18737 * @param {Calendar} this
18740 'eventleave': true,
18742 * @event eventclick
18743 * Fires when the mouse click an
18744 * @param {Calendar} this
18753 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18756 * @cfg {Number} startDay
18757 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18765 getAutoCreate : function(){
18768 var fc_button = function(name, corner, style, content ) {
18769 return Roo.apply({},{
18771 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18773 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18776 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18787 style : 'width:100%',
18794 cls : 'fc-header-left',
18796 fc_button('prev', 'left', 'arrow', '‹' ),
18797 fc_button('next', 'right', 'arrow', '›' ),
18798 { tag: 'span', cls: 'fc-header-space' },
18799 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18807 cls : 'fc-header-center',
18811 cls: 'fc-header-title',
18814 html : 'month / year'
18822 cls : 'fc-header-right',
18824 /* fc_button('month', 'left', '', 'month' ),
18825 fc_button('week', '', '', 'week' ),
18826 fc_button('day', 'right', '', 'day' )
18838 header = this.header;
18841 var cal_heads = function() {
18843 // fixme - handle this.
18845 for (var i =0; i < Date.dayNames.length; i++) {
18846 var d = Date.dayNames[i];
18849 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18850 html : d.substring(0,3)
18854 ret[0].cls += ' fc-first';
18855 ret[6].cls += ' fc-last';
18858 var cal_cell = function(n) {
18861 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18866 cls: 'fc-day-number',
18870 cls: 'fc-day-content',
18874 style: 'position: relative;' // height: 17px;
18886 var cal_rows = function() {
18889 for (var r = 0; r < 6; r++) {
18896 for (var i =0; i < Date.dayNames.length; i++) {
18897 var d = Date.dayNames[i];
18898 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18901 row.cn[0].cls+=' fc-first';
18902 row.cn[0].cn[0].style = 'min-height:90px';
18903 row.cn[6].cls+=' fc-last';
18907 ret[0].cls += ' fc-first';
18908 ret[4].cls += ' fc-prev-last';
18909 ret[5].cls += ' fc-last';
18916 cls: 'fc-border-separate',
18917 style : 'width:100%',
18925 cls : 'fc-first fc-last',
18943 cls : 'fc-content',
18944 style : "position: relative;",
18947 cls : 'fc-view fc-view-month fc-grid',
18948 style : 'position: relative',
18949 unselectable : 'on',
18952 cls : 'fc-event-container',
18953 style : 'position:absolute;z-index:8;top:0;left:0;'
18971 initEvents : function()
18974 throw "can not find store for calendar";
18980 style: "text-align:center",
18984 style: "background-color:white;width:50%;margin:250 auto",
18988 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18999 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19001 var size = this.el.select('.fc-content', true).first().getSize();
19002 this.maskEl.setSize(size.width, size.height);
19003 this.maskEl.enableDisplayMode("block");
19004 if(!this.loadMask){
19005 this.maskEl.hide();
19008 this.store = Roo.factory(this.store, Roo.data);
19009 this.store.on('load', this.onLoad, this);
19010 this.store.on('beforeload', this.onBeforeLoad, this);
19014 this.cells = this.el.select('.fc-day',true);
19015 //Roo.log(this.cells);
19016 this.textNodes = this.el.query('.fc-day-number');
19017 this.cells.addClassOnOver('fc-state-hover');
19019 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19020 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19021 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19022 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19024 this.on('monthchange', this.onMonthChange, this);
19026 this.update(new Date().clearTime());
19029 resize : function() {
19030 var sz = this.el.getSize();
19032 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19033 this.el.select('.fc-day-content div',true).setHeight(34);
19038 showPrevMonth : function(e){
19039 this.update(this.activeDate.add("mo", -1));
19041 showToday : function(e){
19042 this.update(new Date().clearTime());
19045 showNextMonth : function(e){
19046 this.update(this.activeDate.add("mo", 1));
19050 showPrevYear : function(){
19051 this.update(this.activeDate.add("y", -1));
19055 showNextYear : function(){
19056 this.update(this.activeDate.add("y", 1));
19061 update : function(date)
19063 var vd = this.activeDate;
19064 this.activeDate = date;
19065 // if(vd && this.el){
19066 // var t = date.getTime();
19067 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19068 // Roo.log('using add remove');
19070 // this.fireEvent('monthchange', this, date);
19072 // this.cells.removeClass("fc-state-highlight");
19073 // this.cells.each(function(c){
19074 // if(c.dateValue == t){
19075 // c.addClass("fc-state-highlight");
19076 // setTimeout(function(){
19077 // try{c.dom.firstChild.focus();}catch(e){}
19087 var days = date.getDaysInMonth();
19089 var firstOfMonth = date.getFirstDateOfMonth();
19090 var startingPos = firstOfMonth.getDay()-this.startDay;
19092 if(startingPos < this.startDay){
19096 var pm = date.add(Date.MONTH, -1);
19097 var prevStart = pm.getDaysInMonth()-startingPos;
19099 this.cells = this.el.select('.fc-day',true);
19100 this.textNodes = this.el.query('.fc-day-number');
19101 this.cells.addClassOnOver('fc-state-hover');
19103 var cells = this.cells.elements;
19104 var textEls = this.textNodes;
19106 Roo.each(cells, function(cell){
19107 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19110 days += startingPos;
19112 // convert everything to numbers so it's fast
19113 var day = 86400000;
19114 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19117 //Roo.log(prevStart);
19119 var today = new Date().clearTime().getTime();
19120 var sel = date.clearTime().getTime();
19121 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19122 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19123 var ddMatch = this.disabledDatesRE;
19124 var ddText = this.disabledDatesText;
19125 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19126 var ddaysText = this.disabledDaysText;
19127 var format = this.format;
19129 var setCellClass = function(cal, cell){
19133 //Roo.log('set Cell Class');
19135 var t = d.getTime();
19139 cell.dateValue = t;
19141 cell.className += " fc-today";
19142 cell.className += " fc-state-highlight";
19143 cell.title = cal.todayText;
19146 // disable highlight in other month..
19147 //cell.className += " fc-state-highlight";
19152 cell.className = " fc-state-disabled";
19153 cell.title = cal.minText;
19157 cell.className = " fc-state-disabled";
19158 cell.title = cal.maxText;
19162 if(ddays.indexOf(d.getDay()) != -1){
19163 cell.title = ddaysText;
19164 cell.className = " fc-state-disabled";
19167 if(ddMatch && format){
19168 var fvalue = d.dateFormat(format);
19169 if(ddMatch.test(fvalue)){
19170 cell.title = ddText.replace("%0", fvalue);
19171 cell.className = " fc-state-disabled";
19175 if (!cell.initialClassName) {
19176 cell.initialClassName = cell.dom.className;
19179 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19184 for(; i < startingPos; i++) {
19185 textEls[i].innerHTML = (++prevStart);
19186 d.setDate(d.getDate()+1);
19188 cells[i].className = "fc-past fc-other-month";
19189 setCellClass(this, cells[i]);
19194 for(; i < days; i++){
19195 intDay = i - startingPos + 1;
19196 textEls[i].innerHTML = (intDay);
19197 d.setDate(d.getDate()+1);
19199 cells[i].className = ''; // "x-date-active";
19200 setCellClass(this, cells[i]);
19204 for(; i < 42; i++) {
19205 textEls[i].innerHTML = (++extraDays);
19206 d.setDate(d.getDate()+1);
19208 cells[i].className = "fc-future fc-other-month";
19209 setCellClass(this, cells[i]);
19212 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19214 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19216 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19217 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19219 if(totalRows != 6){
19220 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19221 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19224 this.fireEvent('monthchange', this, date);
19228 if(!this.internalRender){
19229 var main = this.el.dom.firstChild;
19230 var w = main.offsetWidth;
19231 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19232 Roo.fly(main).setWidth(w);
19233 this.internalRender = true;
19234 // opera does not respect the auto grow header center column
19235 // then, after it gets a width opera refuses to recalculate
19236 // without a second pass
19237 if(Roo.isOpera && !this.secondPass){
19238 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19239 this.secondPass = true;
19240 this.update.defer(10, this, [date]);
19247 findCell : function(dt) {
19248 dt = dt.clearTime().getTime();
19250 this.cells.each(function(c){
19251 //Roo.log("check " +c.dateValue + '?=' + dt);
19252 if(c.dateValue == dt){
19262 findCells : function(ev) {
19263 var s = ev.start.clone().clearTime().getTime();
19265 var e= ev.end.clone().clearTime().getTime();
19268 this.cells.each(function(c){
19269 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19271 if(c.dateValue > e){
19274 if(c.dateValue < s){
19283 // findBestRow: function(cells)
19287 // for (var i =0 ; i < cells.length;i++) {
19288 // ret = Math.max(cells[i].rows || 0,ret);
19295 addItem : function(ev)
19297 // look for vertical location slot in
19298 var cells = this.findCells(ev);
19300 // ev.row = this.findBestRow(cells);
19302 // work out the location.
19306 for(var i =0; i < cells.length; i++) {
19308 cells[i].row = cells[0].row;
19311 cells[i].row = cells[i].row + 1;
19321 if (crow.start.getY() == cells[i].getY()) {
19323 crow.end = cells[i];
19340 cells[0].events.push(ev);
19342 this.calevents.push(ev);
19345 clearEvents: function() {
19347 if(!this.calevents){
19351 Roo.each(this.cells.elements, function(c){
19357 Roo.each(this.calevents, function(e) {
19358 Roo.each(e.els, function(el) {
19359 el.un('mouseenter' ,this.onEventEnter, this);
19360 el.un('mouseleave' ,this.onEventLeave, this);
19365 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19371 renderEvents: function()
19375 this.cells.each(function(c) {
19384 if(c.row != c.events.length){
19385 r = 4 - (4 - (c.row - c.events.length));
19388 c.events = ev.slice(0, r);
19389 c.more = ev.slice(r);
19391 if(c.more.length && c.more.length == 1){
19392 c.events.push(c.more.pop());
19395 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399 this.cells.each(function(c) {
19401 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19404 for (var e = 0; e < c.events.length; e++){
19405 var ev = c.events[e];
19406 var rows = ev.rows;
19408 for(var i = 0; i < rows.length; i++) {
19410 // how many rows should it span..
19413 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19414 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19416 unselectable : "on",
19419 cls: 'fc-event-inner',
19423 // cls: 'fc-event-time',
19424 // html : cells.length > 1 ? '' : ev.time
19428 cls: 'fc-event-title',
19429 html : String.format('{0}', ev.title)
19436 cls: 'ui-resizable-handle ui-resizable-e',
19437 html : '  '
19444 cfg.cls += ' fc-event-start';
19446 if ((i+1) == rows.length) {
19447 cfg.cls += ' fc-event-end';
19450 var ctr = _this.el.select('.fc-event-container',true).first();
19451 var cg = ctr.createChild(cfg);
19453 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19454 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19456 var r = (c.more.length) ? 1 : 0;
19457 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19458 cg.setWidth(ebox.right - sbox.x -2);
19460 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19461 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19462 cg.on('click', _this.onEventClick, _this, ev);
19473 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19474 style : 'position: absolute',
19475 unselectable : "on",
19478 cls: 'fc-event-inner',
19482 cls: 'fc-event-title',
19490 cls: 'ui-resizable-handle ui-resizable-e',
19491 html : '  '
19497 var ctr = _this.el.select('.fc-event-container',true).first();
19498 var cg = ctr.createChild(cfg);
19500 var sbox = c.select('.fc-day-content',true).first().getBox();
19501 var ebox = c.select('.fc-day-content',true).first().getBox();
19503 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19504 cg.setWidth(ebox.right - sbox.x -2);
19506 cg.on('click', _this.onMoreEventClick, _this, c.more);
19516 onEventEnter: function (e, el,event,d) {
19517 this.fireEvent('evententer', this, el, event);
19520 onEventLeave: function (e, el,event,d) {
19521 this.fireEvent('eventleave', this, el, event);
19524 onEventClick: function (e, el,event,d) {
19525 this.fireEvent('eventclick', this, el, event);
19528 onMonthChange: function () {
19532 onMoreEventClick: function(e, el, more)
19536 this.calpopover.placement = 'right';
19537 this.calpopover.setTitle('More');
19539 this.calpopover.setContent('');
19541 var ctr = this.calpopover.el.select('.popover-content', true).first();
19543 Roo.each(more, function(m){
19545 cls : 'fc-event-hori fc-event-draggable',
19548 var cg = ctr.createChild(cfg);
19550 cg.on('click', _this.onEventClick, _this, m);
19553 this.calpopover.show(el);
19558 onLoad: function ()
19560 this.calevents = [];
19563 if(this.store.getCount() > 0){
19564 this.store.data.each(function(d){
19567 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19568 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19569 time : d.data.start_time,
19570 title : d.data.title,
19571 description : d.data.description,
19572 venue : d.data.venue
19577 this.renderEvents();
19579 if(this.calevents.length && this.loadMask){
19580 this.maskEl.hide();
19584 onBeforeLoad: function()
19586 this.clearEvents();
19588 this.maskEl.show();
19602 * @class Roo.bootstrap.Popover
19603 * @extends Roo.bootstrap.Component
19604 * Bootstrap Popover class
19605 * @cfg {String} html contents of the popover (or false to use children..)
19606 * @cfg {String} title of popover (or false to hide)
19607 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19608 * @cfg {String} trigger click || hover (or false to trigger manually)
19609 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19610 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19611 * - if false and it has a 'parent' then it will be automatically added to that element
19612 * - if string - Roo.get will be called
19613 * @cfg {Number} delay - delay before showing
19616 * Create a new Popover
19617 * @param {Object} config The config object
19620 Roo.bootstrap.Popover = function(config){
19621 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19627 * After the popover show
19629 * @param {Roo.bootstrap.Popover} this
19634 * After the popover hide
19636 * @param {Roo.bootstrap.Popover} this
19642 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19647 placement : 'right',
19648 trigger : 'hover', // hover
19654 can_build_overlaid : false,
19656 maskEl : false, // the mask element
19661 getChildContainer : function()
19663 return this.contentEl;
19666 getPopoverHeader : function()
19668 this.title = true; // flag not to hide it..
19669 return this.headerEl
19673 getAutoCreate : function(){
19676 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19677 style: 'display:block',
19683 cls : 'popover-inner ',
19687 cls: 'popover-title popover-header',
19688 html : this.title === false ? '' : this.title
19691 cls : 'popover-content popover-body ' + (this.cls || ''),
19692 html : this.html || ''
19703 * @param {string} the title
19705 setTitle: function(str)
19709 this.headerEl.dom.innerHTML = str;
19714 * @param {string} the body content
19716 setContent: function(str)
19719 if (this.contentEl) {
19720 this.contentEl.dom.innerHTML = str;
19724 // as it get's added to the bottom of the page.
19725 onRender : function(ct, position)
19727 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732 var cfg = Roo.apply({}, this.getAutoCreate());
19736 cfg.cls += ' ' + this.cls;
19739 cfg.style = this.style;
19741 //Roo.log("adding to ");
19742 this.el = Roo.get(document.body).createChild(cfg, position);
19743 // Roo.log(this.el);
19746 this.contentEl = this.el.select('.popover-content',true).first();
19747 this.headerEl = this.el.select('.popover-title',true).first();
19750 if(typeof(this.items) != 'undefined'){
19751 var items = this.items;
19754 for(var i =0;i < items.length;i++) {
19755 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19759 this.items = nitems;
19761 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19762 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19769 resizeMask : function()
19771 this.maskEl.setSize(
19772 Roo.lib.Dom.getViewWidth(true),
19773 Roo.lib.Dom.getViewHeight(true)
19777 initEvents : function()
19781 Roo.bootstrap.Popover.register(this);
19785 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19786 this.el.enableDisplayMode('block');
19788 if (this.over === false && !this.parent()) {
19791 if (this.triggers === false) {
19796 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19797 var triggers = this.trigger ? this.trigger.split(' ') : [];
19798 Roo.each(triggers, function(trigger) {
19800 if (trigger == 'click') {
19801 on_el.on('click', this.toggle, this);
19802 } else if (trigger != 'manual') {
19803 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19804 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19806 on_el.on(eventIn ,this.enter, this);
19807 on_el.on(eventOut, this.leave, this);
19818 toggle : function () {
19819 this.hoverState == 'in' ? this.leave() : this.enter();
19822 enter : function () {
19824 clearTimeout(this.timeout);
19826 this.hoverState = 'in';
19828 if (!this.delay || !this.delay.show) {
19833 this.timeout = setTimeout(function () {
19834 if (_t.hoverState == 'in') {
19837 }, this.delay.show)
19840 leave : function() {
19841 clearTimeout(this.timeout);
19843 this.hoverState = 'out';
19845 if (!this.delay || !this.delay.hide) {
19850 this.timeout = setTimeout(function () {
19851 if (_t.hoverState == 'out') {
19854 }, this.delay.hide)
19858 * @param {Roo.Element|string|false} - element to align and point to.
19860 show : function (on_el)
19863 on_el = on_el || false; // default to false
19865 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19866 on_el = this.parent().el;
19867 } else if (this.over) {
19868 Roo.get(this.over);
19874 this.render(document.body);
19878 this.el.removeClass([
19879 'fade','top','bottom', 'left', 'right','in',
19880 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19883 if (this.title === false) {
19884 this.headerEl.hide();
19888 var placement = typeof this.placement == 'function' ?
19889 this.placement.call(this, this.el, on_el) :
19893 var autoToken = /\s?auto?\s?/i; /// not sure how this was supposed to work? right auto ? what?
19895 // I think 'auto right' - but
19897 var autoPlace = autoToken.test(placement);
19899 placement = placement.replace(autoToken, '') || 'top';
19905 this.el.dom.style.display='block';
19907 //this.el.appendTo(on_el);
19909 var p = this.getPosition();
19910 var box = this.el.getBox();
19913 var align = Roo.bootstrap.Popover.alignment[placement];
19914 this.el.addClass(align[2]);
19919 this.el.alignTo(on_el, align[0],align[1]);
19921 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19922 var es = this.el.getSize();
19923 var x = Roo.lib.Dom.getViewWidth()/2;
19924 var y = Roo.lib.Dom.getViewHeight()/2;
19925 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19930 //var arrow = this.el.select('.arrow',true).first();
19931 //arrow.set(align[2],
19933 this.el.addClass('in');
19936 if (this.el.hasClass('fade')) {
19940 this.hoverState = 'in';
19943 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19944 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945 this.maskEl.dom.style.display = 'block';
19946 this.maskEl.addClass('show');
19948 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19952 this.fireEvent('show', this);
19957 this.el.setXY([0,0]);
19958 this.el.removeClass('in');
19960 this.hoverState = null;
19961 this.maskEl.hide(); // always..
19962 this.fireEvent('hide', this);
19968 Roo.apply(Roo.bootstrap.Popover, {
19971 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19972 'right' : ['l-br', [10,0], 'right bs-popover-right'],
19973 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19974 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19979 clickHander : false,
19982 onMouseDown : function(e)
19984 if (!e.getTarget(".roo-popover")) {
19992 register : function(popup)
19994 if (!Roo.bootstrap.Popover.clickHandler) {
19995 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19997 // hide other popups.
19999 this.popups.push(popup);
20001 hideAll : function()
20003 this.popups.forEach(function(p) {
20011 * Card header - holder for the card header elements.
20016 * @class Roo.bootstrap.PopoverNav
20017 * @extends Roo.bootstrap.NavGroup
20018 * Bootstrap Popover header navigation class
20020 * Create a new Popover Header Navigation
20021 * @param {Object} config The config object
20024 Roo.bootstrap.PopoverNav = function(config){
20025 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20028 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20031 container_method : 'getPopoverHeader'
20049 * @class Roo.bootstrap.Progress
20050 * @extends Roo.bootstrap.Component
20051 * Bootstrap Progress class
20052 * @cfg {Boolean} striped striped of the progress bar
20053 * @cfg {Boolean} active animated of the progress bar
20057 * Create a new Progress
20058 * @param {Object} config The config object
20061 Roo.bootstrap.Progress = function(config){
20062 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20065 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20070 getAutoCreate : function(){
20078 cfg.cls += ' progress-striped';
20082 cfg.cls += ' active';
20101 * @class Roo.bootstrap.ProgressBar
20102 * @extends Roo.bootstrap.Component
20103 * Bootstrap ProgressBar class
20104 * @cfg {Number} aria_valuenow aria-value now
20105 * @cfg {Number} aria_valuemin aria-value min
20106 * @cfg {Number} aria_valuemax aria-value max
20107 * @cfg {String} label label for the progress bar
20108 * @cfg {String} panel (success | info | warning | danger )
20109 * @cfg {String} role role of the progress bar
20110 * @cfg {String} sr_only text
20114 * Create a new ProgressBar
20115 * @param {Object} config The config object
20118 Roo.bootstrap.ProgressBar = function(config){
20119 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20122 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20126 aria_valuemax : 100,
20132 getAutoCreate : function()
20137 cls: 'progress-bar',
20138 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20150 cfg.role = this.role;
20153 if(this.aria_valuenow){
20154 cfg['aria-valuenow'] = this.aria_valuenow;
20157 if(this.aria_valuemin){
20158 cfg['aria-valuemin'] = this.aria_valuemin;
20161 if(this.aria_valuemax){
20162 cfg['aria-valuemax'] = this.aria_valuemax;
20165 if(this.label && !this.sr_only){
20166 cfg.html = this.label;
20170 cfg.cls += ' progress-bar-' + this.panel;
20176 update : function(aria_valuenow)
20178 this.aria_valuenow = aria_valuenow;
20180 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20195 * @class Roo.bootstrap.TabGroup
20196 * @extends Roo.bootstrap.Column
20197 * Bootstrap Column class
20198 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20199 * @cfg {Boolean} carousel true to make the group behave like a carousel
20200 * @cfg {Boolean} bullets show bullets for the panels
20201 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20202 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20203 * @cfg {Boolean} showarrow (true|false) show arrow default true
20206 * Create a new TabGroup
20207 * @param {Object} config The config object
20210 Roo.bootstrap.TabGroup = function(config){
20211 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20213 this.navId = Roo.id();
20216 Roo.bootstrap.TabGroup.register(this);
20220 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20223 transition : false,
20228 slideOnTouch : false,
20231 getAutoCreate : function()
20233 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20235 cfg.cls += ' tab-content';
20237 if (this.carousel) {
20238 cfg.cls += ' carousel slide';
20241 cls : 'carousel-inner',
20245 if(this.bullets && !Roo.isTouch){
20248 cls : 'carousel-bullets',
20252 if(this.bullets_cls){
20253 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20260 cfg.cn[0].cn.push(bullets);
20263 if(this.showarrow){
20264 cfg.cn[0].cn.push({
20266 class : 'carousel-arrow',
20270 class : 'carousel-prev',
20274 class : 'fa fa-chevron-left'
20280 class : 'carousel-next',
20284 class : 'fa fa-chevron-right'
20297 initEvents: function()
20299 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20300 // this.el.on("touchstart", this.onTouchStart, this);
20303 if(this.autoslide){
20306 this.slideFn = window.setInterval(function() {
20307 _this.showPanelNext();
20311 if(this.showarrow){
20312 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20313 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20319 // onTouchStart : function(e, el, o)
20321 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20325 // this.showPanelNext();
20329 getChildContainer : function()
20331 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20335 * register a Navigation item
20336 * @param {Roo.bootstrap.NavItem} the navitem to add
20338 register : function(item)
20340 this.tabs.push( item);
20341 item.navId = this.navId; // not really needed..
20346 getActivePanel : function()
20349 Roo.each(this.tabs, function(t) {
20359 getPanelByName : function(n)
20362 Roo.each(this.tabs, function(t) {
20363 if (t.tabId == n) {
20371 indexOfPanel : function(p)
20374 Roo.each(this.tabs, function(t,i) {
20375 if (t.tabId == p.tabId) {
20384 * show a specific panel
20385 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20386 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20388 showPanel : function (pan)
20390 if(this.transition || typeof(pan) == 'undefined'){
20391 Roo.log("waiting for the transitionend");
20395 if (typeof(pan) == 'number') {
20396 pan = this.tabs[pan];
20399 if (typeof(pan) == 'string') {
20400 pan = this.getPanelByName(pan);
20403 var cur = this.getActivePanel();
20406 Roo.log('pan or acitve pan is undefined');
20410 if (pan.tabId == this.getActivePanel().tabId) {
20414 if (false === cur.fireEvent('beforedeactivate')) {
20418 if(this.bullets > 0 && !Roo.isTouch){
20419 this.setActiveBullet(this.indexOfPanel(pan));
20422 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20424 //class="carousel-item carousel-item-next carousel-item-left"
20426 this.transition = true;
20427 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20428 var lr = dir == 'next' ? 'left' : 'right';
20429 pan.el.addClass(dir); // or prev
20430 pan.el.addClass('carousel-item-' + dir); // or prev
20431 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20432 cur.el.addClass(lr); // or right
20433 pan.el.addClass(lr);
20434 cur.el.addClass('carousel-item-' +lr); // or right
20435 pan.el.addClass('carousel-item-' +lr);
20439 cur.el.on('transitionend', function() {
20440 Roo.log("trans end?");
20442 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20443 pan.setActive(true);
20445 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20446 cur.setActive(false);
20448 _this.transition = false;
20450 }, this, { single: true } );
20455 cur.setActive(false);
20456 pan.setActive(true);
20461 showPanelNext : function()
20463 var i = this.indexOfPanel(this.getActivePanel());
20465 if (i >= this.tabs.length - 1 && !this.autoslide) {
20469 if (i >= this.tabs.length - 1 && this.autoslide) {
20473 this.showPanel(this.tabs[i+1]);
20476 showPanelPrev : function()
20478 var i = this.indexOfPanel(this.getActivePanel());
20480 if (i < 1 && !this.autoslide) {
20484 if (i < 1 && this.autoslide) {
20485 i = this.tabs.length;
20488 this.showPanel(this.tabs[i-1]);
20492 addBullet: function()
20494 if(!this.bullets || Roo.isTouch){
20497 var ctr = this.el.select('.carousel-bullets',true).first();
20498 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20499 var bullet = ctr.createChild({
20500 cls : 'bullet bullet-' + i
20501 },ctr.dom.lastChild);
20506 bullet.on('click', (function(e, el, o, ii, t){
20508 e.preventDefault();
20510 this.showPanel(ii);
20512 if(this.autoslide && this.slideFn){
20513 clearInterval(this.slideFn);
20514 this.slideFn = window.setInterval(function() {
20515 _this.showPanelNext();
20519 }).createDelegate(this, [i, bullet], true));
20524 setActiveBullet : function(i)
20530 Roo.each(this.el.select('.bullet', true).elements, function(el){
20531 el.removeClass('selected');
20534 var bullet = this.el.select('.bullet-' + i, true).first();
20540 bullet.addClass('selected');
20551 Roo.apply(Roo.bootstrap.TabGroup, {
20555 * register a Navigation Group
20556 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20558 register : function(navgrp)
20560 this.groups[navgrp.navId] = navgrp;
20564 * fetch a Navigation Group based on the navigation ID
20565 * if one does not exist , it will get created.
20566 * @param {string} the navgroup to add
20567 * @returns {Roo.bootstrap.NavGroup} the navgroup
20569 get: function(navId) {
20570 if (typeof(this.groups[navId]) == 'undefined') {
20571 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20573 return this.groups[navId] ;
20588 * @class Roo.bootstrap.TabPanel
20589 * @extends Roo.bootstrap.Component
20590 * Bootstrap TabPanel class
20591 * @cfg {Boolean} active panel active
20592 * @cfg {String} html panel content
20593 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20594 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20595 * @cfg {String} href click to link..
20596 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20600 * Create a new TabPanel
20601 * @param {Object} config The config object
20604 Roo.bootstrap.TabPanel = function(config){
20605 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20609 * Fires when the active status changes
20610 * @param {Roo.bootstrap.TabPanel} this
20611 * @param {Boolean} state the new state
20616 * @event beforedeactivate
20617 * Fires before a tab is de-activated - can be used to do validation on a form.
20618 * @param {Roo.bootstrap.TabPanel} this
20619 * @return {Boolean} false if there is an error
20622 'beforedeactivate': true
20625 this.tabId = this.tabId || Roo.id();
20629 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20636 touchSlide : false,
20637 getAutoCreate : function(){
20642 // item is needed for carousel - not sure if it has any effect otherwise
20643 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20644 html: this.html || ''
20648 cfg.cls += ' active';
20652 cfg.tabId = this.tabId;
20660 initEvents: function()
20662 var p = this.parent();
20664 this.navId = this.navId || p.navId;
20666 if (typeof(this.navId) != 'undefined') {
20667 // not really needed.. but just in case.. parent should be a NavGroup.
20668 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20672 var i = tg.tabs.length - 1;
20674 if(this.active && tg.bullets > 0 && i < tg.bullets){
20675 tg.setActiveBullet(i);
20679 this.el.on('click', this.onClick, this);
20681 if(Roo.isTouch && this.touchSlide){
20682 this.el.on("touchstart", this.onTouchStart, this);
20683 this.el.on("touchmove", this.onTouchMove, this);
20684 this.el.on("touchend", this.onTouchEnd, this);
20689 onRender : function(ct, position)
20691 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20694 setActive : function(state)
20696 Roo.log("panel - set active " + this.tabId + "=" + state);
20698 this.active = state;
20700 this.el.removeClass('active');
20702 } else if (!this.el.hasClass('active')) {
20703 this.el.addClass('active');
20706 this.fireEvent('changed', this, state);
20709 onClick : function(e)
20711 e.preventDefault();
20713 if(!this.href.length){
20717 window.location.href = this.href;
20726 onTouchStart : function(e)
20728 this.swiping = false;
20730 this.startX = e.browserEvent.touches[0].clientX;
20731 this.startY = e.browserEvent.touches[0].clientY;
20734 onTouchMove : function(e)
20736 this.swiping = true;
20738 this.endX = e.browserEvent.touches[0].clientX;
20739 this.endY = e.browserEvent.touches[0].clientY;
20742 onTouchEnd : function(e)
20749 var tabGroup = this.parent();
20751 if(this.endX > this.startX){ // swiping right
20752 tabGroup.showPanelPrev();
20756 if(this.startX > this.endX){ // swiping left
20757 tabGroup.showPanelNext();
20776 * @class Roo.bootstrap.DateField
20777 * @extends Roo.bootstrap.Input
20778 * Bootstrap DateField class
20779 * @cfg {Number} weekStart default 0
20780 * @cfg {String} viewMode default empty, (months|years)
20781 * @cfg {String} minViewMode default empty, (months|years)
20782 * @cfg {Number} startDate default -Infinity
20783 * @cfg {Number} endDate default Infinity
20784 * @cfg {Boolean} todayHighlight default false
20785 * @cfg {Boolean} todayBtn default false
20786 * @cfg {Boolean} calendarWeeks default false
20787 * @cfg {Object} daysOfWeekDisabled default empty
20788 * @cfg {Boolean} singleMode default false (true | false)
20790 * @cfg {Boolean} keyboardNavigation default true
20791 * @cfg {String} language default en
20794 * Create a new DateField
20795 * @param {Object} config The config object
20798 Roo.bootstrap.DateField = function(config){
20799 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20803 * Fires when this field show.
20804 * @param {Roo.bootstrap.DateField} this
20805 * @param {Mixed} date The date value
20810 * Fires when this field hide.
20811 * @param {Roo.bootstrap.DateField} this
20812 * @param {Mixed} date The date value
20817 * Fires when select a date.
20818 * @param {Roo.bootstrap.DateField} this
20819 * @param {Mixed} date The date value
20823 * @event beforeselect
20824 * Fires when before select a date.
20825 * @param {Roo.bootstrap.DateField} this
20826 * @param {Mixed} date The date value
20828 beforeselect : true
20832 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20835 * @cfg {String} format
20836 * The default date format string which can be overriden for localization support. The format must be
20837 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20841 * @cfg {String} altFormats
20842 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20843 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20845 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20853 todayHighlight : false,
20859 keyboardNavigation: true,
20861 calendarWeeks: false,
20863 startDate: -Infinity,
20867 daysOfWeekDisabled: [],
20871 singleMode : false,
20873 UTCDate: function()
20875 return new Date(Date.UTC.apply(Date, arguments));
20878 UTCToday: function()
20880 var today = new Date();
20881 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20884 getDate: function() {
20885 var d = this.getUTCDate();
20886 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20889 getUTCDate: function() {
20893 setDate: function(d) {
20894 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20897 setUTCDate: function(d) {
20899 this.setValue(this.formatDate(this.date));
20902 onRender: function(ct, position)
20905 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20907 this.language = this.language || 'en';
20908 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20909 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20911 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20912 this.format = this.format || 'm/d/y';
20913 this.isInline = false;
20914 this.isInput = true;
20915 this.component = this.el.select('.add-on', true).first() || false;
20916 this.component = (this.component && this.component.length === 0) ? false : this.component;
20917 this.hasInput = this.component && this.inputEl().length;
20919 if (typeof(this.minViewMode === 'string')) {
20920 switch (this.minViewMode) {
20922 this.minViewMode = 1;
20925 this.minViewMode = 2;
20928 this.minViewMode = 0;
20933 if (typeof(this.viewMode === 'string')) {
20934 switch (this.viewMode) {
20947 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20949 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20951 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20953 this.picker().on('mousedown', this.onMousedown, this);
20954 this.picker().on('click', this.onClick, this);
20956 this.picker().addClass('datepicker-dropdown');
20958 this.startViewMode = this.viewMode;
20960 if(this.singleMode){
20961 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20962 v.setVisibilityMode(Roo.Element.DISPLAY);
20966 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20967 v.setStyle('width', '189px');
20971 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20972 if(!this.calendarWeeks){
20977 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20978 v.attr('colspan', function(i, val){
20979 return parseInt(val) + 1;
20984 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20986 this.setStartDate(this.startDate);
20987 this.setEndDate(this.endDate);
20989 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20996 if(this.isInline) {
21001 picker : function()
21003 return this.pickerEl;
21004 // return this.el.select('.datepicker', true).first();
21007 fillDow: function()
21009 var dowCnt = this.weekStart;
21018 if(this.calendarWeeks){
21026 while (dowCnt < this.weekStart + 7) {
21030 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21034 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21037 fillMonths: function()
21040 var months = this.picker().select('>.datepicker-months td', true).first();
21042 months.dom.innerHTML = '';
21048 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21051 months.createChild(month);
21058 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;
21060 if (this.date < this.startDate) {
21061 this.viewDate = new Date(this.startDate);
21062 } else if (this.date > this.endDate) {
21063 this.viewDate = new Date(this.endDate);
21065 this.viewDate = new Date(this.date);
21073 var d = new Date(this.viewDate),
21074 year = d.getUTCFullYear(),
21075 month = d.getUTCMonth(),
21076 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21077 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21078 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21079 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21080 currentDate = this.date && this.date.valueOf(),
21081 today = this.UTCToday();
21083 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21085 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21087 // this.picker.select('>tfoot th.today').
21088 // .text(dates[this.language].today)
21089 // .toggle(this.todayBtn !== false);
21091 this.updateNavArrows();
21094 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21096 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21098 prevMonth.setUTCDate(day);
21100 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21102 var nextMonth = new Date(prevMonth);
21104 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21106 nextMonth = nextMonth.valueOf();
21108 var fillMonths = false;
21110 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21112 while(prevMonth.valueOf() <= nextMonth) {
21115 if (prevMonth.getUTCDay() === this.weekStart) {
21117 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21125 if(this.calendarWeeks){
21126 // ISO 8601: First week contains first thursday.
21127 // ISO also states week starts on Monday, but we can be more abstract here.
21129 // Start of current week: based on weekstart/current date
21130 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21131 // Thursday of this week
21132 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21133 // First Thursday of year, year from thursday
21134 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21135 // Calendar week: ms between thursdays, div ms per day, div 7 days
21136 calWeek = (th - yth) / 864e5 / 7 + 1;
21138 fillMonths.cn.push({
21146 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21148 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21151 if (this.todayHighlight &&
21152 prevMonth.getUTCFullYear() == today.getFullYear() &&
21153 prevMonth.getUTCMonth() == today.getMonth() &&
21154 prevMonth.getUTCDate() == today.getDate()) {
21155 clsName += ' today';
21158 if (currentDate && prevMonth.valueOf() === currentDate) {
21159 clsName += ' active';
21162 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21163 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21164 clsName += ' disabled';
21167 fillMonths.cn.push({
21169 cls: 'day ' + clsName,
21170 html: prevMonth.getDate()
21173 prevMonth.setDate(prevMonth.getDate()+1);
21176 var currentYear = this.date && this.date.getUTCFullYear();
21177 var currentMonth = this.date && this.date.getUTCMonth();
21179 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21181 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21182 v.removeClass('active');
21184 if(currentYear === year && k === currentMonth){
21185 v.addClass('active');
21188 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21189 v.addClass('disabled');
21195 year = parseInt(year/10, 10) * 10;
21197 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21199 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21202 for (var i = -1; i < 11; i++) {
21203 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21205 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21213 showMode: function(dir)
21216 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21219 Roo.each(this.picker().select('>div',true).elements, function(v){
21220 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21223 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21228 if(this.isInline) {
21232 this.picker().removeClass(['bottom', 'top']);
21234 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21236 * place to the top of element!
21240 this.picker().addClass('top');
21241 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21246 this.picker().addClass('bottom');
21248 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21251 parseDate : function(value)
21253 if(!value || value instanceof Date){
21256 var v = Date.parseDate(value, this.format);
21257 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21258 v = Date.parseDate(value, 'Y-m-d');
21260 if(!v && this.altFormats){
21261 if(!this.altFormatsArray){
21262 this.altFormatsArray = this.altFormats.split("|");
21264 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21265 v = Date.parseDate(value, this.altFormatsArray[i]);
21271 formatDate : function(date, fmt)
21273 return (!date || !(date instanceof Date)) ?
21274 date : date.dateFormat(fmt || this.format);
21277 onFocus : function()
21279 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21283 onBlur : function()
21285 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21287 var d = this.inputEl().getValue();
21294 showPopup : function()
21296 this.picker().show();
21300 this.fireEvent('showpopup', this, this.date);
21303 hidePopup : function()
21305 if(this.isInline) {
21308 this.picker().hide();
21309 this.viewMode = this.startViewMode;
21312 this.fireEvent('hidepopup', this, this.date);
21316 onMousedown: function(e)
21318 e.stopPropagation();
21319 e.preventDefault();
21324 Roo.bootstrap.DateField.superclass.keyup.call(this);
21328 setValue: function(v)
21330 if(this.fireEvent('beforeselect', this, v) !== false){
21331 var d = new Date(this.parseDate(v) ).clearTime();
21333 if(isNaN(d.getTime())){
21334 this.date = this.viewDate = '';
21335 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21339 v = this.formatDate(d);
21341 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21343 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21347 this.fireEvent('select', this, this.date);
21351 getValue: function()
21353 return this.formatDate(this.date);
21356 fireKey: function(e)
21358 if (!this.picker().isVisible()){
21359 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21365 var dateChanged = false,
21367 newDate, newViewDate;
21372 e.preventDefault();
21376 if (!this.keyboardNavigation) {
21379 dir = e.keyCode == 37 ? -1 : 1;
21382 newDate = this.moveYear(this.date, dir);
21383 newViewDate = this.moveYear(this.viewDate, dir);
21384 } else if (e.shiftKey){
21385 newDate = this.moveMonth(this.date, dir);
21386 newViewDate = this.moveMonth(this.viewDate, dir);
21388 newDate = new Date(this.date);
21389 newDate.setUTCDate(this.date.getUTCDate() + dir);
21390 newViewDate = new Date(this.viewDate);
21391 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21393 if (this.dateWithinRange(newDate)){
21394 this.date = newDate;
21395 this.viewDate = newViewDate;
21396 this.setValue(this.formatDate(this.date));
21398 e.preventDefault();
21399 dateChanged = true;
21404 if (!this.keyboardNavigation) {
21407 dir = e.keyCode == 38 ? -1 : 1;
21409 newDate = this.moveYear(this.date, dir);
21410 newViewDate = this.moveYear(this.viewDate, dir);
21411 } else if (e.shiftKey){
21412 newDate = this.moveMonth(this.date, dir);
21413 newViewDate = this.moveMonth(this.viewDate, dir);
21415 newDate = new Date(this.date);
21416 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21417 newViewDate = new Date(this.viewDate);
21418 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21420 if (this.dateWithinRange(newDate)){
21421 this.date = newDate;
21422 this.viewDate = newViewDate;
21423 this.setValue(this.formatDate(this.date));
21425 e.preventDefault();
21426 dateChanged = true;
21430 this.setValue(this.formatDate(this.date));
21432 e.preventDefault();
21435 this.setValue(this.formatDate(this.date));
21449 onClick: function(e)
21451 e.stopPropagation();
21452 e.preventDefault();
21454 var target = e.getTarget();
21456 if(target.nodeName.toLowerCase() === 'i'){
21457 target = Roo.get(target).dom.parentNode;
21460 var nodeName = target.nodeName;
21461 var className = target.className;
21462 var html = target.innerHTML;
21463 //Roo.log(nodeName);
21465 switch(nodeName.toLowerCase()) {
21467 switch(className) {
21473 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21474 switch(this.viewMode){
21476 this.viewDate = this.moveMonth(this.viewDate, dir);
21480 this.viewDate = this.moveYear(this.viewDate, dir);
21486 var date = new Date();
21487 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21489 this.setValue(this.formatDate(this.date));
21496 if (className.indexOf('disabled') < 0) {
21497 this.viewDate.setUTCDate(1);
21498 if (className.indexOf('month') > -1) {
21499 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21501 var year = parseInt(html, 10) || 0;
21502 this.viewDate.setUTCFullYear(year);
21506 if(this.singleMode){
21507 this.setValue(this.formatDate(this.viewDate));
21518 //Roo.log(className);
21519 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21520 var day = parseInt(html, 10) || 1;
21521 var year = this.viewDate.getUTCFullYear(),
21522 month = this.viewDate.getUTCMonth();
21524 if (className.indexOf('old') > -1) {
21531 } else if (className.indexOf('new') > -1) {
21539 //Roo.log([year,month,day]);
21540 this.date = this.UTCDate(year, month, day,0,0,0,0);
21541 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21543 //Roo.log(this.formatDate(this.date));
21544 this.setValue(this.formatDate(this.date));
21551 setStartDate: function(startDate)
21553 this.startDate = startDate || -Infinity;
21554 if (this.startDate !== -Infinity) {
21555 this.startDate = this.parseDate(this.startDate);
21558 this.updateNavArrows();
21561 setEndDate: function(endDate)
21563 this.endDate = endDate || Infinity;
21564 if (this.endDate !== Infinity) {
21565 this.endDate = this.parseDate(this.endDate);
21568 this.updateNavArrows();
21571 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21573 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21574 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21575 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21577 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21578 return parseInt(d, 10);
21581 this.updateNavArrows();
21584 updateNavArrows: function()
21586 if(this.singleMode){
21590 var d = new Date(this.viewDate),
21591 year = d.getUTCFullYear(),
21592 month = d.getUTCMonth();
21594 Roo.each(this.picker().select('.prev', true).elements, function(v){
21596 switch (this.viewMode) {
21599 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21605 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21612 Roo.each(this.picker().select('.next', true).elements, function(v){
21614 switch (this.viewMode) {
21617 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21623 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21631 moveMonth: function(date, dir)
21636 var new_date = new Date(date.valueOf()),
21637 day = new_date.getUTCDate(),
21638 month = new_date.getUTCMonth(),
21639 mag = Math.abs(dir),
21641 dir = dir > 0 ? 1 : -1;
21644 // If going back one month, make sure month is not current month
21645 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21647 return new_date.getUTCMonth() == month;
21649 // If going forward one month, make sure month is as expected
21650 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21652 return new_date.getUTCMonth() != new_month;
21654 new_month = month + dir;
21655 new_date.setUTCMonth(new_month);
21656 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21657 if (new_month < 0 || new_month > 11) {
21658 new_month = (new_month + 12) % 12;
21661 // For magnitudes >1, move one month at a time...
21662 for (var i=0; i<mag; i++) {
21663 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21664 new_date = this.moveMonth(new_date, dir);
21666 // ...then reset the day, keeping it in the new month
21667 new_month = new_date.getUTCMonth();
21668 new_date.setUTCDate(day);
21670 return new_month != new_date.getUTCMonth();
21673 // Common date-resetting loop -- if date is beyond end of month, make it
21676 new_date.setUTCDate(--day);
21677 new_date.setUTCMonth(new_month);
21682 moveYear: function(date, dir)
21684 return this.moveMonth(date, dir*12);
21687 dateWithinRange: function(date)
21689 return date >= this.startDate && date <= this.endDate;
21695 this.picker().remove();
21698 validateValue : function(value)
21700 if(this.getVisibilityEl().hasClass('hidden')){
21704 if(value.length < 1) {
21705 if(this.allowBlank){
21711 if(value.length < this.minLength){
21714 if(value.length > this.maxLength){
21718 var vt = Roo.form.VTypes;
21719 if(!vt[this.vtype](value, this)){
21723 if(typeof this.validator == "function"){
21724 var msg = this.validator(value);
21730 if(this.regex && !this.regex.test(value)){
21734 if(typeof(this.parseDate(value)) == 'undefined'){
21738 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21742 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21752 this.date = this.viewDate = '';
21754 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21759 Roo.apply(Roo.bootstrap.DateField, {
21770 html: '<i class="fa fa-arrow-left"/>'
21780 html: '<i class="fa fa-arrow-right"/>'
21822 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21823 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21824 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21825 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21826 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21839 navFnc: 'FullYear',
21844 navFnc: 'FullYear',
21849 Roo.apply(Roo.bootstrap.DateField, {
21853 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21857 cls: 'datepicker-days',
21861 cls: 'table-condensed',
21863 Roo.bootstrap.DateField.head,
21867 Roo.bootstrap.DateField.footer
21874 cls: 'datepicker-months',
21878 cls: 'table-condensed',
21880 Roo.bootstrap.DateField.head,
21881 Roo.bootstrap.DateField.content,
21882 Roo.bootstrap.DateField.footer
21889 cls: 'datepicker-years',
21893 cls: 'table-condensed',
21895 Roo.bootstrap.DateField.head,
21896 Roo.bootstrap.DateField.content,
21897 Roo.bootstrap.DateField.footer
21916 * @class Roo.bootstrap.TimeField
21917 * @extends Roo.bootstrap.Input
21918 * Bootstrap DateField class
21922 * Create a new TimeField
21923 * @param {Object} config The config object
21926 Roo.bootstrap.TimeField = function(config){
21927 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21931 * Fires when this field show.
21932 * @param {Roo.bootstrap.DateField} thisthis
21933 * @param {Mixed} date The date value
21938 * Fires when this field hide.
21939 * @param {Roo.bootstrap.DateField} this
21940 * @param {Mixed} date The date value
21945 * Fires when select a date.
21946 * @param {Roo.bootstrap.DateField} this
21947 * @param {Mixed} date The date value
21953 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21956 * @cfg {String} format
21957 * The default time format string which can be overriden for localization support. The format must be
21958 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21962 onRender: function(ct, position)
21965 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21967 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21969 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21971 this.pop = this.picker().select('>.datepicker-time',true).first();
21972 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21974 this.picker().on('mousedown', this.onMousedown, this);
21975 this.picker().on('click', this.onClick, this);
21977 this.picker().addClass('datepicker-dropdown');
21982 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21983 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21984 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21985 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21986 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21987 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21991 fireKey: function(e){
21992 if (!this.picker().isVisible()){
21993 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21999 e.preventDefault();
22007 this.onTogglePeriod();
22010 this.onIncrementMinutes();
22013 this.onDecrementMinutes();
22022 onClick: function(e) {
22023 e.stopPropagation();
22024 e.preventDefault();
22027 picker : function()
22029 return this.el.select('.datepicker', true).first();
22032 fillTime: function()
22034 var time = this.pop.select('tbody', true).first();
22036 time.dom.innerHTML = '';
22051 cls: 'hours-up glyphicon glyphicon-chevron-up'
22071 cls: 'minutes-up glyphicon glyphicon-chevron-up'
22092 cls: 'timepicker-hour',
22107 cls: 'timepicker-minute',
22122 cls: 'btn btn-primary period',
22144 cls: 'hours-down glyphicon glyphicon-chevron-down'
22164 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22182 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22189 var hours = this.time.getHours();
22190 var minutes = this.time.getMinutes();
22203 hours = hours - 12;
22207 hours = '0' + hours;
22211 minutes = '0' + minutes;
22214 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22215 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22216 this.pop.select('button', true).first().dom.innerHTML = period;
22222 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22224 var cls = ['bottom'];
22226 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22233 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22238 this.picker().addClass(cls.join('-'));
22242 Roo.each(cls, function(c){
22244 _this.picker().setTop(_this.inputEl().getHeight());
22248 _this.picker().setTop(0 - _this.picker().getHeight());
22253 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22257 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22264 onFocus : function()
22266 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22270 onBlur : function()
22272 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22278 this.picker().show();
22283 this.fireEvent('show', this, this.date);
22288 this.picker().hide();
22291 this.fireEvent('hide', this, this.date);
22294 setTime : function()
22297 this.setValue(this.time.format(this.format));
22299 this.fireEvent('select', this, this.date);
22304 onMousedown: function(e){
22305 e.stopPropagation();
22306 e.preventDefault();
22309 onIncrementHours: function()
22311 Roo.log('onIncrementHours');
22312 this.time = this.time.add(Date.HOUR, 1);
22317 onDecrementHours: function()
22319 Roo.log('onDecrementHours');
22320 this.time = this.time.add(Date.HOUR, -1);
22324 onIncrementMinutes: function()
22326 Roo.log('onIncrementMinutes');
22327 this.time = this.time.add(Date.MINUTE, 1);
22331 onDecrementMinutes: function()
22333 Roo.log('onDecrementMinutes');
22334 this.time = this.time.add(Date.MINUTE, -1);
22338 onTogglePeriod: function()
22340 Roo.log('onTogglePeriod');
22341 this.time = this.time.add(Date.HOUR, 12);
22348 Roo.apply(Roo.bootstrap.TimeField, {
22378 cls: 'btn btn-info ok',
22390 Roo.apply(Roo.bootstrap.TimeField, {
22394 cls: 'datepicker dropdown-menu',
22398 cls: 'datepicker-time',
22402 cls: 'table-condensed',
22404 Roo.bootstrap.TimeField.content,
22405 Roo.bootstrap.TimeField.footer
22424 * @class Roo.bootstrap.MonthField
22425 * @extends Roo.bootstrap.Input
22426 * Bootstrap MonthField class
22428 * @cfg {String} language default en
22431 * Create a new MonthField
22432 * @param {Object} config The config object
22435 Roo.bootstrap.MonthField = function(config){
22436 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22441 * Fires when this field show.
22442 * @param {Roo.bootstrap.MonthField} this
22443 * @param {Mixed} date The date value
22448 * Fires when this field hide.
22449 * @param {Roo.bootstrap.MonthField} this
22450 * @param {Mixed} date The date value
22455 * Fires when select a date.
22456 * @param {Roo.bootstrap.MonthField} this
22457 * @param {String} oldvalue The old value
22458 * @param {String} newvalue The new value
22464 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22466 onRender: function(ct, position)
22469 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22471 this.language = this.language || 'en';
22472 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22473 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22475 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22476 this.isInline = false;
22477 this.isInput = true;
22478 this.component = this.el.select('.add-on', true).first() || false;
22479 this.component = (this.component && this.component.length === 0) ? false : this.component;
22480 this.hasInput = this.component && this.inputEL().length;
22482 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22486 this.picker().on('mousedown', this.onMousedown, this);
22487 this.picker().on('click', this.onClick, this);
22489 this.picker().addClass('datepicker-dropdown');
22491 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22492 v.setStyle('width', '189px');
22499 if(this.isInline) {
22505 setValue: function(v, suppressEvent)
22507 var o = this.getValue();
22509 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22513 if(suppressEvent !== true){
22514 this.fireEvent('select', this, o, v);
22519 getValue: function()
22524 onClick: function(e)
22526 e.stopPropagation();
22527 e.preventDefault();
22529 var target = e.getTarget();
22531 if(target.nodeName.toLowerCase() === 'i'){
22532 target = Roo.get(target).dom.parentNode;
22535 var nodeName = target.nodeName;
22536 var className = target.className;
22537 var html = target.innerHTML;
22539 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22543 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22545 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22551 picker : function()
22553 return this.pickerEl;
22556 fillMonths: function()
22559 var months = this.picker().select('>.datepicker-months td', true).first();
22561 months.dom.innerHTML = '';
22567 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22570 months.createChild(month);
22579 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22580 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22583 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22584 e.removeClass('active');
22586 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22587 e.addClass('active');
22594 if(this.isInline) {
22598 this.picker().removeClass(['bottom', 'top']);
22600 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22602 * place to the top of element!
22606 this.picker().addClass('top');
22607 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22612 this.picker().addClass('bottom');
22614 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22617 onFocus : function()
22619 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22623 onBlur : function()
22625 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22627 var d = this.inputEl().getValue();
22636 this.picker().show();
22637 this.picker().select('>.datepicker-months', true).first().show();
22641 this.fireEvent('show', this, this.date);
22646 if(this.isInline) {
22649 this.picker().hide();
22650 this.fireEvent('hide', this, this.date);
22654 onMousedown: function(e)
22656 e.stopPropagation();
22657 e.preventDefault();
22662 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22666 fireKey: function(e)
22668 if (!this.picker().isVisible()){
22669 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22680 e.preventDefault();
22684 dir = e.keyCode == 37 ? -1 : 1;
22686 this.vIndex = this.vIndex + dir;
22688 if(this.vIndex < 0){
22692 if(this.vIndex > 11){
22696 if(isNaN(this.vIndex)){
22700 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22706 dir = e.keyCode == 38 ? -1 : 1;
22708 this.vIndex = this.vIndex + dir * 4;
22710 if(this.vIndex < 0){
22714 if(this.vIndex > 11){
22718 if(isNaN(this.vIndex)){
22722 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22727 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22728 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22732 e.preventDefault();
22735 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22736 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22752 this.picker().remove();
22757 Roo.apply(Roo.bootstrap.MonthField, {
22776 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22777 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22782 Roo.apply(Roo.bootstrap.MonthField, {
22786 cls: 'datepicker dropdown-menu roo-dynamic',
22790 cls: 'datepicker-months',
22794 cls: 'table-condensed',
22796 Roo.bootstrap.DateField.content
22816 * @class Roo.bootstrap.CheckBox
22817 * @extends Roo.bootstrap.Input
22818 * Bootstrap CheckBox class
22820 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22821 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22822 * @cfg {String} boxLabel The text that appears beside the checkbox
22823 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22824 * @cfg {Boolean} checked initnal the element
22825 * @cfg {Boolean} inline inline the element (default false)
22826 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22827 * @cfg {String} tooltip label tooltip
22830 * Create a new CheckBox
22831 * @param {Object} config The config object
22834 Roo.bootstrap.CheckBox = function(config){
22835 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22840 * Fires when the element is checked or unchecked.
22841 * @param {Roo.bootstrap.CheckBox} this This input
22842 * @param {Boolean} checked The new checked value
22847 * Fires when the element is click.
22848 * @param {Roo.bootstrap.CheckBox} this This input
22855 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22857 inputType: 'checkbox',
22866 // checkbox success does not make any sense really..
22871 getAutoCreate : function()
22873 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22879 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22882 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22888 type : this.inputType,
22889 value : this.inputValue,
22890 cls : 'roo-' + this.inputType, //'form-box',
22891 placeholder : this.placeholder || ''
22895 if(this.inputType != 'radio'){
22899 cls : 'roo-hidden-value',
22900 value : this.checked ? this.inputValue : this.valueOff
22905 if (this.weight) { // Validity check?
22906 cfg.cls += " " + this.inputType + "-" + this.weight;
22909 if (this.disabled) {
22910 input.disabled=true;
22914 input.checked = this.checked;
22919 input.name = this.name;
22921 if(this.inputType != 'radio'){
22922 hidden.name = this.name;
22923 input.name = '_hidden_' + this.name;
22928 input.cls += ' input-' + this.size;
22933 ['xs','sm','md','lg'].map(function(size){
22934 if (settings[size]) {
22935 cfg.cls += ' col-' + size + '-' + settings[size];
22939 var inputblock = input;
22941 if (this.before || this.after) {
22944 cls : 'input-group',
22949 inputblock.cn.push({
22951 cls : 'input-group-addon',
22956 inputblock.cn.push(input);
22958 if(this.inputType != 'radio'){
22959 inputblock.cn.push(hidden);
22963 inputblock.cn.push({
22965 cls : 'input-group-addon',
22971 var boxLabelCfg = false;
22977 //'for': id, // box label is handled by onclick - so no for...
22979 html: this.boxLabel
22982 boxLabelCfg.tooltip = this.tooltip;
22988 if (align ==='left' && this.fieldLabel.length) {
22989 // Roo.log("left and has label");
22994 cls : 'control-label',
22995 html : this.fieldLabel
23006 cfg.cn[1].cn.push(boxLabelCfg);
23009 if(this.labelWidth > 12){
23010 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23013 if(this.labelWidth < 13 && this.labelmd == 0){
23014 this.labelmd = this.labelWidth;
23017 if(this.labellg > 0){
23018 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23019 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23022 if(this.labelmd > 0){
23023 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23024 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23027 if(this.labelsm > 0){
23028 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23029 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23032 if(this.labelxs > 0){
23033 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23034 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23037 } else if ( this.fieldLabel.length) {
23038 // Roo.log(" label");
23042 tag: this.boxLabel ? 'span' : 'label',
23044 cls: 'control-label box-input-label',
23045 //cls : 'input-group-addon',
23046 html : this.fieldLabel
23053 cfg.cn.push(boxLabelCfg);
23058 // Roo.log(" no label && no align");
23059 cfg.cn = [ inputblock ] ;
23061 cfg.cn.push(boxLabelCfg);
23069 if(this.inputType != 'radio'){
23070 cfg.cn.push(hidden);
23078 * return the real input element.
23080 inputEl: function ()
23082 return this.el.select('input.roo-' + this.inputType,true).first();
23084 hiddenEl: function ()
23086 return this.el.select('input.roo-hidden-value',true).first();
23089 labelEl: function()
23091 return this.el.select('label.control-label',true).first();
23093 /* depricated... */
23097 return this.labelEl();
23100 boxLabelEl: function()
23102 return this.el.select('label.box-label',true).first();
23105 initEvents : function()
23107 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23109 this.inputEl().on('click', this.onClick, this);
23111 if (this.boxLabel) {
23112 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23115 this.startValue = this.getValue();
23118 Roo.bootstrap.CheckBox.register(this);
23122 onClick : function(e)
23124 if(this.fireEvent('click', this, e) !== false){
23125 this.setChecked(!this.checked);
23130 setChecked : function(state,suppressEvent)
23132 this.startValue = this.getValue();
23134 if(this.inputType == 'radio'){
23136 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23137 e.dom.checked = false;
23140 this.inputEl().dom.checked = true;
23142 this.inputEl().dom.value = this.inputValue;
23144 if(suppressEvent !== true){
23145 this.fireEvent('check', this, true);
23153 this.checked = state;
23155 this.inputEl().dom.checked = state;
23158 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23160 if(suppressEvent !== true){
23161 this.fireEvent('check', this, state);
23167 getValue : function()
23169 if(this.inputType == 'radio'){
23170 return this.getGroupValue();
23173 return this.hiddenEl().dom.value;
23177 getGroupValue : function()
23179 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23183 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23186 setValue : function(v,suppressEvent)
23188 if(this.inputType == 'radio'){
23189 this.setGroupValue(v, suppressEvent);
23193 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23198 setGroupValue : function(v, suppressEvent)
23200 this.startValue = this.getValue();
23202 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23203 e.dom.checked = false;
23205 if(e.dom.value == v){
23206 e.dom.checked = true;
23210 if(suppressEvent !== true){
23211 this.fireEvent('check', this, true);
23219 validate : function()
23221 if(this.getVisibilityEl().hasClass('hidden')){
23227 (this.inputType == 'radio' && this.validateRadio()) ||
23228 (this.inputType == 'checkbox' && this.validateCheckbox())
23234 this.markInvalid();
23238 validateRadio : function()
23240 if(this.getVisibilityEl().hasClass('hidden')){
23244 if(this.allowBlank){
23250 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23251 if(!e.dom.checked){
23263 validateCheckbox : function()
23266 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23267 //return (this.getValue() == this.inputValue) ? true : false;
23270 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23278 for(var i in group){
23279 if(group[i].el.isVisible(true)){
23287 for(var i in group){
23292 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23299 * Mark this field as valid
23301 markValid : function()
23305 this.fireEvent('valid', this);
23307 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23310 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23317 if(this.inputType == 'radio'){
23318 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23319 var fg = e.findParent('.form-group', false, true);
23320 if (Roo.bootstrap.version == 3) {
23321 fg.removeClass([_this.invalidClass, _this.validClass]);
23322 fg.addClass(_this.validClass);
23324 fg.removeClass(['is-valid', 'is-invalid']);
23325 fg.addClass('is-valid');
23333 var fg = this.el.findParent('.form-group', false, true);
23334 if (Roo.bootstrap.version == 3) {
23335 fg.removeClass([this.invalidClass, this.validClass]);
23336 fg.addClass(this.validClass);
23338 fg.removeClass(['is-valid', 'is-invalid']);
23339 fg.addClass('is-valid');
23344 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23350 for(var i in group){
23351 var fg = group[i].el.findParent('.form-group', false, true);
23352 if (Roo.bootstrap.version == 3) {
23353 fg.removeClass([this.invalidClass, this.validClass]);
23354 fg.addClass(this.validClass);
23356 fg.removeClass(['is-valid', 'is-invalid']);
23357 fg.addClass('is-valid');
23363 * Mark this field as invalid
23364 * @param {String} msg The validation message
23366 markInvalid : function(msg)
23368 if(this.allowBlank){
23374 this.fireEvent('invalid', this, msg);
23376 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23379 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23383 label.markInvalid();
23386 if(this.inputType == 'radio'){
23388 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23389 var fg = e.findParent('.form-group', false, true);
23390 if (Roo.bootstrap.version == 3) {
23391 fg.removeClass([_this.invalidClass, _this.validClass]);
23392 fg.addClass(_this.invalidClass);
23394 fg.removeClass(['is-invalid', 'is-valid']);
23395 fg.addClass('is-invalid');
23403 var fg = this.el.findParent('.form-group', false, true);
23404 if (Roo.bootstrap.version == 3) {
23405 fg.removeClass([_this.invalidClass, _this.validClass]);
23406 fg.addClass(_this.invalidClass);
23408 fg.removeClass(['is-invalid', 'is-valid']);
23409 fg.addClass('is-invalid');
23414 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23420 for(var i in group){
23421 var fg = group[i].el.findParent('.form-group', false, true);
23422 if (Roo.bootstrap.version == 3) {
23423 fg.removeClass([_this.invalidClass, _this.validClass]);
23424 fg.addClass(_this.invalidClass);
23426 fg.removeClass(['is-invalid', 'is-valid']);
23427 fg.addClass('is-invalid');
23433 clearInvalid : function()
23435 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23437 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23439 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23441 if (label && label.iconEl) {
23442 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23443 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23447 disable : function()
23449 if(this.inputType != 'radio'){
23450 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23458 _this.getActionEl().addClass(this.disabledClass);
23459 e.dom.disabled = true;
23463 this.disabled = true;
23464 this.fireEvent("disable", this);
23468 enable : function()
23470 if(this.inputType != 'radio'){
23471 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23478 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23479 _this.getActionEl().removeClass(this.disabledClass);
23480 e.dom.disabled = false;
23484 this.disabled = false;
23485 this.fireEvent("enable", this);
23489 setBoxLabel : function(v)
23494 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23500 Roo.apply(Roo.bootstrap.CheckBox, {
23505 * register a CheckBox Group
23506 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23508 register : function(checkbox)
23510 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23511 this.groups[checkbox.groupId] = {};
23514 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23518 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23522 * fetch a CheckBox Group based on the group ID
23523 * @param {string} the group ID
23524 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23526 get: function(groupId) {
23527 if (typeof(this.groups[groupId]) == 'undefined') {
23531 return this.groups[groupId] ;
23544 * @class Roo.bootstrap.Radio
23545 * @extends Roo.bootstrap.Component
23546 * Bootstrap Radio class
23547 * @cfg {String} boxLabel - the label associated
23548 * @cfg {String} value - the value of radio
23551 * Create a new Radio
23552 * @param {Object} config The config object
23554 Roo.bootstrap.Radio = function(config){
23555 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23559 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23565 getAutoCreate : function()
23569 cls : 'form-group radio',
23574 html : this.boxLabel
23582 initEvents : function()
23584 this.parent().register(this);
23586 this.el.on('click', this.onClick, this);
23590 onClick : function(e)
23592 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23593 this.setChecked(true);
23597 setChecked : function(state, suppressEvent)
23599 this.parent().setValue(this.value, suppressEvent);
23603 setBoxLabel : function(v)
23608 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23623 * @class Roo.bootstrap.SecurePass
23624 * @extends Roo.bootstrap.Input
23625 * Bootstrap SecurePass class
23629 * Create a new SecurePass
23630 * @param {Object} config The config object
23633 Roo.bootstrap.SecurePass = function (config) {
23634 // these go here, so the translation tool can replace them..
23636 PwdEmpty: "Please type a password, and then retype it to confirm.",
23637 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23638 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23639 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23640 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23641 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23642 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23643 TooWeak: "Your password is Too Weak."
23645 this.meterLabel = "Password strength:";
23646 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23647 this.meterClass = [
23648 "roo-password-meter-tooweak",
23649 "roo-password-meter-weak",
23650 "roo-password-meter-medium",
23651 "roo-password-meter-strong",
23652 "roo-password-meter-grey"
23657 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23660 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23662 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23664 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23665 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23666 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23667 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23668 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23669 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23670 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23680 * @cfg {String/Object} Label for the strength meter (defaults to
23681 * 'Password strength:')
23686 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23687 * ['Weak', 'Medium', 'Strong'])
23690 pwdStrengths: false,
23703 initEvents: function ()
23705 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23707 if (this.el.is('input[type=password]') && Roo.isSafari) {
23708 this.el.on('keydown', this.SafariOnKeyDown, this);
23711 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23714 onRender: function (ct, position)
23716 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23717 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23718 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23720 this.trigger.createChild({
23725 cls: 'roo-password-meter-grey col-xs-12',
23728 //width: this.meterWidth + 'px'
23732 cls: 'roo-password-meter-text'
23738 if (this.hideTrigger) {
23739 this.trigger.setDisplayed(false);
23741 this.setSize(this.width || '', this.height || '');
23744 onDestroy: function ()
23746 if (this.trigger) {
23747 this.trigger.removeAllListeners();
23748 this.trigger.remove();
23751 this.wrap.remove();
23753 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23756 checkStrength: function ()
23758 var pwd = this.inputEl().getValue();
23759 if (pwd == this._lastPwd) {
23764 if (this.ClientSideStrongPassword(pwd)) {
23766 } else if (this.ClientSideMediumPassword(pwd)) {
23768 } else if (this.ClientSideWeakPassword(pwd)) {
23774 Roo.log('strength1: ' + strength);
23776 //var pm = this.trigger.child('div/div/div').dom;
23777 var pm = this.trigger.child('div/div');
23778 pm.removeClass(this.meterClass);
23779 pm.addClass(this.meterClass[strength]);
23782 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23784 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23786 this._lastPwd = pwd;
23790 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23792 this._lastPwd = '';
23794 var pm = this.trigger.child('div/div');
23795 pm.removeClass(this.meterClass);
23796 pm.addClass('roo-password-meter-grey');
23799 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23802 this.inputEl().dom.type='password';
23805 validateValue: function (value)
23807 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23810 if (value.length == 0) {
23811 if (this.allowBlank) {
23812 this.clearInvalid();
23816 this.markInvalid(this.errors.PwdEmpty);
23817 this.errorMsg = this.errors.PwdEmpty;
23825 if (!value.match(/[\x21-\x7e]+/)) {
23826 this.markInvalid(this.errors.PwdBadChar);
23827 this.errorMsg = this.errors.PwdBadChar;
23830 if (value.length < 6) {
23831 this.markInvalid(this.errors.PwdShort);
23832 this.errorMsg = this.errors.PwdShort;
23835 if (value.length > 16) {
23836 this.markInvalid(this.errors.PwdLong);
23837 this.errorMsg = this.errors.PwdLong;
23841 if (this.ClientSideStrongPassword(value)) {
23843 } else if (this.ClientSideMediumPassword(value)) {
23845 } else if (this.ClientSideWeakPassword(value)) {
23852 if (strength < 2) {
23853 //this.markInvalid(this.errors.TooWeak);
23854 this.errorMsg = this.errors.TooWeak;
23859 console.log('strength2: ' + strength);
23861 //var pm = this.trigger.child('div/div/div').dom;
23863 var pm = this.trigger.child('div/div');
23864 pm.removeClass(this.meterClass);
23865 pm.addClass(this.meterClass[strength]);
23867 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23869 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23871 this.errorMsg = '';
23875 CharacterSetChecks: function (type)
23878 this.fResult = false;
23881 isctype: function (character, type)
23884 case this.kCapitalLetter:
23885 if (character >= 'A' && character <= 'Z') {
23890 case this.kSmallLetter:
23891 if (character >= 'a' && character <= 'z') {
23897 if (character >= '0' && character <= '9') {
23902 case this.kPunctuation:
23903 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23914 IsLongEnough: function (pwd, size)
23916 return !(pwd == null || isNaN(size) || pwd.length < size);
23919 SpansEnoughCharacterSets: function (word, nb)
23921 if (!this.IsLongEnough(word, nb))
23926 var characterSetChecks = new Array(
23927 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23928 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23931 for (var index = 0; index < word.length; ++index) {
23932 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23933 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23934 characterSetChecks[nCharSet].fResult = true;
23941 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23942 if (characterSetChecks[nCharSet].fResult) {
23947 if (nCharSets < nb) {
23953 ClientSideStrongPassword: function (pwd)
23955 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23958 ClientSideMediumPassword: function (pwd)
23960 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23963 ClientSideWeakPassword: function (pwd)
23965 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23968 })//<script type="text/javascript">
23971 * Based Ext JS Library 1.1.1
23972 * Copyright(c) 2006-2007, Ext JS, LLC.
23978 * @class Roo.HtmlEditorCore
23979 * @extends Roo.Component
23980 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23982 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23985 Roo.HtmlEditorCore = function(config){
23988 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23993 * @event initialize
23994 * Fires when the editor is fully initialized (including the iframe)
23995 * @param {Roo.HtmlEditorCore} this
24000 * Fires when the editor is first receives the focus. Any insertion must wait
24001 * until after this event.
24002 * @param {Roo.HtmlEditorCore} this
24006 * @event beforesync
24007 * Fires before the textarea is updated with content from the editor iframe. Return false
24008 * to cancel the sync.
24009 * @param {Roo.HtmlEditorCore} this
24010 * @param {String} html
24014 * @event beforepush
24015 * Fires before the iframe editor is updated with content from the textarea. Return false
24016 * to cancel the push.
24017 * @param {Roo.HtmlEditorCore} this
24018 * @param {String} html
24023 * Fires when the textarea is updated with content from the editor iframe.
24024 * @param {Roo.HtmlEditorCore} this
24025 * @param {String} html
24030 * Fires when the iframe editor is updated with content from the textarea.
24031 * @param {Roo.HtmlEditorCore} this
24032 * @param {String} html
24037 * @event editorevent
24038 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24039 * @param {Roo.HtmlEditorCore} this
24045 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24047 // defaults : white / black...
24048 this.applyBlacklists();
24055 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24059 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24065 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24070 * @cfg {Number} height (in pixels)
24074 * @cfg {Number} width (in pixels)
24079 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24082 stylesheets: false,
24087 // private properties
24088 validationEvent : false,
24090 initialized : false,
24092 sourceEditMode : false,
24093 onFocus : Roo.emptyFn,
24095 hideMode:'offsets',
24099 // blacklist + whitelisted elements..
24106 * Protected method that will not generally be called directly. It
24107 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24108 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24110 getDocMarkup : function(){
24114 // inherit styels from page...??
24115 if (this.stylesheets === false) {
24117 Roo.get(document.head).select('style').each(function(node) {
24118 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24121 Roo.get(document.head).select('link').each(function(node) {
24122 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24125 } else if (!this.stylesheets.length) {
24127 st = '<style type="text/css">' +
24128 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24131 for (var i in this.stylesheets) {
24132 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24137 st += '<style type="text/css">' +
24138 'IMG { cursor: pointer } ' +
24141 var cls = 'roo-htmleditor-body';
24143 if(this.bodyCls.length){
24144 cls += ' ' + this.bodyCls;
24147 return '<html><head>' + st +
24148 //<style type="text/css">' +
24149 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24151 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24155 onRender : function(ct, position)
24158 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24159 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24162 this.el.dom.style.border = '0 none';
24163 this.el.dom.setAttribute('tabIndex', -1);
24164 this.el.addClass('x-hidden hide');
24168 if(Roo.isIE){ // fix IE 1px bogus margin
24169 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24173 this.frameId = Roo.id();
24177 var iframe = this.owner.wrap.createChild({
24179 cls: 'form-control', // bootstrap..
24181 name: this.frameId,
24182 frameBorder : 'no',
24183 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24188 this.iframe = iframe.dom;
24190 this.assignDocWin();
24192 this.doc.designMode = 'on';
24195 this.doc.write(this.getDocMarkup());
24199 var task = { // must defer to wait for browser to be ready
24201 //console.log("run task?" + this.doc.readyState);
24202 this.assignDocWin();
24203 if(this.doc.body || this.doc.readyState == 'complete'){
24205 this.doc.designMode="on";
24209 Roo.TaskMgr.stop(task);
24210 this.initEditor.defer(10, this);
24217 Roo.TaskMgr.start(task);
24222 onResize : function(w, h)
24224 Roo.log('resize: ' +w + ',' + h );
24225 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24229 if(typeof w == 'number'){
24231 this.iframe.style.width = w + 'px';
24233 if(typeof h == 'number'){
24235 this.iframe.style.height = h + 'px';
24237 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24244 * Toggles the editor between standard and source edit mode.
24245 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24247 toggleSourceEdit : function(sourceEditMode){
24249 this.sourceEditMode = sourceEditMode === true;
24251 if(this.sourceEditMode){
24253 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24256 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24257 //this.iframe.className = '';
24260 //this.setSize(this.owner.wrap.getSize());
24261 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24268 * Protected method that will not generally be called directly. If you need/want
24269 * custom HTML cleanup, this is the method you should override.
24270 * @param {String} html The HTML to be cleaned
24271 * return {String} The cleaned HTML
24273 cleanHtml : function(html){
24274 html = String(html);
24275 if(html.length > 5){
24276 if(Roo.isSafari){ // strip safari nonsense
24277 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24280 if(html == ' '){
24287 * HTML Editor -> Textarea
24288 * Protected method that will not generally be called directly. Syncs the contents
24289 * of the editor iframe with the textarea.
24291 syncValue : function(){
24292 if(this.initialized){
24293 var bd = (this.doc.body || this.doc.documentElement);
24294 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24295 var html = bd.innerHTML;
24297 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24298 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24300 html = '<div style="'+m[0]+'">' + html + '</div>';
24303 html = this.cleanHtml(html);
24304 // fix up the special chars.. normaly like back quotes in word...
24305 // however we do not want to do this with chinese..
24306 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24308 var cc = match.charCodeAt();
24310 // Get the character value, handling surrogate pairs
24311 if (match.length == 2) {
24312 // It's a surrogate pair, calculate the Unicode code point
24313 var high = match.charCodeAt(0) - 0xD800;
24314 var low = match.charCodeAt(1) - 0xDC00;
24315 cc = (high * 0x400) + low + 0x10000;
24317 (cc >= 0x4E00 && cc < 0xA000 ) ||
24318 (cc >= 0x3400 && cc < 0x4E00 ) ||
24319 (cc >= 0xf900 && cc < 0xfb00 )
24324 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24325 return "&#" + cc + ";";
24332 if(this.owner.fireEvent('beforesync', this, html) !== false){
24333 this.el.dom.value = html;
24334 this.owner.fireEvent('sync', this, html);
24340 * Protected method that will not generally be called directly. Pushes the value of the textarea
24341 * into the iframe editor.
24343 pushValue : function(){
24344 if(this.initialized){
24345 var v = this.el.dom.value.trim();
24347 // if(v.length < 1){
24351 if(this.owner.fireEvent('beforepush', this, v) !== false){
24352 var d = (this.doc.body || this.doc.documentElement);
24354 this.cleanUpPaste();
24355 this.el.dom.value = d.innerHTML;
24356 this.owner.fireEvent('push', this, v);
24362 deferFocus : function(){
24363 this.focus.defer(10, this);
24367 focus : function(){
24368 if(this.win && !this.sourceEditMode){
24375 assignDocWin: function()
24377 var iframe = this.iframe;
24380 this.doc = iframe.contentWindow.document;
24381 this.win = iframe.contentWindow;
24383 // if (!Roo.get(this.frameId)) {
24386 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24387 // this.win = Roo.get(this.frameId).dom.contentWindow;
24389 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24393 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24394 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24399 initEditor : function(){
24400 //console.log("INIT EDITOR");
24401 this.assignDocWin();
24405 this.doc.designMode="on";
24407 this.doc.write(this.getDocMarkup());
24410 var dbody = (this.doc.body || this.doc.documentElement);
24411 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24412 // this copies styles from the containing element into thsi one..
24413 // not sure why we need all of this..
24414 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24416 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24417 //ss['background-attachment'] = 'fixed'; // w3c
24418 dbody.bgProperties = 'fixed'; // ie
24419 //Roo.DomHelper.applyStyles(dbody, ss);
24420 Roo.EventManager.on(this.doc, {
24421 //'mousedown': this.onEditorEvent,
24422 'mouseup': this.onEditorEvent,
24423 'dblclick': this.onEditorEvent,
24424 'click': this.onEditorEvent,
24425 'keyup': this.onEditorEvent,
24430 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24432 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24433 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24435 this.initialized = true;
24437 this.owner.fireEvent('initialize', this);
24442 onDestroy : function(){
24448 //for (var i =0; i < this.toolbars.length;i++) {
24449 // // fixme - ask toolbars for heights?
24450 // this.toolbars[i].onDestroy();
24453 //this.wrap.dom.innerHTML = '';
24454 //this.wrap.remove();
24459 onFirstFocus : function(){
24461 this.assignDocWin();
24464 this.activated = true;
24467 if(Roo.isGecko){ // prevent silly gecko errors
24469 var s = this.win.getSelection();
24470 if(!s.focusNode || s.focusNode.nodeType != 3){
24471 var r = s.getRangeAt(0);
24472 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24477 this.execCmd('useCSS', true);
24478 this.execCmd('styleWithCSS', false);
24481 this.owner.fireEvent('activate', this);
24485 adjustFont: function(btn){
24486 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24487 //if(Roo.isSafari){ // safari
24490 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24491 if(Roo.isSafari){ // safari
24492 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24493 v = (v < 10) ? 10 : v;
24494 v = (v > 48) ? 48 : v;
24495 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24500 v = Math.max(1, v+adjust);
24502 this.execCmd('FontSize', v );
24505 onEditorEvent : function(e)
24507 this.owner.fireEvent('editorevent', this, e);
24508 // this.updateToolbar();
24509 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24512 insertTag : function(tg)
24514 // could be a bit smarter... -> wrap the current selected tRoo..
24515 if (tg.toLowerCase() == 'span' ||
24516 tg.toLowerCase() == 'code' ||
24517 tg.toLowerCase() == 'sup' ||
24518 tg.toLowerCase() == 'sub'
24521 range = this.createRange(this.getSelection());
24522 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24523 wrappingNode.appendChild(range.extractContents());
24524 range.insertNode(wrappingNode);
24531 this.execCmd("formatblock", tg);
24535 insertText : function(txt)
24539 var range = this.createRange();
24540 range.deleteContents();
24541 //alert(Sender.getAttribute('label'));
24543 range.insertNode(this.doc.createTextNode(txt));
24549 * Executes a Midas editor command on the editor document and performs necessary focus and
24550 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24551 * @param {String} cmd The Midas command
24552 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24554 relayCmd : function(cmd, value){
24556 this.execCmd(cmd, value);
24557 this.owner.fireEvent('editorevent', this);
24558 //this.updateToolbar();
24559 this.owner.deferFocus();
24563 * Executes a Midas editor command directly on the editor document.
24564 * For visual commands, you should use {@link #relayCmd} instead.
24565 * <b>This should only be called after the editor is initialized.</b>
24566 * @param {String} cmd The Midas command
24567 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24569 execCmd : function(cmd, value){
24570 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24577 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24579 * @param {String} text | dom node..
24581 insertAtCursor : function(text)
24584 if(!this.activated){
24590 var r = this.doc.selection.createRange();
24601 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24605 // from jquery ui (MIT licenced)
24607 var win = this.win;
24609 if (win.getSelection && win.getSelection().getRangeAt) {
24610 range = win.getSelection().getRangeAt(0);
24611 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24612 range.insertNode(node);
24613 } else if (win.document.selection && win.document.selection.createRange) {
24614 // no firefox support
24615 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24616 win.document.selection.createRange().pasteHTML(txt);
24618 // no firefox support
24619 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24620 this.execCmd('InsertHTML', txt);
24629 mozKeyPress : function(e){
24631 var c = e.getCharCode(), cmd;
24634 c = String.fromCharCode(c).toLowerCase();
24648 this.cleanUpPaste.defer(100, this);
24656 e.preventDefault();
24664 fixKeys : function(){ // load time branching for fastest keydown performance
24666 return function(e){
24667 var k = e.getKey(), r;
24670 r = this.doc.selection.createRange();
24673 r.pasteHTML('    ');
24680 r = this.doc.selection.createRange();
24682 var target = r.parentElement();
24683 if(!target || target.tagName.toLowerCase() != 'li'){
24685 r.pasteHTML('<br />');
24691 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24692 this.cleanUpPaste.defer(100, this);
24698 }else if(Roo.isOpera){
24699 return function(e){
24700 var k = e.getKey();
24704 this.execCmd('InsertHTML','    ');
24707 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24708 this.cleanUpPaste.defer(100, this);
24713 }else if(Roo.isSafari){
24714 return function(e){
24715 var k = e.getKey();
24719 this.execCmd('InsertText','\t');
24723 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24724 this.cleanUpPaste.defer(100, this);
24732 getAllAncestors: function()
24734 var p = this.getSelectedNode();
24737 a.push(p); // push blank onto stack..
24738 p = this.getParentElement();
24742 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24746 a.push(this.doc.body);
24750 lastSelNode : false,
24753 getSelection : function()
24755 this.assignDocWin();
24756 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24759 getSelectedNode: function()
24761 // this may only work on Gecko!!!
24763 // should we cache this!!!!
24768 var range = this.createRange(this.getSelection()).cloneRange();
24771 var parent = range.parentElement();
24773 var testRange = range.duplicate();
24774 testRange.moveToElementText(parent);
24775 if (testRange.inRange(range)) {
24778 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24781 parent = parent.parentElement;
24786 // is ancestor a text element.
24787 var ac = range.commonAncestorContainer;
24788 if (ac.nodeType == 3) {
24789 ac = ac.parentNode;
24792 var ar = ac.childNodes;
24795 var other_nodes = [];
24796 var has_other_nodes = false;
24797 for (var i=0;i<ar.length;i++) {
24798 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24801 // fullly contained node.
24803 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24808 // probably selected..
24809 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24810 other_nodes.push(ar[i]);
24814 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24819 has_other_nodes = true;
24821 if (!nodes.length && other_nodes.length) {
24822 nodes= other_nodes;
24824 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24830 createRange: function(sel)
24832 // this has strange effects when using with
24833 // top toolbar - not sure if it's a great idea.
24834 //this.editor.contentWindow.focus();
24835 if (typeof sel != "undefined") {
24837 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24839 return this.doc.createRange();
24842 return this.doc.createRange();
24845 getParentElement: function()
24848 this.assignDocWin();
24849 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24851 var range = this.createRange(sel);
24854 var p = range.commonAncestorContainer;
24855 while (p.nodeType == 3) { // text node
24866 * Range intersection.. the hard stuff...
24870 * [ -- selected range --- ]
24874 * if end is before start or hits it. fail.
24875 * if start is after end or hits it fail.
24877 * if either hits (but other is outside. - then it's not
24883 // @see http://www.thismuchiknow.co.uk/?p=64.
24884 rangeIntersectsNode : function(range, node)
24886 var nodeRange = node.ownerDocument.createRange();
24888 nodeRange.selectNode(node);
24890 nodeRange.selectNodeContents(node);
24893 var rangeStartRange = range.cloneRange();
24894 rangeStartRange.collapse(true);
24896 var rangeEndRange = range.cloneRange();
24897 rangeEndRange.collapse(false);
24899 var nodeStartRange = nodeRange.cloneRange();
24900 nodeStartRange.collapse(true);
24902 var nodeEndRange = nodeRange.cloneRange();
24903 nodeEndRange.collapse(false);
24905 return rangeStartRange.compareBoundaryPoints(
24906 Range.START_TO_START, nodeEndRange) == -1 &&
24907 rangeEndRange.compareBoundaryPoints(
24908 Range.START_TO_START, nodeStartRange) == 1;
24912 rangeCompareNode : function(range, node)
24914 var nodeRange = node.ownerDocument.createRange();
24916 nodeRange.selectNode(node);
24918 nodeRange.selectNodeContents(node);
24922 range.collapse(true);
24924 nodeRange.collapse(true);
24926 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24927 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24929 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24931 var nodeIsBefore = ss == 1;
24932 var nodeIsAfter = ee == -1;
24934 if (nodeIsBefore && nodeIsAfter) {
24937 if (!nodeIsBefore && nodeIsAfter) {
24938 return 1; //right trailed.
24941 if (nodeIsBefore && !nodeIsAfter) {
24942 return 2; // left trailed.
24948 // private? - in a new class?
24949 cleanUpPaste : function()
24951 // cleans up the whole document..
24952 Roo.log('cleanuppaste');
24954 this.cleanUpChildren(this.doc.body);
24955 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24956 if (clean != this.doc.body.innerHTML) {
24957 this.doc.body.innerHTML = clean;
24962 cleanWordChars : function(input) {// change the chars to hex code
24963 var he = Roo.HtmlEditorCore;
24965 var output = input;
24966 Roo.each(he.swapCodes, function(sw) {
24967 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24969 output = output.replace(swapper, sw[1]);
24976 cleanUpChildren : function (n)
24978 if (!n.childNodes.length) {
24981 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24982 this.cleanUpChild(n.childNodes[i]);
24989 cleanUpChild : function (node)
24992 //console.log(node);
24993 if (node.nodeName == "#text") {
24994 // clean up silly Windows -- stuff?
24997 if (node.nodeName == "#comment") {
24998 node.parentNode.removeChild(node);
24999 // clean up silly Windows -- stuff?
25002 var lcname = node.tagName.toLowerCase();
25003 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25004 // whitelist of tags..
25006 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25008 node.parentNode.removeChild(node);
25013 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25015 // spans with no attributes - just remove them..
25016 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25017 remove_keep_children = true;
25020 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25021 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25023 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25024 // remove_keep_children = true;
25027 if (remove_keep_children) {
25028 this.cleanUpChildren(node);
25029 // inserts everything just before this node...
25030 while (node.childNodes.length) {
25031 var cn = node.childNodes[0];
25032 node.removeChild(cn);
25033 node.parentNode.insertBefore(cn, node);
25035 node.parentNode.removeChild(node);
25039 if (!node.attributes || !node.attributes.length) {
25044 this.cleanUpChildren(node);
25048 function cleanAttr(n,v)
25051 if (v.match(/^\./) || v.match(/^\//)) {
25054 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25057 if (v.match(/^#/)) {
25060 if (v.match(/^\{/)) { // allow template editing.
25063 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25064 node.removeAttribute(n);
25068 var cwhite = this.cwhite;
25069 var cblack = this.cblack;
25071 function cleanStyle(n,v)
25073 if (v.match(/expression/)) { //XSS?? should we even bother..
25074 node.removeAttribute(n);
25078 var parts = v.split(/;/);
25081 Roo.each(parts, function(p) {
25082 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25086 var l = p.split(':').shift().replace(/\s+/g,'');
25087 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25089 if ( cwhite.length && cblack.indexOf(l) > -1) {
25090 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25091 //node.removeAttribute(n);
25095 // only allow 'c whitelisted system attributes'
25096 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25097 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25098 //node.removeAttribute(n);
25108 if (clean.length) {
25109 node.setAttribute(n, clean.join(';'));
25111 node.removeAttribute(n);
25117 for (var i = node.attributes.length-1; i > -1 ; i--) {
25118 var a = node.attributes[i];
25121 if (a.name.toLowerCase().substr(0,2)=='on') {
25122 node.removeAttribute(a.name);
25125 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25126 node.removeAttribute(a.name);
25129 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25130 cleanAttr(a.name,a.value); // fixme..
25133 if (a.name == 'style') {
25134 cleanStyle(a.name,a.value);
25137 /// clean up MS crap..
25138 // tecnically this should be a list of valid class'es..
25141 if (a.name == 'class') {
25142 if (a.value.match(/^Mso/)) {
25143 node.removeAttribute('class');
25146 if (a.value.match(/^body$/)) {
25147 node.removeAttribute('class');
25158 this.cleanUpChildren(node);
25164 * Clean up MS wordisms...
25166 cleanWord : function(node)
25169 this.cleanWord(this.doc.body);
25174 node.nodeName == 'SPAN' &&
25175 !node.hasAttributes() &&
25176 node.childNodes.length == 1 &&
25177 node.firstChild.nodeName == "#text"
25179 var textNode = node.firstChild;
25180 node.removeChild(textNode);
25181 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25182 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25184 node.parentNode.insertBefore(textNode, node);
25185 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25186 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25188 node.parentNode.removeChild(node);
25191 if (node.nodeName == "#text") {
25192 // clean up silly Windows -- stuff?
25195 if (node.nodeName == "#comment") {
25196 node.parentNode.removeChild(node);
25197 // clean up silly Windows -- stuff?
25201 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25202 node.parentNode.removeChild(node);
25205 //Roo.log(node.tagName);
25206 // remove - but keep children..
25207 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25208 //Roo.log('-- removed');
25209 while (node.childNodes.length) {
25210 var cn = node.childNodes[0];
25211 node.removeChild(cn);
25212 node.parentNode.insertBefore(cn, node);
25213 // move node to parent - and clean it..
25214 this.cleanWord(cn);
25216 node.parentNode.removeChild(node);
25217 /// no need to iterate chidlren = it's got none..
25218 //this.iterateChildren(node, this.cleanWord);
25222 if (node.className.length) {
25224 var cn = node.className.split(/\W+/);
25226 Roo.each(cn, function(cls) {
25227 if (cls.match(/Mso[a-zA-Z]+/)) {
25232 node.className = cna.length ? cna.join(' ') : '';
25234 node.removeAttribute("class");
25238 if (node.hasAttribute("lang")) {
25239 node.removeAttribute("lang");
25242 if (node.hasAttribute("style")) {
25244 var styles = node.getAttribute("style").split(";");
25246 Roo.each(styles, function(s) {
25247 if (!s.match(/:/)) {
25250 var kv = s.split(":");
25251 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25254 // what ever is left... we allow.
25257 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25258 if (!nstyle.length) {
25259 node.removeAttribute('style');
25262 this.iterateChildren(node, this.cleanWord);
25268 * iterateChildren of a Node, calling fn each time, using this as the scole..
25269 * @param {DomNode} node node to iterate children of.
25270 * @param {Function} fn method of this class to call on each item.
25272 iterateChildren : function(node, fn)
25274 if (!node.childNodes.length) {
25277 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25278 fn.call(this, node.childNodes[i])
25284 * cleanTableWidths.
25286 * Quite often pasting from word etc.. results in tables with column and widths.
25287 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25290 cleanTableWidths : function(node)
25295 this.cleanTableWidths(this.doc.body);
25300 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25303 Roo.log(node.tagName);
25304 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25305 this.iterateChildren(node, this.cleanTableWidths);
25308 if (node.hasAttribute('width')) {
25309 node.removeAttribute('width');
25313 if (node.hasAttribute("style")) {
25316 var styles = node.getAttribute("style").split(";");
25318 Roo.each(styles, function(s) {
25319 if (!s.match(/:/)) {
25322 var kv = s.split(":");
25323 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25326 // what ever is left... we allow.
25329 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25330 if (!nstyle.length) {
25331 node.removeAttribute('style');
25335 this.iterateChildren(node, this.cleanTableWidths);
25343 domToHTML : function(currentElement, depth, nopadtext) {
25345 depth = depth || 0;
25346 nopadtext = nopadtext || false;
25348 if (!currentElement) {
25349 return this.domToHTML(this.doc.body);
25352 //Roo.log(currentElement);
25354 var allText = false;
25355 var nodeName = currentElement.nodeName;
25356 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25358 if (nodeName == '#text') {
25360 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25365 if (nodeName != 'BODY') {
25368 // Prints the node tagName, such as <A>, <IMG>, etc
25371 for(i = 0; i < currentElement.attributes.length;i++) {
25373 var aname = currentElement.attributes.item(i).name;
25374 if (!currentElement.attributes.item(i).value.length) {
25377 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25380 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25389 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25392 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25397 // Traverse the tree
25399 var currentElementChild = currentElement.childNodes.item(i);
25400 var allText = true;
25401 var innerHTML = '';
25403 while (currentElementChild) {
25404 // Formatting code (indent the tree so it looks nice on the screen)
25405 var nopad = nopadtext;
25406 if (lastnode == 'SPAN') {
25410 if (currentElementChild.nodeName == '#text') {
25411 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25412 toadd = nopadtext ? toadd : toadd.trim();
25413 if (!nopad && toadd.length > 80) {
25414 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25416 innerHTML += toadd;
25419 currentElementChild = currentElement.childNodes.item(i);
25425 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25427 // Recursively traverse the tree structure of the child node
25428 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25429 lastnode = currentElementChild.nodeName;
25431 currentElementChild=currentElement.childNodes.item(i);
25437 // The remaining code is mostly for formatting the tree
25438 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25443 ret+= "</"+tagName+">";
25449 applyBlacklists : function()
25451 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25452 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25456 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25457 if (b.indexOf(tag) > -1) {
25460 this.white.push(tag);
25464 Roo.each(w, function(tag) {
25465 if (b.indexOf(tag) > -1) {
25468 if (this.white.indexOf(tag) > -1) {
25471 this.white.push(tag);
25476 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25477 if (w.indexOf(tag) > -1) {
25480 this.black.push(tag);
25484 Roo.each(b, function(tag) {
25485 if (w.indexOf(tag) > -1) {
25488 if (this.black.indexOf(tag) > -1) {
25491 this.black.push(tag);
25496 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25497 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25501 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25502 if (b.indexOf(tag) > -1) {
25505 this.cwhite.push(tag);
25509 Roo.each(w, function(tag) {
25510 if (b.indexOf(tag) > -1) {
25513 if (this.cwhite.indexOf(tag) > -1) {
25516 this.cwhite.push(tag);
25521 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25522 if (w.indexOf(tag) > -1) {
25525 this.cblack.push(tag);
25529 Roo.each(b, function(tag) {
25530 if (w.indexOf(tag) > -1) {
25533 if (this.cblack.indexOf(tag) > -1) {
25536 this.cblack.push(tag);
25541 setStylesheets : function(stylesheets)
25543 if(typeof(stylesheets) == 'string'){
25544 Roo.get(this.iframe.contentDocument.head).createChild({
25546 rel : 'stylesheet',
25555 Roo.each(stylesheets, function(s) {
25560 Roo.get(_this.iframe.contentDocument.head).createChild({
25562 rel : 'stylesheet',
25571 removeStylesheets : function()
25575 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25580 setStyle : function(style)
25582 Roo.get(this.iframe.contentDocument.head).createChild({
25591 // hide stuff that is not compatible
25605 * @event specialkey
25609 * @cfg {String} fieldClass @hide
25612 * @cfg {String} focusClass @hide
25615 * @cfg {String} autoCreate @hide
25618 * @cfg {String} inputType @hide
25621 * @cfg {String} invalidClass @hide
25624 * @cfg {String} invalidText @hide
25627 * @cfg {String} msgFx @hide
25630 * @cfg {String} validateOnBlur @hide
25634 Roo.HtmlEditorCore.white = [
25635 'area', 'br', 'img', 'input', 'hr', 'wbr',
25637 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25638 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25639 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25640 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25641 'table', 'ul', 'xmp',
25643 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25646 'dir', 'menu', 'ol', 'ul', 'dl',
25652 Roo.HtmlEditorCore.black = [
25653 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25655 'base', 'basefont', 'bgsound', 'blink', 'body',
25656 'frame', 'frameset', 'head', 'html', 'ilayer',
25657 'iframe', 'layer', 'link', 'meta', 'object',
25658 'script', 'style' ,'title', 'xml' // clean later..
25660 Roo.HtmlEditorCore.clean = [
25661 'script', 'style', 'title', 'xml'
25663 Roo.HtmlEditorCore.remove = [
25668 Roo.HtmlEditorCore.ablack = [
25672 Roo.HtmlEditorCore.aclean = [
25673 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25677 Roo.HtmlEditorCore.pwhite= [
25678 'http', 'https', 'mailto'
25681 // white listed style attributes.
25682 Roo.HtmlEditorCore.cwhite= [
25683 // 'text-align', /// default is to allow most things..
25689 // black listed style attributes.
25690 Roo.HtmlEditorCore.cblack= [
25691 // 'font-size' -- this can be set by the project
25695 Roo.HtmlEditorCore.swapCodes =[
25714 * @class Roo.bootstrap.HtmlEditor
25715 * @extends Roo.bootstrap.TextArea
25716 * Bootstrap HtmlEditor class
25719 * Create a new HtmlEditor
25720 * @param {Object} config The config object
25723 Roo.bootstrap.HtmlEditor = function(config){
25724 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25725 if (!this.toolbars) {
25726 this.toolbars = [];
25729 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25732 * @event initialize
25733 * Fires when the editor is fully initialized (including the iframe)
25734 * @param {HtmlEditor} this
25739 * Fires when the editor is first receives the focus. Any insertion must wait
25740 * until after this event.
25741 * @param {HtmlEditor} this
25745 * @event beforesync
25746 * Fires before the textarea is updated with content from the editor iframe. Return false
25747 * to cancel the sync.
25748 * @param {HtmlEditor} this
25749 * @param {String} html
25753 * @event beforepush
25754 * Fires before the iframe editor is updated with content from the textarea. Return false
25755 * to cancel the push.
25756 * @param {HtmlEditor} this
25757 * @param {String} html
25762 * Fires when the textarea is updated with content from the editor iframe.
25763 * @param {HtmlEditor} this
25764 * @param {String} html
25769 * Fires when the iframe editor is updated with content from the textarea.
25770 * @param {HtmlEditor} this
25771 * @param {String} html
25775 * @event editmodechange
25776 * Fires when the editor switches edit modes
25777 * @param {HtmlEditor} this
25778 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25780 editmodechange: true,
25782 * @event editorevent
25783 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25784 * @param {HtmlEditor} this
25788 * @event firstfocus
25789 * Fires when on first focus - needed by toolbars..
25790 * @param {HtmlEditor} this
25795 * Auto save the htmlEditor value as a file into Events
25796 * @param {HtmlEditor} this
25800 * @event savedpreview
25801 * preview the saved version of htmlEditor
25802 * @param {HtmlEditor} this
25809 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25813 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25818 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25823 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25828 * @cfg {Number} height (in pixels)
25832 * @cfg {Number} width (in pixels)
25837 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25840 stylesheets: false,
25845 // private properties
25846 validationEvent : false,
25848 initialized : false,
25851 onFocus : Roo.emptyFn,
25853 hideMode:'offsets',
25855 tbContainer : false,
25859 toolbarContainer :function() {
25860 return this.wrap.select('.x-html-editor-tb',true).first();
25864 * Protected method that will not generally be called directly. It
25865 * is called when the editor creates its toolbar. Override this method if you need to
25866 * add custom toolbar buttons.
25867 * @param {HtmlEditor} editor
25869 createToolbar : function(){
25870 Roo.log('renewing');
25871 Roo.log("create toolbars");
25873 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25874 this.toolbars[0].render(this.toolbarContainer());
25878 // if (!editor.toolbars || !editor.toolbars.length) {
25879 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25882 // for (var i =0 ; i < editor.toolbars.length;i++) {
25883 // editor.toolbars[i] = Roo.factory(
25884 // typeof(editor.toolbars[i]) == 'string' ?
25885 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25886 // Roo.bootstrap.HtmlEditor);
25887 // editor.toolbars[i].init(editor);
25893 onRender : function(ct, position)
25895 // Roo.log("Call onRender: " + this.xtype);
25897 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25899 this.wrap = this.inputEl().wrap({
25900 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25903 this.editorcore.onRender(ct, position);
25905 if (this.resizable) {
25906 this.resizeEl = new Roo.Resizable(this.wrap, {
25910 minHeight : this.height,
25911 height: this.height,
25912 handles : this.resizable,
25915 resize : function(r, w, h) {
25916 _t.onResize(w,h); // -something
25922 this.createToolbar(this);
25925 if(!this.width && this.resizable){
25926 this.setSize(this.wrap.getSize());
25928 if (this.resizeEl) {
25929 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25930 // should trigger onReize..
25936 onResize : function(w, h)
25938 Roo.log('resize: ' +w + ',' + h );
25939 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25943 if(this.inputEl() ){
25944 if(typeof w == 'number'){
25945 var aw = w - this.wrap.getFrameWidth('lr');
25946 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25949 if(typeof h == 'number'){
25950 var tbh = -11; // fixme it needs to tool bar size!
25951 for (var i =0; i < this.toolbars.length;i++) {
25952 // fixme - ask toolbars for heights?
25953 tbh += this.toolbars[i].el.getHeight();
25954 //if (this.toolbars[i].footer) {
25955 // tbh += this.toolbars[i].footer.el.getHeight();
25963 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25964 ah -= 5; // knock a few pixes off for look..
25965 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25969 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25970 this.editorcore.onResize(ew,eh);
25975 * Toggles the editor between standard and source edit mode.
25976 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25978 toggleSourceEdit : function(sourceEditMode)
25980 this.editorcore.toggleSourceEdit(sourceEditMode);
25982 if(this.editorcore.sourceEditMode){
25983 Roo.log('editor - showing textarea');
25986 // Roo.log(this.syncValue());
25988 this.inputEl().removeClass(['hide', 'x-hidden']);
25989 this.inputEl().dom.removeAttribute('tabIndex');
25990 this.inputEl().focus();
25992 Roo.log('editor - hiding textarea');
25994 // Roo.log(this.pushValue());
25997 this.inputEl().addClass(['hide', 'x-hidden']);
25998 this.inputEl().dom.setAttribute('tabIndex', -1);
25999 //this.deferFocus();
26002 if(this.resizable){
26003 this.setSize(this.wrap.getSize());
26006 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26009 // private (for BoxComponent)
26010 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26012 // private (for BoxComponent)
26013 getResizeEl : function(){
26017 // private (for BoxComponent)
26018 getPositionEl : function(){
26023 initEvents : function(){
26024 this.originalValue = this.getValue();
26028 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26031 // markInvalid : Roo.emptyFn,
26033 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26036 // clearInvalid : Roo.emptyFn,
26038 setValue : function(v){
26039 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26040 this.editorcore.pushValue();
26045 deferFocus : function(){
26046 this.focus.defer(10, this);
26050 focus : function(){
26051 this.editorcore.focus();
26057 onDestroy : function(){
26063 for (var i =0; i < this.toolbars.length;i++) {
26064 // fixme - ask toolbars for heights?
26065 this.toolbars[i].onDestroy();
26068 this.wrap.dom.innerHTML = '';
26069 this.wrap.remove();
26074 onFirstFocus : function(){
26075 //Roo.log("onFirstFocus");
26076 this.editorcore.onFirstFocus();
26077 for (var i =0; i < this.toolbars.length;i++) {
26078 this.toolbars[i].onFirstFocus();
26084 syncValue : function()
26086 this.editorcore.syncValue();
26089 pushValue : function()
26091 this.editorcore.pushValue();
26095 // hide stuff that is not compatible
26109 * @event specialkey
26113 * @cfg {String} fieldClass @hide
26116 * @cfg {String} focusClass @hide
26119 * @cfg {String} autoCreate @hide
26122 * @cfg {String} inputType @hide
26126 * @cfg {String} invalidText @hide
26129 * @cfg {String} msgFx @hide
26132 * @cfg {String} validateOnBlur @hide
26141 Roo.namespace('Roo.bootstrap.htmleditor');
26143 * @class Roo.bootstrap.HtmlEditorToolbar1
26149 new Roo.bootstrap.HtmlEditor({
26152 new Roo.bootstrap.HtmlEditorToolbar1({
26153 disable : { fonts: 1 , format: 1, ..., ... , ...],
26159 * @cfg {Object} disable List of elements to disable..
26160 * @cfg {Array} btns List of additional buttons.
26164 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26167 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26170 Roo.apply(this, config);
26172 // default disabled, based on 'good practice'..
26173 this.disable = this.disable || {};
26174 Roo.applyIf(this.disable, {
26177 specialElements : true
26179 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26181 this.editor = config.editor;
26182 this.editorcore = config.editor.editorcore;
26184 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26186 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26187 // dont call parent... till later.
26189 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26194 editorcore : false,
26199 "h1","h2","h3","h4","h5","h6",
26201 "abbr", "acronym", "address", "cite", "samp", "var",
26205 onRender : function(ct, position)
26207 // Roo.log("Call onRender: " + this.xtype);
26209 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26211 this.el.dom.style.marginBottom = '0';
26213 var editorcore = this.editorcore;
26214 var editor= this.editor;
26217 var btn = function(id,cmd , toggle, handler, html){
26219 var event = toggle ? 'toggle' : 'click';
26224 xns: Roo.bootstrap,
26228 enableToggle:toggle !== false,
26230 pressed : toggle ? false : null,
26233 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26234 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26240 // var cb_box = function...
26245 xns: Roo.bootstrap,
26250 xns: Roo.bootstrap,
26254 Roo.each(this.formats, function(f) {
26255 style.menu.items.push({
26257 xns: Roo.bootstrap,
26258 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26263 editorcore.insertTag(this.tagname);
26270 children.push(style);
26272 btn('bold',false,true);
26273 btn('italic',false,true);
26274 btn('align-left', 'justifyleft',true);
26275 btn('align-center', 'justifycenter',true);
26276 btn('align-right' , 'justifyright',true);
26277 btn('link', false, false, function(btn) {
26278 //Roo.log("create link?");
26279 var url = prompt(this.createLinkText, this.defaultLinkValue);
26280 if(url && url != 'http:/'+'/'){
26281 this.editorcore.relayCmd('createlink', url);
26284 btn('list','insertunorderedlist',true);
26285 btn('pencil', false,true, function(btn){
26287 this.toggleSourceEdit(btn.pressed);
26290 if (this.editor.btns.length > 0) {
26291 for (var i = 0; i<this.editor.btns.length; i++) {
26292 children.push(this.editor.btns[i]);
26300 xns: Roo.bootstrap,
26305 xns: Roo.bootstrap,
26310 cog.menu.items.push({
26312 xns: Roo.bootstrap,
26313 html : Clean styles,
26318 editorcore.insertTag(this.tagname);
26327 this.xtype = 'NavSimplebar';
26329 for(var i=0;i< children.length;i++) {
26331 this.buttons.add(this.addxtypeChild(children[i]));
26335 editor.on('editorevent', this.updateToolbar, this);
26337 onBtnClick : function(id)
26339 this.editorcore.relayCmd(id);
26340 this.editorcore.focus();
26344 * Protected method that will not generally be called directly. It triggers
26345 * a toolbar update by reading the markup state of the current selection in the editor.
26347 updateToolbar: function(){
26349 if(!this.editorcore.activated){
26350 this.editor.onFirstFocus(); // is this neeed?
26354 var btns = this.buttons;
26355 var doc = this.editorcore.doc;
26356 btns.get('bold').setActive(doc.queryCommandState('bold'));
26357 btns.get('italic').setActive(doc.queryCommandState('italic'));
26358 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26360 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26361 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26362 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26364 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26365 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26368 var ans = this.editorcore.getAllAncestors();
26369 if (this.formatCombo) {
26372 var store = this.formatCombo.store;
26373 this.formatCombo.setValue("");
26374 for (var i =0; i < ans.length;i++) {
26375 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26377 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26385 // hides menus... - so this cant be on a menu...
26386 Roo.bootstrap.MenuMgr.hideAll();
26388 Roo.bootstrap.MenuMgr.hideAll();
26389 //this.editorsyncValue();
26391 onFirstFocus: function() {
26392 this.buttons.each(function(item){
26396 toggleSourceEdit : function(sourceEditMode){
26399 if(sourceEditMode){
26400 Roo.log("disabling buttons");
26401 this.buttons.each( function(item){
26402 if(item.cmd != 'pencil'){
26408 Roo.log("enabling buttons");
26409 if(this.editorcore.initialized){
26410 this.buttons.each( function(item){
26416 Roo.log("calling toggole on editor");
26417 // tell the editor that it's been pressed..
26418 this.editor.toggleSourceEdit(sourceEditMode);
26432 * @class Roo.bootstrap.Markdown
26433 * @extends Roo.bootstrap.TextArea
26434 * Bootstrap Showdown editable area
26435 * @cfg {string} content
26438 * Create a new Showdown
26441 Roo.bootstrap.Markdown = function(config){
26442 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26446 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26450 initEvents : function()
26453 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26454 this.markdownEl = this.el.createChild({
26455 cls : 'roo-markdown-area'
26457 this.inputEl().addClass('d-none');
26458 if (this.getValue() == '') {
26459 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26462 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26464 this.markdownEl.on('click', this.toggleTextEdit, this);
26465 this.on('blur', this.toggleTextEdit, this);
26466 this.on('specialkey', this.resizeTextArea, this);
26469 toggleTextEdit : function()
26471 var sh = this.markdownEl.getHeight();
26472 this.inputEl().addClass('d-none');
26473 this.markdownEl.addClass('d-none');
26474 if (!this.editing) {
26476 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26477 this.inputEl().removeClass('d-none');
26478 this.inputEl().focus();
26479 this.editing = true;
26482 // show showdown...
26483 this.updateMarkdown();
26484 this.markdownEl.removeClass('d-none');
26485 this.editing = false;
26488 updateMarkdown : function()
26490 if (this.getValue() == '') {
26491 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26495 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26498 resizeTextArea: function () {
26501 Roo.log([sh, this.getValue().split("\n").length * 30]);
26502 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26504 setValue : function(val)
26506 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26507 if (!this.editing) {
26508 this.updateMarkdown();
26514 if (!this.editing) {
26515 this.toggleTextEdit();
26523 * @class Roo.bootstrap.Table.AbstractSelectionModel
26524 * @extends Roo.util.Observable
26525 * Abstract base class for grid SelectionModels. It provides the interface that should be
26526 * implemented by descendant classes. This class should not be directly instantiated.
26529 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26530 this.locked = false;
26531 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26535 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26536 /** @ignore Called by the grid automatically. Do not call directly. */
26537 init : function(grid){
26543 * Locks the selections.
26546 this.locked = true;
26550 * Unlocks the selections.
26552 unlock : function(){
26553 this.locked = false;
26557 * Returns true if the selections are locked.
26558 * @return {Boolean}
26560 isLocked : function(){
26561 return this.locked;
26565 initEvents : function ()
26571 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26572 * @class Roo.bootstrap.Table.RowSelectionModel
26573 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26574 * It supports multiple selections and keyboard selection/navigation.
26576 * @param {Object} config
26579 Roo.bootstrap.Table.RowSelectionModel = function(config){
26580 Roo.apply(this, config);
26581 this.selections = new Roo.util.MixedCollection(false, function(o){
26586 this.lastActive = false;
26590 * @event selectionchange
26591 * Fires when the selection changes
26592 * @param {SelectionModel} this
26594 "selectionchange" : true,
26596 * @event afterselectionchange
26597 * Fires after the selection changes (eg. by key press or clicking)
26598 * @param {SelectionModel} this
26600 "afterselectionchange" : true,
26602 * @event beforerowselect
26603 * Fires when a row is selected being selected, return false to cancel.
26604 * @param {SelectionModel} this
26605 * @param {Number} rowIndex The selected index
26606 * @param {Boolean} keepExisting False if other selections will be cleared
26608 "beforerowselect" : true,
26611 * Fires when a row is selected.
26612 * @param {SelectionModel} this
26613 * @param {Number} rowIndex The selected index
26614 * @param {Roo.data.Record} r The record
26616 "rowselect" : true,
26618 * @event rowdeselect
26619 * Fires when a row is deselected.
26620 * @param {SelectionModel} this
26621 * @param {Number} rowIndex The selected index
26623 "rowdeselect" : true
26625 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26626 this.locked = false;
26629 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26631 * @cfg {Boolean} singleSelect
26632 * True to allow selection of only one row at a time (defaults to false)
26634 singleSelect : false,
26637 initEvents : function()
26640 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26641 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26642 //}else{ // allow click to work like normal
26643 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26645 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26646 this.grid.on("rowclick", this.handleMouseDown, this);
26648 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26649 "up" : function(e){
26651 this.selectPrevious(e.shiftKey);
26652 }else if(this.last !== false && this.lastActive !== false){
26653 var last = this.last;
26654 this.selectRange(this.last, this.lastActive-1);
26655 this.grid.getView().focusRow(this.lastActive);
26656 if(last !== false){
26660 this.selectFirstRow();
26662 this.fireEvent("afterselectionchange", this);
26664 "down" : function(e){
26666 this.selectNext(e.shiftKey);
26667 }else if(this.last !== false && this.lastActive !== false){
26668 var last = this.last;
26669 this.selectRange(this.last, this.lastActive+1);
26670 this.grid.getView().focusRow(this.lastActive);
26671 if(last !== false){
26675 this.selectFirstRow();
26677 this.fireEvent("afterselectionchange", this);
26681 this.grid.store.on('load', function(){
26682 this.selections.clear();
26685 var view = this.grid.view;
26686 view.on("refresh", this.onRefresh, this);
26687 view.on("rowupdated", this.onRowUpdated, this);
26688 view.on("rowremoved", this.onRemove, this);
26693 onRefresh : function()
26695 var ds = this.grid.store, i, v = this.grid.view;
26696 var s = this.selections;
26697 s.each(function(r){
26698 if((i = ds.indexOfId(r.id)) != -1){
26707 onRemove : function(v, index, r){
26708 this.selections.remove(r);
26712 onRowUpdated : function(v, index, r){
26713 if(this.isSelected(r)){
26714 v.onRowSelect(index);
26720 * @param {Array} records The records to select
26721 * @param {Boolean} keepExisting (optional) True to keep existing selections
26723 selectRecords : function(records, keepExisting)
26726 this.clearSelections();
26728 var ds = this.grid.store;
26729 for(var i = 0, len = records.length; i < len; i++){
26730 this.selectRow(ds.indexOf(records[i]), true);
26735 * Gets the number of selected rows.
26738 getCount : function(){
26739 return this.selections.length;
26743 * Selects the first row in the grid.
26745 selectFirstRow : function(){
26750 * Select the last row.
26751 * @param {Boolean} keepExisting (optional) True to keep existing selections
26753 selectLastRow : function(keepExisting){
26754 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26755 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26759 * Selects the row immediately following the last selected row.
26760 * @param {Boolean} keepExisting (optional) True to keep existing selections
26762 selectNext : function(keepExisting)
26764 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26765 this.selectRow(this.last+1, keepExisting);
26766 this.grid.getView().focusRow(this.last);
26771 * Selects the row that precedes the last selected row.
26772 * @param {Boolean} keepExisting (optional) True to keep existing selections
26774 selectPrevious : function(keepExisting){
26776 this.selectRow(this.last-1, keepExisting);
26777 this.grid.getView().focusRow(this.last);
26782 * Returns the selected records
26783 * @return {Array} Array of selected records
26785 getSelections : function(){
26786 return [].concat(this.selections.items);
26790 * Returns the first selected record.
26793 getSelected : function(){
26794 return this.selections.itemAt(0);
26799 * Clears all selections.
26801 clearSelections : function(fast)
26807 var ds = this.grid.store;
26808 var s = this.selections;
26809 s.each(function(r){
26810 this.deselectRow(ds.indexOfId(r.id));
26814 this.selections.clear();
26821 * Selects all rows.
26823 selectAll : function(){
26827 this.selections.clear();
26828 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26829 this.selectRow(i, true);
26834 * Returns True if there is a selection.
26835 * @return {Boolean}
26837 hasSelection : function(){
26838 return this.selections.length > 0;
26842 * Returns True if the specified row is selected.
26843 * @param {Number/Record} record The record or index of the record to check
26844 * @return {Boolean}
26846 isSelected : function(index){
26847 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26848 return (r && this.selections.key(r.id) ? true : false);
26852 * Returns True if the specified record id is selected.
26853 * @param {String} id The id of record to check
26854 * @return {Boolean}
26856 isIdSelected : function(id){
26857 return (this.selections.key(id) ? true : false);
26862 handleMouseDBClick : function(e, t){
26866 handleMouseDown : function(e, t)
26868 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26869 if(this.isLocked() || rowIndex < 0 ){
26872 if(e.shiftKey && this.last !== false){
26873 var last = this.last;
26874 this.selectRange(last, rowIndex, e.ctrlKey);
26875 this.last = last; // reset the last
26879 var isSelected = this.isSelected(rowIndex);
26880 //Roo.log("select row:" + rowIndex);
26882 this.deselectRow(rowIndex);
26884 this.selectRow(rowIndex, true);
26888 if(e.button !== 0 && isSelected){
26889 alert('rowIndex 2: ' + rowIndex);
26890 view.focusRow(rowIndex);
26891 }else if(e.ctrlKey && isSelected){
26892 this.deselectRow(rowIndex);
26893 }else if(!isSelected){
26894 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26895 view.focusRow(rowIndex);
26899 this.fireEvent("afterselectionchange", this);
26902 handleDragableRowClick : function(grid, rowIndex, e)
26904 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26905 this.selectRow(rowIndex, false);
26906 grid.view.focusRow(rowIndex);
26907 this.fireEvent("afterselectionchange", this);
26912 * Selects multiple rows.
26913 * @param {Array} rows Array of the indexes of the row to select
26914 * @param {Boolean} keepExisting (optional) True to keep existing selections
26916 selectRows : function(rows, keepExisting){
26918 this.clearSelections();
26920 for(var i = 0, len = rows.length; i < len; i++){
26921 this.selectRow(rows[i], true);
26926 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26927 * @param {Number} startRow The index of the first row in the range
26928 * @param {Number} endRow The index of the last row in the range
26929 * @param {Boolean} keepExisting (optional) True to retain existing selections
26931 selectRange : function(startRow, endRow, keepExisting){
26936 this.clearSelections();
26938 if(startRow <= endRow){
26939 for(var i = startRow; i <= endRow; i++){
26940 this.selectRow(i, true);
26943 for(var i = startRow; i >= endRow; i--){
26944 this.selectRow(i, true);
26950 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26951 * @param {Number} startRow The index of the first row in the range
26952 * @param {Number} endRow The index of the last row in the range
26954 deselectRange : function(startRow, endRow, preventViewNotify){
26958 for(var i = startRow; i <= endRow; i++){
26959 this.deselectRow(i, preventViewNotify);
26965 * @param {Number} row The index of the row to select
26966 * @param {Boolean} keepExisting (optional) True to keep existing selections
26968 selectRow : function(index, keepExisting, preventViewNotify)
26970 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26973 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26974 if(!keepExisting || this.singleSelect){
26975 this.clearSelections();
26978 var r = this.grid.store.getAt(index);
26979 //console.log('selectRow - record id :' + r.id);
26981 this.selections.add(r);
26982 this.last = this.lastActive = index;
26983 if(!preventViewNotify){
26984 var proxy = new Roo.Element(
26985 this.grid.getRowDom(index)
26987 proxy.addClass('bg-info info');
26989 this.fireEvent("rowselect", this, index, r);
26990 this.fireEvent("selectionchange", this);
26996 * @param {Number} row The index of the row to deselect
26998 deselectRow : function(index, preventViewNotify)
27003 if(this.last == index){
27006 if(this.lastActive == index){
27007 this.lastActive = false;
27010 var r = this.grid.store.getAt(index);
27015 this.selections.remove(r);
27016 //.console.log('deselectRow - record id :' + r.id);
27017 if(!preventViewNotify){
27019 var proxy = new Roo.Element(
27020 this.grid.getRowDom(index)
27022 proxy.removeClass('bg-info info');
27024 this.fireEvent("rowdeselect", this, index);
27025 this.fireEvent("selectionchange", this);
27029 restoreLast : function(){
27031 this.last = this._last;
27036 acceptsNav : function(row, col, cm){
27037 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27041 onEditorKey : function(field, e){
27042 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27047 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27049 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27051 }else if(k == e.ENTER && !e.ctrlKey){
27055 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27057 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27059 }else if(k == e.ESC){
27063 g.startEditing(newCell[0], newCell[1]);
27069 * Ext JS Library 1.1.1
27070 * Copyright(c) 2006-2007, Ext JS, LLC.
27072 * Originally Released Under LGPL - original licence link has changed is not relivant.
27075 * <script type="text/javascript">
27079 * @class Roo.bootstrap.PagingToolbar
27080 * @extends Roo.bootstrap.NavSimplebar
27081 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27083 * Create a new PagingToolbar
27084 * @param {Object} config The config object
27085 * @param {Roo.data.Store} store
27087 Roo.bootstrap.PagingToolbar = function(config)
27089 // old args format still supported... - xtype is prefered..
27090 // created from xtype...
27092 this.ds = config.dataSource;
27094 if (config.store && !this.ds) {
27095 this.store= Roo.factory(config.store, Roo.data);
27096 this.ds = this.store;
27097 this.ds.xmodule = this.xmodule || false;
27100 this.toolbarItems = [];
27101 if (config.items) {
27102 this.toolbarItems = config.items;
27105 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27110 this.bind(this.ds);
27113 if (Roo.bootstrap.version == 4) {
27114 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27116 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27121 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27123 * @cfg {Roo.data.Store} dataSource
27124 * The underlying data store providing the paged data
27127 * @cfg {String/HTMLElement/Element} container
27128 * container The id or element that will contain the toolbar
27131 * @cfg {Boolean} displayInfo
27132 * True to display the displayMsg (defaults to false)
27135 * @cfg {Number} pageSize
27136 * The number of records to display per page (defaults to 20)
27140 * @cfg {String} displayMsg
27141 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27143 displayMsg : 'Displaying {0} - {1} of {2}',
27145 * @cfg {String} emptyMsg
27146 * The message to display when no records are found (defaults to "No data to display")
27148 emptyMsg : 'No data to display',
27150 * Customizable piece of the default paging text (defaults to "Page")
27153 beforePageText : "Page",
27155 * Customizable piece of the default paging text (defaults to "of %0")
27158 afterPageText : "of {0}",
27160 * Customizable piece of the default paging text (defaults to "First Page")
27163 firstText : "First Page",
27165 * Customizable piece of the default paging text (defaults to "Previous Page")
27168 prevText : "Previous Page",
27170 * Customizable piece of the default paging text (defaults to "Next Page")
27173 nextText : "Next Page",
27175 * Customizable piece of the default paging text (defaults to "Last Page")
27178 lastText : "Last Page",
27180 * Customizable piece of the default paging text (defaults to "Refresh")
27183 refreshText : "Refresh",
27187 onRender : function(ct, position)
27189 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27190 this.navgroup.parentId = this.id;
27191 this.navgroup.onRender(this.el, null);
27192 // add the buttons to the navgroup
27194 if(this.displayInfo){
27195 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27196 this.displayEl = this.el.select('.x-paging-info', true).first();
27197 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27198 // this.displayEl = navel.el.select('span',true).first();
27204 Roo.each(_this.buttons, function(e){ // this might need to use render????
27205 Roo.factory(e).render(_this.el);
27209 Roo.each(_this.toolbarItems, function(e) {
27210 _this.navgroup.addItem(e);
27214 this.first = this.navgroup.addItem({
27215 tooltip: this.firstText,
27216 cls: "prev btn-outline-secondary",
27217 html : ' <i class="fa fa-step-backward"></i>',
27219 preventDefault: true,
27220 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27223 this.prev = this.navgroup.addItem({
27224 tooltip: this.prevText,
27225 cls: "prev btn-outline-secondary",
27226 html : ' <i class="fa fa-backward"></i>',
27228 preventDefault: true,
27229 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27231 //this.addSeparator();
27234 var field = this.navgroup.addItem( {
27236 cls : 'x-paging-position btn-outline-secondary',
27238 html : this.beforePageText +
27239 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27240 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27243 this.field = field.el.select('input', true).first();
27244 this.field.on("keydown", this.onPagingKeydown, this);
27245 this.field.on("focus", function(){this.dom.select();});
27248 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27249 //this.field.setHeight(18);
27250 //this.addSeparator();
27251 this.next = this.navgroup.addItem({
27252 tooltip: this.nextText,
27253 cls: "next btn-outline-secondary",
27254 html : ' <i class="fa fa-forward"></i>',
27256 preventDefault: true,
27257 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27259 this.last = this.navgroup.addItem({
27260 tooltip: this.lastText,
27261 html : ' <i class="fa fa-step-forward"></i>',
27262 cls: "next btn-outline-secondary",
27264 preventDefault: true,
27265 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27267 //this.addSeparator();
27268 this.loading = this.navgroup.addItem({
27269 tooltip: this.refreshText,
27270 cls: "btn-outline-secondary",
27271 html : ' <i class="fa fa-refresh"></i>',
27272 preventDefault: true,
27273 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27279 updateInfo : function(){
27280 if(this.displayEl){
27281 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27282 var msg = count == 0 ?
27286 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27288 this.displayEl.update(msg);
27293 onLoad : function(ds, r, o)
27295 this.cursor = o.params.start ? o.params.start : 0;
27297 var d = this.getPageData(),
27302 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27303 this.field.dom.value = ap;
27304 this.first.setDisabled(ap == 1);
27305 this.prev.setDisabled(ap == 1);
27306 this.next.setDisabled(ap == ps);
27307 this.last.setDisabled(ap == ps);
27308 this.loading.enable();
27313 getPageData : function(){
27314 var total = this.ds.getTotalCount();
27317 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27318 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27323 onLoadError : function(){
27324 this.loading.enable();
27328 onPagingKeydown : function(e){
27329 var k = e.getKey();
27330 var d = this.getPageData();
27332 var v = this.field.dom.value, pageNum;
27333 if(!v || isNaN(pageNum = parseInt(v, 10))){
27334 this.field.dom.value = d.activePage;
27337 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27338 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27341 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))
27343 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27344 this.field.dom.value = pageNum;
27345 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27348 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27350 var v = this.field.dom.value, pageNum;
27351 var increment = (e.shiftKey) ? 10 : 1;
27352 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27355 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27356 this.field.dom.value = d.activePage;
27359 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27361 this.field.dom.value = parseInt(v, 10) + increment;
27362 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27363 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27370 beforeLoad : function(){
27372 this.loading.disable();
27377 onClick : function(which){
27386 ds.load({params:{start: 0, limit: this.pageSize}});
27389 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27392 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27395 var total = ds.getTotalCount();
27396 var extra = total % this.pageSize;
27397 var lastStart = extra ? (total - extra) : total-this.pageSize;
27398 ds.load({params:{start: lastStart, limit: this.pageSize}});
27401 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27407 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27408 * @param {Roo.data.Store} store The data store to unbind
27410 unbind : function(ds){
27411 ds.un("beforeload", this.beforeLoad, this);
27412 ds.un("load", this.onLoad, this);
27413 ds.un("loadexception", this.onLoadError, this);
27414 ds.un("remove", this.updateInfo, this);
27415 ds.un("add", this.updateInfo, this);
27416 this.ds = undefined;
27420 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27421 * @param {Roo.data.Store} store The data store to bind
27423 bind : function(ds){
27424 ds.on("beforeload", this.beforeLoad, this);
27425 ds.on("load", this.onLoad, this);
27426 ds.on("loadexception", this.onLoadError, this);
27427 ds.on("remove", this.updateInfo, this);
27428 ds.on("add", this.updateInfo, this);
27439 * @class Roo.bootstrap.MessageBar
27440 * @extends Roo.bootstrap.Component
27441 * Bootstrap MessageBar class
27442 * @cfg {String} html contents of the MessageBar
27443 * @cfg {String} weight (info | success | warning | danger) default info
27444 * @cfg {String} beforeClass insert the bar before the given class
27445 * @cfg {Boolean} closable (true | false) default false
27446 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27449 * Create a new Element
27450 * @param {Object} config The config object
27453 Roo.bootstrap.MessageBar = function(config){
27454 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27457 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27463 beforeClass: 'bootstrap-sticky-wrap',
27465 getAutoCreate : function(){
27469 cls: 'alert alert-dismissable alert-' + this.weight,
27474 html: this.html || ''
27480 cfg.cls += ' alert-messages-fixed';
27494 onRender : function(ct, position)
27496 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27499 var cfg = Roo.apply({}, this.getAutoCreate());
27503 cfg.cls += ' ' + this.cls;
27506 cfg.style = this.style;
27508 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27510 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27513 this.el.select('>button.close').on('click', this.hide, this);
27519 if (!this.rendered) {
27525 this.fireEvent('show', this);
27531 if (!this.rendered) {
27537 this.fireEvent('hide', this);
27540 update : function()
27542 // var e = this.el.dom.firstChild;
27544 // if(this.closable){
27545 // e = e.nextSibling;
27548 // e.data = this.html || '';
27550 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27566 * @class Roo.bootstrap.Graph
27567 * @extends Roo.bootstrap.Component
27568 * Bootstrap Graph class
27572 @cfg {String} graphtype bar | vbar | pie
27573 @cfg {number} g_x coodinator | centre x (pie)
27574 @cfg {number} g_y coodinator | centre y (pie)
27575 @cfg {number} g_r radius (pie)
27576 @cfg {number} g_height height of the chart (respected by all elements in the set)
27577 @cfg {number} g_width width of the chart (respected by all elements in the set)
27578 @cfg {Object} title The title of the chart
27581 -opts (object) options for the chart
27583 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27584 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27586 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.
27587 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27589 o stretch (boolean)
27591 -opts (object) options for the pie
27594 o startAngle (number)
27595 o endAngle (number)
27599 * Create a new Input
27600 * @param {Object} config The config object
27603 Roo.bootstrap.Graph = function(config){
27604 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27610 * The img click event for the img.
27611 * @param {Roo.EventObject} e
27617 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27628 //g_colors: this.colors,
27635 getAutoCreate : function(){
27646 onRender : function(ct,position){
27649 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27651 if (typeof(Raphael) == 'undefined') {
27652 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27656 this.raphael = Raphael(this.el.dom);
27658 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27659 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27660 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27661 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27663 r.text(160, 10, "Single Series Chart").attr(txtattr);
27664 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27665 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27666 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27668 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27669 r.barchart(330, 10, 300, 220, data1);
27670 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27671 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27674 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27675 // r.barchart(30, 30, 560, 250, xdata, {
27676 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27677 // axis : "0 0 1 1",
27678 // axisxlabels : xdata
27679 // //yvalues : cols,
27682 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27684 // this.load(null,xdata,{
27685 // axis : "0 0 1 1",
27686 // axisxlabels : xdata
27691 load : function(graphtype,xdata,opts)
27693 this.raphael.clear();
27695 graphtype = this.graphtype;
27700 var r = this.raphael,
27701 fin = function () {
27702 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27704 fout = function () {
27705 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27707 pfin = function() {
27708 this.sector.stop();
27709 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27712 this.label[0].stop();
27713 this.label[0].attr({ r: 7.5 });
27714 this.label[1].attr({ "font-weight": 800 });
27717 pfout = function() {
27718 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27721 this.label[0].animate({ r: 5 }, 500, "bounce");
27722 this.label[1].attr({ "font-weight": 400 });
27728 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27731 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27734 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27735 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27737 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27744 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27749 setTitle: function(o)
27754 initEvents: function() {
27757 this.el.on('click', this.onClick, this);
27761 onClick : function(e)
27763 Roo.log('img onclick');
27764 this.fireEvent('click', this, e);
27776 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27779 * @class Roo.bootstrap.dash.NumberBox
27780 * @extends Roo.bootstrap.Component
27781 * Bootstrap NumberBox class
27782 * @cfg {String} headline Box headline
27783 * @cfg {String} content Box content
27784 * @cfg {String} icon Box icon
27785 * @cfg {String} footer Footer text
27786 * @cfg {String} fhref Footer href
27789 * Create a new NumberBox
27790 * @param {Object} config The config object
27794 Roo.bootstrap.dash.NumberBox = function(config){
27795 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27799 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27808 getAutoCreate : function(){
27812 cls : 'small-box ',
27820 cls : 'roo-headline',
27821 html : this.headline
27825 cls : 'roo-content',
27826 html : this.content
27840 cls : 'ion ' + this.icon
27849 cls : 'small-box-footer',
27850 href : this.fhref || '#',
27854 cfg.cn.push(footer);
27861 onRender : function(ct,position){
27862 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27869 setHeadline: function (value)
27871 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27874 setFooter: function (value, href)
27876 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27879 this.el.select('a.small-box-footer',true).first().attr('href', href);
27884 setContent: function (value)
27886 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27889 initEvents: function()
27903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27906 * @class Roo.bootstrap.dash.TabBox
27907 * @extends Roo.bootstrap.Component
27908 * Bootstrap TabBox class
27909 * @cfg {String} title Title of the TabBox
27910 * @cfg {String} icon Icon of the TabBox
27911 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27912 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27915 * Create a new TabBox
27916 * @param {Object} config The config object
27920 Roo.bootstrap.dash.TabBox = function(config){
27921 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27926 * When a pane is added
27927 * @param {Roo.bootstrap.dash.TabPane} pane
27931 * @event activatepane
27932 * When a pane is activated
27933 * @param {Roo.bootstrap.dash.TabPane} pane
27935 "activatepane" : true
27943 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27948 tabScrollable : false,
27950 getChildContainer : function()
27952 return this.el.select('.tab-content', true).first();
27955 getAutoCreate : function(){
27959 cls: 'pull-left header',
27967 cls: 'fa ' + this.icon
27973 cls: 'nav nav-tabs pull-right',
27979 if(this.tabScrollable){
27986 cls: 'nav nav-tabs pull-right',
27997 cls: 'nav-tabs-custom',
28002 cls: 'tab-content no-padding',
28010 initEvents : function()
28012 //Roo.log('add add pane handler');
28013 this.on('addpane', this.onAddPane, this);
28016 * Updates the box title
28017 * @param {String} html to set the title to.
28019 setTitle : function(value)
28021 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28023 onAddPane : function(pane)
28025 this.panes.push(pane);
28026 //Roo.log('addpane');
28028 // tabs are rendere left to right..
28029 if(!this.showtabs){
28033 var ctr = this.el.select('.nav-tabs', true).first();
28036 var existing = ctr.select('.nav-tab',true);
28037 var qty = existing.getCount();;
28040 var tab = ctr.createChild({
28042 cls : 'nav-tab' + (qty ? '' : ' active'),
28050 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28053 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28055 pane.el.addClass('active');
28060 onTabClick : function(ev,un,ob,pane)
28062 //Roo.log('tab - prev default');
28063 ev.preventDefault();
28066 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28067 pane.tab.addClass('active');
28068 //Roo.log(pane.title);
28069 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28070 // technically we should have a deactivate event.. but maybe add later.
28071 // and it should not de-activate the selected tab...
28072 this.fireEvent('activatepane', pane);
28073 pane.el.addClass('active');
28074 pane.fireEvent('activate');
28079 getActivePane : function()
28082 Roo.each(this.panes, function(p) {
28083 if(p.el.hasClass('active')){
28104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28106 * @class Roo.bootstrap.TabPane
28107 * @extends Roo.bootstrap.Component
28108 * Bootstrap TabPane class
28109 * @cfg {Boolean} active (false | true) Default false
28110 * @cfg {String} title title of panel
28114 * Create a new TabPane
28115 * @param {Object} config The config object
28118 Roo.bootstrap.dash.TabPane = function(config){
28119 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28125 * When a pane is activated
28126 * @param {Roo.bootstrap.dash.TabPane} pane
28133 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28138 // the tabBox that this is attached to.
28141 getAutoCreate : function()
28149 cfg.cls += ' active';
28154 initEvents : function()
28156 //Roo.log('trigger add pane handler');
28157 this.parent().fireEvent('addpane', this)
28161 * Updates the tab title
28162 * @param {String} html to set the title to.
28164 setTitle: function(str)
28170 this.tab.select('a', true).first().dom.innerHTML = str;
28187 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28190 * @class Roo.bootstrap.menu.Menu
28191 * @extends Roo.bootstrap.Component
28192 * Bootstrap Menu class - container for Menu
28193 * @cfg {String} html Text of the menu
28194 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28195 * @cfg {String} icon Font awesome icon
28196 * @cfg {String} pos Menu align to (top | bottom) default bottom
28200 * Create a new Menu
28201 * @param {Object} config The config object
28205 Roo.bootstrap.menu.Menu = function(config){
28206 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28210 * @event beforeshow
28211 * Fires before this menu is displayed
28212 * @param {Roo.bootstrap.menu.Menu} this
28216 * @event beforehide
28217 * Fires before this menu is hidden
28218 * @param {Roo.bootstrap.menu.Menu} this
28223 * Fires after this menu is displayed
28224 * @param {Roo.bootstrap.menu.Menu} this
28229 * Fires after this menu is hidden
28230 * @param {Roo.bootstrap.menu.Menu} this
28235 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28236 * @param {Roo.bootstrap.menu.Menu} this
28237 * @param {Roo.EventObject} e
28244 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28248 weight : 'default',
28253 getChildContainer : function() {
28254 if(this.isSubMenu){
28258 return this.el.select('ul.dropdown-menu', true).first();
28261 getAutoCreate : function()
28266 cls : 'roo-menu-text',
28274 cls : 'fa ' + this.icon
28285 cls : 'dropdown-button btn btn-' + this.weight,
28290 cls : 'dropdown-toggle btn btn-' + this.weight,
28300 cls : 'dropdown-menu'
28306 if(this.pos == 'top'){
28307 cfg.cls += ' dropup';
28310 if(this.isSubMenu){
28313 cls : 'dropdown-menu'
28320 onRender : function(ct, position)
28322 this.isSubMenu = ct.hasClass('dropdown-submenu');
28324 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28327 initEvents : function()
28329 if(this.isSubMenu){
28333 this.hidden = true;
28335 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28336 this.triggerEl.on('click', this.onTriggerPress, this);
28338 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28339 this.buttonEl.on('click', this.onClick, this);
28345 if(this.isSubMenu){
28349 return this.el.select('ul.dropdown-menu', true).first();
28352 onClick : function(e)
28354 this.fireEvent("click", this, e);
28357 onTriggerPress : function(e)
28359 if (this.isVisible()) {
28366 isVisible : function(){
28367 return !this.hidden;
28372 this.fireEvent("beforeshow", this);
28374 this.hidden = false;
28375 this.el.addClass('open');
28377 Roo.get(document).on("mouseup", this.onMouseUp, this);
28379 this.fireEvent("show", this);
28386 this.fireEvent("beforehide", this);
28388 this.hidden = true;
28389 this.el.removeClass('open');
28391 Roo.get(document).un("mouseup", this.onMouseUp);
28393 this.fireEvent("hide", this);
28396 onMouseUp : function()
28410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28413 * @class Roo.bootstrap.menu.Item
28414 * @extends Roo.bootstrap.Component
28415 * Bootstrap MenuItem class
28416 * @cfg {Boolean} submenu (true | false) default false
28417 * @cfg {String} html text of the item
28418 * @cfg {String} href the link
28419 * @cfg {Boolean} disable (true | false) default false
28420 * @cfg {Boolean} preventDefault (true | false) default true
28421 * @cfg {String} icon Font awesome icon
28422 * @cfg {String} pos Submenu align to (left | right) default right
28426 * Create a new Item
28427 * @param {Object} config The config object
28431 Roo.bootstrap.menu.Item = function(config){
28432 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28436 * Fires when the mouse is hovering over this menu
28437 * @param {Roo.bootstrap.menu.Item} this
28438 * @param {Roo.EventObject} e
28443 * Fires when the mouse exits this menu
28444 * @param {Roo.bootstrap.menu.Item} this
28445 * @param {Roo.EventObject} e
28451 * The raw click event for the entire grid.
28452 * @param {Roo.EventObject} e
28458 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28463 preventDefault: true,
28468 getAutoCreate : function()
28473 cls : 'roo-menu-item-text',
28481 cls : 'fa ' + this.icon
28490 href : this.href || '#',
28497 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28501 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28503 if(this.pos == 'left'){
28504 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28511 initEvents : function()
28513 this.el.on('mouseover', this.onMouseOver, this);
28514 this.el.on('mouseout', this.onMouseOut, this);
28516 this.el.select('a', true).first().on('click', this.onClick, this);
28520 onClick : function(e)
28522 if(this.preventDefault){
28523 e.preventDefault();
28526 this.fireEvent("click", this, e);
28529 onMouseOver : function(e)
28531 if(this.submenu && this.pos == 'left'){
28532 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28535 this.fireEvent("mouseover", this, e);
28538 onMouseOut : function(e)
28540 this.fireEvent("mouseout", this, e);
28552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28555 * @class Roo.bootstrap.menu.Separator
28556 * @extends Roo.bootstrap.Component
28557 * Bootstrap Separator class
28560 * Create a new Separator
28561 * @param {Object} config The config object
28565 Roo.bootstrap.menu.Separator = function(config){
28566 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28569 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28571 getAutoCreate : function(){
28592 * @class Roo.bootstrap.Tooltip
28593 * Bootstrap Tooltip class
28594 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28595 * to determine which dom element triggers the tooltip.
28597 * It needs to add support for additional attributes like tooltip-position
28600 * Create a new Toolti
28601 * @param {Object} config The config object
28604 Roo.bootstrap.Tooltip = function(config){
28605 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28607 this.alignment = Roo.bootstrap.Tooltip.alignment;
28609 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28610 this.alignment = config.alignment;
28615 Roo.apply(Roo.bootstrap.Tooltip, {
28617 * @function init initialize tooltip monitoring.
28621 currentTip : false,
28622 currentRegion : false,
28628 Roo.get(document).on('mouseover', this.enter ,this);
28629 Roo.get(document).on('mouseout', this.leave, this);
28632 this.currentTip = new Roo.bootstrap.Tooltip();
28635 enter : function(ev)
28637 var dom = ev.getTarget();
28639 //Roo.log(['enter',dom]);
28640 var el = Roo.fly(dom);
28641 if (this.currentEl) {
28643 //Roo.log(this.currentEl);
28644 //Roo.log(this.currentEl.contains(dom));
28645 if (this.currentEl == el) {
28648 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28654 if (this.currentTip.el) {
28655 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28659 if(!el || el.dom == document){
28665 // you can not look for children, as if el is the body.. then everythign is the child..
28666 if (!el.attr('tooltip')) { //
28667 if (!el.select("[tooltip]").elements.length) {
28670 // is the mouse over this child...?
28671 bindEl = el.select("[tooltip]").first();
28672 var xy = ev.getXY();
28673 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28674 //Roo.log("not in region.");
28677 //Roo.log("child element over..");
28680 this.currentEl = bindEl;
28681 this.currentTip.bind(bindEl);
28682 this.currentRegion = Roo.lib.Region.getRegion(dom);
28683 this.currentTip.enter();
28686 leave : function(ev)
28688 var dom = ev.getTarget();
28689 //Roo.log(['leave',dom]);
28690 if (!this.currentEl) {
28695 if (dom != this.currentEl.dom) {
28698 var xy = ev.getXY();
28699 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28702 // only activate leave if mouse cursor is outside... bounding box..
28707 if (this.currentTip) {
28708 this.currentTip.leave();
28710 //Roo.log('clear currentEl');
28711 this.currentEl = false;
28716 'left' : ['r-l', [-2,0], 'right'],
28717 'right' : ['l-r', [2,0], 'left'],
28718 'bottom' : ['t-b', [0,2], 'top'],
28719 'top' : [ 'b-t', [0,-2], 'bottom']
28725 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28730 delay : null, // can be { show : 300 , hide: 500}
28734 hoverState : null, //???
28736 placement : 'bottom',
28740 getAutoCreate : function(){
28747 cls : 'tooltip-arrow arrow'
28750 cls : 'tooltip-inner'
28757 bind : function(el)
28762 initEvents : function()
28764 this.arrowEl = this.el.select('.arrow', true).first();
28765 this.innerEl = this.el.select('.tooltip-inner', true).first();
28768 enter : function () {
28770 if (this.timeout != null) {
28771 clearTimeout(this.timeout);
28774 this.hoverState = 'in';
28775 //Roo.log("enter - show");
28776 if (!this.delay || !this.delay.show) {
28781 this.timeout = setTimeout(function () {
28782 if (_t.hoverState == 'in') {
28785 }, this.delay.show);
28789 clearTimeout(this.timeout);
28791 this.hoverState = 'out';
28792 if (!this.delay || !this.delay.hide) {
28798 this.timeout = setTimeout(function () {
28799 //Roo.log("leave - timeout");
28801 if (_t.hoverState == 'out') {
28803 Roo.bootstrap.Tooltip.currentEl = false;
28808 show : function (msg)
28811 this.render(document.body);
28814 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28816 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28818 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28820 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28821 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28823 var placement = typeof this.placement == 'function' ?
28824 this.placement.call(this, this.el, on_el) :
28827 var autoToken = /\s?auto?\s?/i;
28828 var autoPlace = autoToken.test(placement);
28830 placement = placement.replace(autoToken, '') || 'top';
28834 //this.el.setXY([0,0]);
28836 //this.el.dom.style.display='block';
28838 //this.el.appendTo(on_el);
28840 var p = this.getPosition();
28841 var box = this.el.getBox();
28847 var align = this.alignment[placement];
28849 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28851 if(placement == 'top' || placement == 'bottom'){
28853 placement = 'right';
28856 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28857 placement = 'left';
28860 var scroll = Roo.select('body', true).first().getScroll();
28862 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28866 align = this.alignment[placement];
28868 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28872 this.el.alignTo(this.bindEl, align[0],align[1]);
28873 //var arrow = this.el.select('.arrow',true).first();
28874 //arrow.set(align[2],
28876 this.el.addClass(placement);
28877 this.el.addClass("bs-tooltip-"+ placement);
28879 this.el.addClass('in fade show');
28881 this.hoverState = null;
28883 if (this.el.hasClass('fade')) {
28898 //this.el.setXY([0,0]);
28899 this.el.removeClass(['show', 'in']);
28915 * @class Roo.bootstrap.LocationPicker
28916 * @extends Roo.bootstrap.Component
28917 * Bootstrap LocationPicker class
28918 * @cfg {Number} latitude Position when init default 0
28919 * @cfg {Number} longitude Position when init default 0
28920 * @cfg {Number} zoom default 15
28921 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28922 * @cfg {Boolean} mapTypeControl default false
28923 * @cfg {Boolean} disableDoubleClickZoom default false
28924 * @cfg {Boolean} scrollwheel default true
28925 * @cfg {Boolean} streetViewControl default false
28926 * @cfg {Number} radius default 0
28927 * @cfg {String} locationName
28928 * @cfg {Boolean} draggable default true
28929 * @cfg {Boolean} enableAutocomplete default false
28930 * @cfg {Boolean} enableReverseGeocode default true
28931 * @cfg {String} markerTitle
28934 * Create a new LocationPicker
28935 * @param {Object} config The config object
28939 Roo.bootstrap.LocationPicker = function(config){
28941 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28946 * Fires when the picker initialized.
28947 * @param {Roo.bootstrap.LocationPicker} this
28948 * @param {Google Location} location
28952 * @event positionchanged
28953 * Fires when the picker position changed.
28954 * @param {Roo.bootstrap.LocationPicker} this
28955 * @param {Google Location} location
28957 positionchanged : true,
28960 * Fires when the map resize.
28961 * @param {Roo.bootstrap.LocationPicker} this
28966 * Fires when the map show.
28967 * @param {Roo.bootstrap.LocationPicker} this
28972 * Fires when the map hide.
28973 * @param {Roo.bootstrap.LocationPicker} this
28978 * Fires when click the map.
28979 * @param {Roo.bootstrap.LocationPicker} this
28980 * @param {Map event} e
28984 * @event mapRightClick
28985 * Fires when right click the map.
28986 * @param {Roo.bootstrap.LocationPicker} this
28987 * @param {Map event} e
28989 mapRightClick : true,
28991 * @event markerClick
28992 * Fires when click the marker.
28993 * @param {Roo.bootstrap.LocationPicker} this
28994 * @param {Map event} e
28996 markerClick : true,
28998 * @event markerRightClick
28999 * Fires when right click the marker.
29000 * @param {Roo.bootstrap.LocationPicker} this
29001 * @param {Map event} e
29003 markerRightClick : true,
29005 * @event OverlayViewDraw
29006 * Fires when OverlayView Draw
29007 * @param {Roo.bootstrap.LocationPicker} this
29009 OverlayViewDraw : true,
29011 * @event OverlayViewOnAdd
29012 * Fires when OverlayView Draw
29013 * @param {Roo.bootstrap.LocationPicker} this
29015 OverlayViewOnAdd : true,
29017 * @event OverlayViewOnRemove
29018 * Fires when OverlayView Draw
29019 * @param {Roo.bootstrap.LocationPicker} this
29021 OverlayViewOnRemove : true,
29023 * @event OverlayViewShow
29024 * Fires when OverlayView Draw
29025 * @param {Roo.bootstrap.LocationPicker} this
29026 * @param {Pixel} cpx
29028 OverlayViewShow : true,
29030 * @event OverlayViewHide
29031 * Fires when OverlayView Draw
29032 * @param {Roo.bootstrap.LocationPicker} this
29034 OverlayViewHide : true,
29036 * @event loadexception
29037 * Fires when load google lib failed.
29038 * @param {Roo.bootstrap.LocationPicker} this
29040 loadexception : true
29045 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29047 gMapContext: false,
29053 mapTypeControl: false,
29054 disableDoubleClickZoom: false,
29056 streetViewControl: false,
29060 enableAutocomplete: false,
29061 enableReverseGeocode: true,
29064 getAutoCreate: function()
29069 cls: 'roo-location-picker'
29075 initEvents: function(ct, position)
29077 if(!this.el.getWidth() || this.isApplied()){
29081 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29086 initial: function()
29088 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29089 this.fireEvent('loadexception', this);
29093 if(!this.mapTypeId){
29094 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29097 this.gMapContext = this.GMapContext();
29099 this.initOverlayView();
29101 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29105 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29106 _this.setPosition(_this.gMapContext.marker.position);
29109 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29110 _this.fireEvent('mapClick', this, event);
29114 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29115 _this.fireEvent('mapRightClick', this, event);
29119 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29120 _this.fireEvent('markerClick', this, event);
29124 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29125 _this.fireEvent('markerRightClick', this, event);
29129 this.setPosition(this.gMapContext.location);
29131 this.fireEvent('initial', this, this.gMapContext.location);
29134 initOverlayView: function()
29138 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29142 _this.fireEvent('OverlayViewDraw', _this);
29147 _this.fireEvent('OverlayViewOnAdd', _this);
29150 onRemove: function()
29152 _this.fireEvent('OverlayViewOnRemove', _this);
29155 show: function(cpx)
29157 _this.fireEvent('OverlayViewShow', _this, cpx);
29162 _this.fireEvent('OverlayViewHide', _this);
29168 fromLatLngToContainerPixel: function(event)
29170 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29173 isApplied: function()
29175 return this.getGmapContext() == false ? false : true;
29178 getGmapContext: function()
29180 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29183 GMapContext: function()
29185 var position = new google.maps.LatLng(this.latitude, this.longitude);
29187 var _map = new google.maps.Map(this.el.dom, {
29190 mapTypeId: this.mapTypeId,
29191 mapTypeControl: this.mapTypeControl,
29192 disableDoubleClickZoom: this.disableDoubleClickZoom,
29193 scrollwheel: this.scrollwheel,
29194 streetViewControl: this.streetViewControl,
29195 locationName: this.locationName,
29196 draggable: this.draggable,
29197 enableAutocomplete: this.enableAutocomplete,
29198 enableReverseGeocode: this.enableReverseGeocode
29201 var _marker = new google.maps.Marker({
29202 position: position,
29204 title: this.markerTitle,
29205 draggable: this.draggable
29212 location: position,
29213 radius: this.radius,
29214 locationName: this.locationName,
29215 addressComponents: {
29216 formatted_address: null,
29217 addressLine1: null,
29218 addressLine2: null,
29220 streetNumber: null,
29224 stateOrProvince: null
29227 domContainer: this.el.dom,
29228 geodecoder: new google.maps.Geocoder()
29232 drawCircle: function(center, radius, options)
29234 if (this.gMapContext.circle != null) {
29235 this.gMapContext.circle.setMap(null);
29239 options = Roo.apply({}, options, {
29240 strokeColor: "#0000FF",
29241 strokeOpacity: .35,
29243 fillColor: "#0000FF",
29247 options.map = this.gMapContext.map;
29248 options.radius = radius;
29249 options.center = center;
29250 this.gMapContext.circle = new google.maps.Circle(options);
29251 return this.gMapContext.circle;
29257 setPosition: function(location)
29259 this.gMapContext.location = location;
29260 this.gMapContext.marker.setPosition(location);
29261 this.gMapContext.map.panTo(location);
29262 this.drawCircle(location, this.gMapContext.radius, {});
29266 if (this.gMapContext.settings.enableReverseGeocode) {
29267 this.gMapContext.geodecoder.geocode({
29268 latLng: this.gMapContext.location
29269 }, function(results, status) {
29271 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29272 _this.gMapContext.locationName = results[0].formatted_address;
29273 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29275 _this.fireEvent('positionchanged', this, location);
29282 this.fireEvent('positionchanged', this, location);
29287 google.maps.event.trigger(this.gMapContext.map, "resize");
29289 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29291 this.fireEvent('resize', this);
29294 setPositionByLatLng: function(latitude, longitude)
29296 this.setPosition(new google.maps.LatLng(latitude, longitude));
29299 getCurrentPosition: function()
29302 latitude: this.gMapContext.location.lat(),
29303 longitude: this.gMapContext.location.lng()
29307 getAddressName: function()
29309 return this.gMapContext.locationName;
29312 getAddressComponents: function()
29314 return this.gMapContext.addressComponents;
29317 address_component_from_google_geocode: function(address_components)
29321 for (var i = 0; i < address_components.length; i++) {
29322 var component = address_components[i];
29323 if (component.types.indexOf("postal_code") >= 0) {
29324 result.postalCode = component.short_name;
29325 } else if (component.types.indexOf("street_number") >= 0) {
29326 result.streetNumber = component.short_name;
29327 } else if (component.types.indexOf("route") >= 0) {
29328 result.streetName = component.short_name;
29329 } else if (component.types.indexOf("neighborhood") >= 0) {
29330 result.city = component.short_name;
29331 } else if (component.types.indexOf("locality") >= 0) {
29332 result.city = component.short_name;
29333 } else if (component.types.indexOf("sublocality") >= 0) {
29334 result.district = component.short_name;
29335 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29336 result.stateOrProvince = component.short_name;
29337 } else if (component.types.indexOf("country") >= 0) {
29338 result.country = component.short_name;
29342 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29343 result.addressLine2 = "";
29347 setZoomLevel: function(zoom)
29349 this.gMapContext.map.setZoom(zoom);
29362 this.fireEvent('show', this);
29373 this.fireEvent('hide', this);
29378 Roo.apply(Roo.bootstrap.LocationPicker, {
29380 OverlayView : function(map, options)
29382 options = options || {};
29389 * @class Roo.bootstrap.Alert
29390 * @extends Roo.bootstrap.Component
29391 * Bootstrap Alert class - shows an alert area box
29393 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29394 Enter a valid email address
29397 * @cfg {String} title The title of alert
29398 * @cfg {String} html The content of alert
29399 * @cfg {String} weight ( success | info | warning | danger )
29400 * @cfg {String} faicon font-awesomeicon
29403 * Create a new alert
29404 * @param {Object} config The config object
29408 Roo.bootstrap.Alert = function(config){
29409 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29413 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29420 getAutoCreate : function()
29429 cls : 'roo-alert-icon'
29434 cls : 'roo-alert-title',
29439 cls : 'roo-alert-text',
29446 cfg.cn[0].cls += ' fa ' + this.faicon;
29450 cfg.cls += ' alert-' + this.weight;
29456 initEvents: function()
29458 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29461 setTitle : function(str)
29463 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29466 setText : function(str)
29468 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29471 setWeight : function(weight)
29474 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29477 this.weight = weight;
29479 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29482 setIcon : function(icon)
29485 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29488 this.faicon = icon;
29490 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29511 * @class Roo.bootstrap.UploadCropbox
29512 * @extends Roo.bootstrap.Component
29513 * Bootstrap UploadCropbox class
29514 * @cfg {String} emptyText show when image has been loaded
29515 * @cfg {String} rotateNotify show when image too small to rotate
29516 * @cfg {Number} errorTimeout default 3000
29517 * @cfg {Number} minWidth default 300
29518 * @cfg {Number} minHeight default 300
29519 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29520 * @cfg {Boolean} isDocument (true|false) default false
29521 * @cfg {String} url action url
29522 * @cfg {String} paramName default 'imageUpload'
29523 * @cfg {String} method default POST
29524 * @cfg {Boolean} loadMask (true|false) default true
29525 * @cfg {Boolean} loadingText default 'Loading...'
29528 * Create a new UploadCropbox
29529 * @param {Object} config The config object
29532 Roo.bootstrap.UploadCropbox = function(config){
29533 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29537 * @event beforeselectfile
29538 * Fire before select file
29539 * @param {Roo.bootstrap.UploadCropbox} this
29541 "beforeselectfile" : true,
29544 * Fire after initEvent
29545 * @param {Roo.bootstrap.UploadCropbox} this
29550 * Fire after initEvent
29551 * @param {Roo.bootstrap.UploadCropbox} this
29552 * @param {String} data
29557 * Fire when preparing the file data
29558 * @param {Roo.bootstrap.UploadCropbox} this
29559 * @param {Object} file
29564 * Fire when get exception
29565 * @param {Roo.bootstrap.UploadCropbox} this
29566 * @param {XMLHttpRequest} xhr
29568 "exception" : true,
29570 * @event beforeloadcanvas
29571 * Fire before load the canvas
29572 * @param {Roo.bootstrap.UploadCropbox} this
29573 * @param {String} src
29575 "beforeloadcanvas" : true,
29578 * Fire when trash image
29579 * @param {Roo.bootstrap.UploadCropbox} this
29584 * Fire when download the image
29585 * @param {Roo.bootstrap.UploadCropbox} this
29589 * @event footerbuttonclick
29590 * Fire when footerbuttonclick
29591 * @param {Roo.bootstrap.UploadCropbox} this
29592 * @param {String} type
29594 "footerbuttonclick" : true,
29598 * @param {Roo.bootstrap.UploadCropbox} this
29603 * Fire when rotate the image
29604 * @param {Roo.bootstrap.UploadCropbox} this
29605 * @param {String} pos
29610 * Fire when inspect the file
29611 * @param {Roo.bootstrap.UploadCropbox} this
29612 * @param {Object} file
29617 * Fire when xhr upload the file
29618 * @param {Roo.bootstrap.UploadCropbox} this
29619 * @param {Object} data
29624 * Fire when arrange the file data
29625 * @param {Roo.bootstrap.UploadCropbox} this
29626 * @param {Object} formData
29631 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29634 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29636 emptyText : 'Click to upload image',
29637 rotateNotify : 'Image is too small to rotate',
29638 errorTimeout : 3000,
29652 cropType : 'image/jpeg',
29654 canvasLoaded : false,
29655 isDocument : false,
29657 paramName : 'imageUpload',
29659 loadingText : 'Loading...',
29662 getAutoCreate : function()
29666 cls : 'roo-upload-cropbox',
29670 cls : 'roo-upload-cropbox-selector',
29675 cls : 'roo-upload-cropbox-body',
29676 style : 'cursor:pointer',
29680 cls : 'roo-upload-cropbox-preview'
29684 cls : 'roo-upload-cropbox-thumb'
29688 cls : 'roo-upload-cropbox-empty-notify',
29689 html : this.emptyText
29693 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29694 html : this.rotateNotify
29700 cls : 'roo-upload-cropbox-footer',
29703 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29713 onRender : function(ct, position)
29715 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29717 if (this.buttons.length) {
29719 Roo.each(this.buttons, function(bb) {
29721 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29723 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29729 this.maskEl = this.el;
29733 initEvents : function()
29735 this.urlAPI = (window.createObjectURL && window) ||
29736 (window.URL && URL.revokeObjectURL && URL) ||
29737 (window.webkitURL && webkitURL);
29739 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29740 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29742 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29743 this.selectorEl.hide();
29745 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29746 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29749 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29750 this.thumbEl.hide();
29752 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29753 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29756 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29757 this.errorEl.hide();
29759 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29760 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29761 this.footerEl.hide();
29763 this.setThumbBoxSize();
29769 this.fireEvent('initial', this);
29776 window.addEventListener("resize", function() { _this.resize(); } );
29778 this.bodyEl.on('click', this.beforeSelectFile, this);
29781 this.bodyEl.on('touchstart', this.onTouchStart, this);
29782 this.bodyEl.on('touchmove', this.onTouchMove, this);
29783 this.bodyEl.on('touchend', this.onTouchEnd, this);
29787 this.bodyEl.on('mousedown', this.onMouseDown, this);
29788 this.bodyEl.on('mousemove', this.onMouseMove, this);
29789 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29790 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29791 Roo.get(document).on('mouseup', this.onMouseUp, this);
29794 this.selectorEl.on('change', this.onFileSelected, this);
29800 this.baseScale = 1;
29802 this.baseRotate = 1;
29803 this.dragable = false;
29804 this.pinching = false;
29807 this.cropData = false;
29808 this.notifyEl.dom.innerHTML = this.emptyText;
29810 this.selectorEl.dom.value = '';
29814 resize : function()
29816 if(this.fireEvent('resize', this) != false){
29817 this.setThumbBoxPosition();
29818 this.setCanvasPosition();
29822 onFooterButtonClick : function(e, el, o, type)
29825 case 'rotate-left' :
29826 this.onRotateLeft(e);
29828 case 'rotate-right' :
29829 this.onRotateRight(e);
29832 this.beforeSelectFile(e);
29847 this.fireEvent('footerbuttonclick', this, type);
29850 beforeSelectFile : function(e)
29852 e.preventDefault();
29854 if(this.fireEvent('beforeselectfile', this) != false){
29855 this.selectorEl.dom.click();
29859 onFileSelected : function(e)
29861 e.preventDefault();
29863 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29867 var file = this.selectorEl.dom.files[0];
29869 if(this.fireEvent('inspect', this, file) != false){
29870 this.prepare(file);
29875 trash : function(e)
29877 this.fireEvent('trash', this);
29880 download : function(e)
29882 this.fireEvent('download', this);
29885 loadCanvas : function(src)
29887 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29891 this.imageEl = document.createElement('img');
29895 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29897 this.imageEl.src = src;
29901 onLoadCanvas : function()
29903 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29904 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29906 this.bodyEl.un('click', this.beforeSelectFile, this);
29908 this.notifyEl.hide();
29909 this.thumbEl.show();
29910 this.footerEl.show();
29912 this.baseRotateLevel();
29914 if(this.isDocument){
29915 this.setThumbBoxSize();
29918 this.setThumbBoxPosition();
29920 this.baseScaleLevel();
29926 this.canvasLoaded = true;
29929 this.maskEl.unmask();
29934 setCanvasPosition : function()
29936 if(!this.canvasEl){
29940 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29941 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29943 this.previewEl.setLeft(pw);
29944 this.previewEl.setTop(ph);
29948 onMouseDown : function(e)
29952 this.dragable = true;
29953 this.pinching = false;
29955 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29956 this.dragable = false;
29960 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29961 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29965 onMouseMove : function(e)
29969 if(!this.canvasLoaded){
29973 if (!this.dragable){
29977 var minX = Math.ceil(this.thumbEl.getLeft(true));
29978 var minY = Math.ceil(this.thumbEl.getTop(true));
29980 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29981 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29983 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29984 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29986 x = x - this.mouseX;
29987 y = y - this.mouseY;
29989 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29990 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29992 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29993 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29995 this.previewEl.setLeft(bgX);
29996 this.previewEl.setTop(bgY);
29998 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29999 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30002 onMouseUp : function(e)
30006 this.dragable = false;
30009 onMouseWheel : function(e)
30013 this.startScale = this.scale;
30015 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30017 if(!this.zoomable()){
30018 this.scale = this.startScale;
30027 zoomable : function()
30029 var minScale = this.thumbEl.getWidth() / this.minWidth;
30031 if(this.minWidth < this.minHeight){
30032 minScale = this.thumbEl.getHeight() / this.minHeight;
30035 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30036 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30040 (this.rotate == 0 || this.rotate == 180) &&
30042 width > this.imageEl.OriginWidth ||
30043 height > this.imageEl.OriginHeight ||
30044 (width < this.minWidth && height < this.minHeight)
30052 (this.rotate == 90 || this.rotate == 270) &&
30054 width > this.imageEl.OriginWidth ||
30055 height > this.imageEl.OriginHeight ||
30056 (width < this.minHeight && height < this.minWidth)
30063 !this.isDocument &&
30064 (this.rotate == 0 || this.rotate == 180) &&
30066 width < this.minWidth ||
30067 width > this.imageEl.OriginWidth ||
30068 height < this.minHeight ||
30069 height > this.imageEl.OriginHeight
30076 !this.isDocument &&
30077 (this.rotate == 90 || this.rotate == 270) &&
30079 width < this.minHeight ||
30080 width > this.imageEl.OriginWidth ||
30081 height < this.minWidth ||
30082 height > this.imageEl.OriginHeight
30092 onRotateLeft : function(e)
30094 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30096 var minScale = this.thumbEl.getWidth() / this.minWidth;
30098 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30099 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30101 this.startScale = this.scale;
30103 while (this.getScaleLevel() < minScale){
30105 this.scale = this.scale + 1;
30107 if(!this.zoomable()){
30112 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30113 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30118 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30125 this.scale = this.startScale;
30127 this.onRotateFail();
30132 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30134 if(this.isDocument){
30135 this.setThumbBoxSize();
30136 this.setThumbBoxPosition();
30137 this.setCanvasPosition();
30142 this.fireEvent('rotate', this, 'left');
30146 onRotateRight : function(e)
30148 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30150 var minScale = this.thumbEl.getWidth() / this.minWidth;
30152 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30153 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30155 this.startScale = this.scale;
30157 while (this.getScaleLevel() < minScale){
30159 this.scale = this.scale + 1;
30161 if(!this.zoomable()){
30166 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30167 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30172 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30179 this.scale = this.startScale;
30181 this.onRotateFail();
30186 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30188 if(this.isDocument){
30189 this.setThumbBoxSize();
30190 this.setThumbBoxPosition();
30191 this.setCanvasPosition();
30196 this.fireEvent('rotate', this, 'right');
30199 onRotateFail : function()
30201 this.errorEl.show(true);
30205 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30210 this.previewEl.dom.innerHTML = '';
30212 var canvasEl = document.createElement("canvas");
30214 var contextEl = canvasEl.getContext("2d");
30216 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30217 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30218 var center = this.imageEl.OriginWidth / 2;
30220 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30221 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30222 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30223 center = this.imageEl.OriginHeight / 2;
30226 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30228 contextEl.translate(center, center);
30229 contextEl.rotate(this.rotate * Math.PI / 180);
30231 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30233 this.canvasEl = document.createElement("canvas");
30235 this.contextEl = this.canvasEl.getContext("2d");
30237 switch (this.rotate) {
30240 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30241 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30243 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30248 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30249 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30251 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30252 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);
30256 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30261 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30262 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30264 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30265 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);
30269 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);
30274 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30275 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30277 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30278 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30282 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);
30289 this.previewEl.appendChild(this.canvasEl);
30291 this.setCanvasPosition();
30296 if(!this.canvasLoaded){
30300 var imageCanvas = document.createElement("canvas");
30302 var imageContext = imageCanvas.getContext("2d");
30304 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30305 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30307 var center = imageCanvas.width / 2;
30309 imageContext.translate(center, center);
30311 imageContext.rotate(this.rotate * Math.PI / 180);
30313 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30315 var canvas = document.createElement("canvas");
30317 var context = canvas.getContext("2d");
30319 canvas.width = this.minWidth;
30320 canvas.height = this.minHeight;
30322 switch (this.rotate) {
30325 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30326 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30328 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30329 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30331 var targetWidth = this.minWidth - 2 * x;
30332 var targetHeight = this.minHeight - 2 * y;
30336 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30337 scale = targetWidth / width;
30340 if(x > 0 && y == 0){
30341 scale = targetHeight / height;
30344 if(x > 0 && y > 0){
30345 scale = targetWidth / width;
30347 if(width < height){
30348 scale = targetHeight / height;
30352 context.scale(scale, scale);
30354 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30355 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30357 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30358 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30360 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30365 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30366 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30368 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30369 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30371 var targetWidth = this.minWidth - 2 * x;
30372 var targetHeight = this.minHeight - 2 * y;
30376 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30377 scale = targetWidth / width;
30380 if(x > 0 && y == 0){
30381 scale = targetHeight / height;
30384 if(x > 0 && y > 0){
30385 scale = targetWidth / width;
30387 if(width < height){
30388 scale = targetHeight / height;
30392 context.scale(scale, scale);
30394 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30395 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30397 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30398 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30400 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30402 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30407 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30408 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30410 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30411 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30413 var targetWidth = this.minWidth - 2 * x;
30414 var targetHeight = this.minHeight - 2 * y;
30418 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30419 scale = targetWidth / width;
30422 if(x > 0 && y == 0){
30423 scale = targetHeight / height;
30426 if(x > 0 && y > 0){
30427 scale = targetWidth / width;
30429 if(width < height){
30430 scale = targetHeight / height;
30434 context.scale(scale, scale);
30436 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30437 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30439 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30440 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30442 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30443 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30445 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30450 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30451 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30453 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30454 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30456 var targetWidth = this.minWidth - 2 * x;
30457 var targetHeight = this.minHeight - 2 * y;
30461 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30462 scale = targetWidth / width;
30465 if(x > 0 && y == 0){
30466 scale = targetHeight / height;
30469 if(x > 0 && y > 0){
30470 scale = targetWidth / width;
30472 if(width < height){
30473 scale = targetHeight / height;
30477 context.scale(scale, scale);
30479 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30480 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30482 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30483 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30485 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30487 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30494 this.cropData = canvas.toDataURL(this.cropType);
30496 if(this.fireEvent('crop', this, this.cropData) !== false){
30497 this.process(this.file, this.cropData);
30504 setThumbBoxSize : function()
30508 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30509 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30510 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30512 this.minWidth = width;
30513 this.minHeight = height;
30515 if(this.rotate == 90 || this.rotate == 270){
30516 this.minWidth = height;
30517 this.minHeight = width;
30522 width = Math.ceil(this.minWidth * height / this.minHeight);
30524 if(this.minWidth > this.minHeight){
30526 height = Math.ceil(this.minHeight * width / this.minWidth);
30529 this.thumbEl.setStyle({
30530 width : width + 'px',
30531 height : height + 'px'
30538 setThumbBoxPosition : function()
30540 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30541 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30543 this.thumbEl.setLeft(x);
30544 this.thumbEl.setTop(y);
30548 baseRotateLevel : function()
30550 this.baseRotate = 1;
30553 typeof(this.exif) != 'undefined' &&
30554 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30555 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30557 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30560 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30564 baseScaleLevel : function()
30568 if(this.isDocument){
30570 if(this.baseRotate == 6 || this.baseRotate == 8){
30572 height = this.thumbEl.getHeight();
30573 this.baseScale = height / this.imageEl.OriginWidth;
30575 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30576 width = this.thumbEl.getWidth();
30577 this.baseScale = width / this.imageEl.OriginHeight;
30583 height = this.thumbEl.getHeight();
30584 this.baseScale = height / this.imageEl.OriginHeight;
30586 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30587 width = this.thumbEl.getWidth();
30588 this.baseScale = width / this.imageEl.OriginWidth;
30594 if(this.baseRotate == 6 || this.baseRotate == 8){
30596 width = this.thumbEl.getHeight();
30597 this.baseScale = width / this.imageEl.OriginHeight;
30599 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30600 height = this.thumbEl.getWidth();
30601 this.baseScale = height / this.imageEl.OriginHeight;
30604 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30605 height = this.thumbEl.getWidth();
30606 this.baseScale = height / this.imageEl.OriginHeight;
30608 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30609 width = this.thumbEl.getHeight();
30610 this.baseScale = width / this.imageEl.OriginWidth;
30617 width = this.thumbEl.getWidth();
30618 this.baseScale = width / this.imageEl.OriginWidth;
30620 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30621 height = this.thumbEl.getHeight();
30622 this.baseScale = height / this.imageEl.OriginHeight;
30625 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30627 height = this.thumbEl.getHeight();
30628 this.baseScale = height / this.imageEl.OriginHeight;
30630 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30631 width = this.thumbEl.getWidth();
30632 this.baseScale = width / this.imageEl.OriginWidth;
30640 getScaleLevel : function()
30642 return this.baseScale * Math.pow(1.1, this.scale);
30645 onTouchStart : function(e)
30647 if(!this.canvasLoaded){
30648 this.beforeSelectFile(e);
30652 var touches = e.browserEvent.touches;
30658 if(touches.length == 1){
30659 this.onMouseDown(e);
30663 if(touches.length != 2){
30669 for(var i = 0, finger; finger = touches[i]; i++){
30670 coords.push(finger.pageX, finger.pageY);
30673 var x = Math.pow(coords[0] - coords[2], 2);
30674 var y = Math.pow(coords[1] - coords[3], 2);
30676 this.startDistance = Math.sqrt(x + y);
30678 this.startScale = this.scale;
30680 this.pinching = true;
30681 this.dragable = false;
30685 onTouchMove : function(e)
30687 if(!this.pinching && !this.dragable){
30691 var touches = e.browserEvent.touches;
30698 this.onMouseMove(e);
30704 for(var i = 0, finger; finger = touches[i]; i++){
30705 coords.push(finger.pageX, finger.pageY);
30708 var x = Math.pow(coords[0] - coords[2], 2);
30709 var y = Math.pow(coords[1] - coords[3], 2);
30711 this.endDistance = Math.sqrt(x + y);
30713 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30715 if(!this.zoomable()){
30716 this.scale = this.startScale;
30724 onTouchEnd : function(e)
30726 this.pinching = false;
30727 this.dragable = false;
30731 process : function(file, crop)
30734 this.maskEl.mask(this.loadingText);
30737 this.xhr = new XMLHttpRequest();
30739 file.xhr = this.xhr;
30741 this.xhr.open(this.method, this.url, true);
30744 "Accept": "application/json",
30745 "Cache-Control": "no-cache",
30746 "X-Requested-With": "XMLHttpRequest"
30749 for (var headerName in headers) {
30750 var headerValue = headers[headerName];
30752 this.xhr.setRequestHeader(headerName, headerValue);
30758 this.xhr.onload = function()
30760 _this.xhrOnLoad(_this.xhr);
30763 this.xhr.onerror = function()
30765 _this.xhrOnError(_this.xhr);
30768 var formData = new FormData();
30770 formData.append('returnHTML', 'NO');
30773 formData.append('crop', crop);
30776 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30777 formData.append(this.paramName, file, file.name);
30780 if(typeof(file.filename) != 'undefined'){
30781 formData.append('filename', file.filename);
30784 if(typeof(file.mimetype) != 'undefined'){
30785 formData.append('mimetype', file.mimetype);
30788 if(this.fireEvent('arrange', this, formData) != false){
30789 this.xhr.send(formData);
30793 xhrOnLoad : function(xhr)
30796 this.maskEl.unmask();
30799 if (xhr.readyState !== 4) {
30800 this.fireEvent('exception', this, xhr);
30804 var response = Roo.decode(xhr.responseText);
30806 if(!response.success){
30807 this.fireEvent('exception', this, xhr);
30811 var response = Roo.decode(xhr.responseText);
30813 this.fireEvent('upload', this, response);
30817 xhrOnError : function()
30820 this.maskEl.unmask();
30823 Roo.log('xhr on error');
30825 var response = Roo.decode(xhr.responseText);
30831 prepare : function(file)
30834 this.maskEl.mask(this.loadingText);
30840 if(typeof(file) === 'string'){
30841 this.loadCanvas(file);
30845 if(!file || !this.urlAPI){
30850 this.cropType = file.type;
30854 if(this.fireEvent('prepare', this, this.file) != false){
30856 var reader = new FileReader();
30858 reader.onload = function (e) {
30859 if (e.target.error) {
30860 Roo.log(e.target.error);
30864 var buffer = e.target.result,
30865 dataView = new DataView(buffer),
30867 maxOffset = dataView.byteLength - 4,
30871 if (dataView.getUint16(0) === 0xffd8) {
30872 while (offset < maxOffset) {
30873 markerBytes = dataView.getUint16(offset);
30875 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30876 markerLength = dataView.getUint16(offset + 2) + 2;
30877 if (offset + markerLength > dataView.byteLength) {
30878 Roo.log('Invalid meta data: Invalid segment size.');
30882 if(markerBytes == 0xffe1){
30883 _this.parseExifData(
30890 offset += markerLength;
30900 var url = _this.urlAPI.createObjectURL(_this.file);
30902 _this.loadCanvas(url);
30907 reader.readAsArrayBuffer(this.file);
30913 parseExifData : function(dataView, offset, length)
30915 var tiffOffset = offset + 10,
30919 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30920 // No Exif data, might be XMP data instead
30924 // Check for the ASCII code for "Exif" (0x45786966):
30925 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30926 // No Exif data, might be XMP data instead
30929 if (tiffOffset + 8 > dataView.byteLength) {
30930 Roo.log('Invalid Exif data: Invalid segment size.');
30933 // Check for the two null bytes:
30934 if (dataView.getUint16(offset + 8) !== 0x0000) {
30935 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30938 // Check the byte alignment:
30939 switch (dataView.getUint16(tiffOffset)) {
30941 littleEndian = true;
30944 littleEndian = false;
30947 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30950 // Check for the TIFF tag marker (0x002A):
30951 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30952 Roo.log('Invalid Exif data: Missing TIFF marker.');
30955 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30956 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30958 this.parseExifTags(
30961 tiffOffset + dirOffset,
30966 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30971 if (dirOffset + 6 > dataView.byteLength) {
30972 Roo.log('Invalid Exif data: Invalid directory offset.');
30975 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30976 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30977 if (dirEndOffset + 4 > dataView.byteLength) {
30978 Roo.log('Invalid Exif data: Invalid directory size.');
30981 for (i = 0; i < tagsNumber; i += 1) {
30985 dirOffset + 2 + 12 * i, // tag offset
30989 // Return the offset to the next directory:
30990 return dataView.getUint32(dirEndOffset, littleEndian);
30993 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30995 var tag = dataView.getUint16(offset, littleEndian);
30997 this.exif[tag] = this.getExifValue(
31001 dataView.getUint16(offset + 2, littleEndian), // tag type
31002 dataView.getUint32(offset + 4, littleEndian), // tag length
31007 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31009 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31018 Roo.log('Invalid Exif data: Invalid tag type.');
31022 tagSize = tagType.size * length;
31023 // Determine if the value is contained in the dataOffset bytes,
31024 // or if the value at the dataOffset is a pointer to the actual data:
31025 dataOffset = tagSize > 4 ?
31026 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31027 if (dataOffset + tagSize > dataView.byteLength) {
31028 Roo.log('Invalid Exif data: Invalid data offset.');
31031 if (length === 1) {
31032 return tagType.getValue(dataView, dataOffset, littleEndian);
31035 for (i = 0; i < length; i += 1) {
31036 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31039 if (tagType.ascii) {
31041 // Concatenate the chars:
31042 for (i = 0; i < values.length; i += 1) {
31044 // Ignore the terminating NULL byte(s):
31045 if (c === '\u0000') {
31057 Roo.apply(Roo.bootstrap.UploadCropbox, {
31059 'Orientation': 0x0112
31063 1: 0, //'top-left',
31065 3: 180, //'bottom-right',
31066 // 4: 'bottom-left',
31068 6: 90, //'right-top',
31069 // 7: 'right-bottom',
31070 8: 270 //'left-bottom'
31074 // byte, 8-bit unsigned int:
31076 getValue: function (dataView, dataOffset) {
31077 return dataView.getUint8(dataOffset);
31081 // ascii, 8-bit byte:
31083 getValue: function (dataView, dataOffset) {
31084 return String.fromCharCode(dataView.getUint8(dataOffset));
31089 // short, 16 bit int:
31091 getValue: function (dataView, dataOffset, littleEndian) {
31092 return dataView.getUint16(dataOffset, littleEndian);
31096 // long, 32 bit int:
31098 getValue: function (dataView, dataOffset, littleEndian) {
31099 return dataView.getUint32(dataOffset, littleEndian);
31103 // rational = two long values, first is numerator, second is denominator:
31105 getValue: function (dataView, dataOffset, littleEndian) {
31106 return dataView.getUint32(dataOffset, littleEndian) /
31107 dataView.getUint32(dataOffset + 4, littleEndian);
31111 // slong, 32 bit signed int:
31113 getValue: function (dataView, dataOffset, littleEndian) {
31114 return dataView.getInt32(dataOffset, littleEndian);
31118 // srational, two slongs, first is numerator, second is denominator:
31120 getValue: function (dataView, dataOffset, littleEndian) {
31121 return dataView.getInt32(dataOffset, littleEndian) /
31122 dataView.getInt32(dataOffset + 4, littleEndian);
31132 cls : 'btn-group roo-upload-cropbox-rotate-left',
31133 action : 'rotate-left',
31137 cls : 'btn btn-default',
31138 html : '<i class="fa fa-undo"></i>'
31144 cls : 'btn-group roo-upload-cropbox-picture',
31145 action : 'picture',
31149 cls : 'btn btn-default',
31150 html : '<i class="fa fa-picture-o"></i>'
31156 cls : 'btn-group roo-upload-cropbox-rotate-right',
31157 action : 'rotate-right',
31161 cls : 'btn btn-default',
31162 html : '<i class="fa fa-repeat"></i>'
31170 cls : 'btn-group roo-upload-cropbox-rotate-left',
31171 action : 'rotate-left',
31175 cls : 'btn btn-default',
31176 html : '<i class="fa fa-undo"></i>'
31182 cls : 'btn-group roo-upload-cropbox-download',
31183 action : 'download',
31187 cls : 'btn btn-default',
31188 html : '<i class="fa fa-download"></i>'
31194 cls : 'btn-group roo-upload-cropbox-crop',
31199 cls : 'btn btn-default',
31200 html : '<i class="fa fa-crop"></i>'
31206 cls : 'btn-group roo-upload-cropbox-trash',
31211 cls : 'btn btn-default',
31212 html : '<i class="fa fa-trash"></i>'
31218 cls : 'btn-group roo-upload-cropbox-rotate-right',
31219 action : 'rotate-right',
31223 cls : 'btn btn-default',
31224 html : '<i class="fa fa-repeat"></i>'
31232 cls : 'btn-group roo-upload-cropbox-rotate-left',
31233 action : 'rotate-left',
31237 cls : 'btn btn-default',
31238 html : '<i class="fa fa-undo"></i>'
31244 cls : 'btn-group roo-upload-cropbox-rotate-right',
31245 action : 'rotate-right',
31249 cls : 'btn btn-default',
31250 html : '<i class="fa fa-repeat"></i>'
31263 * @class Roo.bootstrap.DocumentManager
31264 * @extends Roo.bootstrap.Component
31265 * Bootstrap DocumentManager class
31266 * @cfg {String} paramName default 'imageUpload'
31267 * @cfg {String} toolTipName default 'filename'
31268 * @cfg {String} method default POST
31269 * @cfg {String} url action url
31270 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31271 * @cfg {Boolean} multiple multiple upload default true
31272 * @cfg {Number} thumbSize default 300
31273 * @cfg {String} fieldLabel
31274 * @cfg {Number} labelWidth default 4
31275 * @cfg {String} labelAlign (left|top) default left
31276 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31277 * @cfg {Number} labellg set the width of label (1-12)
31278 * @cfg {Number} labelmd set the width of label (1-12)
31279 * @cfg {Number} labelsm set the width of label (1-12)
31280 * @cfg {Number} labelxs set the width of label (1-12)
31283 * Create a new DocumentManager
31284 * @param {Object} config The config object
31287 Roo.bootstrap.DocumentManager = function(config){
31288 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31291 this.delegates = [];
31296 * Fire when initial the DocumentManager
31297 * @param {Roo.bootstrap.DocumentManager} this
31302 * inspect selected file
31303 * @param {Roo.bootstrap.DocumentManager} this
31304 * @param {File} file
31309 * Fire when xhr load exception
31310 * @param {Roo.bootstrap.DocumentManager} this
31311 * @param {XMLHttpRequest} xhr
31313 "exception" : true,
31315 * @event afterupload
31316 * Fire when xhr load exception
31317 * @param {Roo.bootstrap.DocumentManager} this
31318 * @param {XMLHttpRequest} xhr
31320 "afterupload" : true,
31323 * prepare the form data
31324 * @param {Roo.bootstrap.DocumentManager} this
31325 * @param {Object} formData
31330 * Fire when remove the file
31331 * @param {Roo.bootstrap.DocumentManager} this
31332 * @param {Object} file
31337 * Fire after refresh the file
31338 * @param {Roo.bootstrap.DocumentManager} this
31343 * Fire after click the image
31344 * @param {Roo.bootstrap.DocumentManager} this
31345 * @param {Object} file
31350 * Fire when upload a image and editable set to true
31351 * @param {Roo.bootstrap.DocumentManager} this
31352 * @param {Object} file
31356 * @event beforeselectfile
31357 * Fire before select file
31358 * @param {Roo.bootstrap.DocumentManager} this
31360 "beforeselectfile" : true,
31363 * Fire before process file
31364 * @param {Roo.bootstrap.DocumentManager} this
31365 * @param {Object} file
31369 * @event previewrendered
31370 * Fire when preview rendered
31371 * @param {Roo.bootstrap.DocumentManager} this
31372 * @param {Object} file
31374 "previewrendered" : true,
31377 "previewResize" : true
31382 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31391 paramName : 'imageUpload',
31392 toolTipName : 'filename',
31395 labelAlign : 'left',
31405 getAutoCreate : function()
31407 var managerWidget = {
31409 cls : 'roo-document-manager',
31413 cls : 'roo-document-manager-selector',
31418 cls : 'roo-document-manager-uploader',
31422 cls : 'roo-document-manager-upload-btn',
31423 html : '<i class="fa fa-plus"></i>'
31434 cls : 'column col-md-12',
31439 if(this.fieldLabel.length){
31444 cls : 'column col-md-12',
31445 html : this.fieldLabel
31449 cls : 'column col-md-12',
31454 if(this.labelAlign == 'left'){
31459 html : this.fieldLabel
31468 if(this.labelWidth > 12){
31469 content[0].style = "width: " + this.labelWidth + 'px';
31472 if(this.labelWidth < 13 && this.labelmd == 0){
31473 this.labelmd = this.labelWidth;
31476 if(this.labellg > 0){
31477 content[0].cls += ' col-lg-' + this.labellg;
31478 content[1].cls += ' col-lg-' + (12 - this.labellg);
31481 if(this.labelmd > 0){
31482 content[0].cls += ' col-md-' + this.labelmd;
31483 content[1].cls += ' col-md-' + (12 - this.labelmd);
31486 if(this.labelsm > 0){
31487 content[0].cls += ' col-sm-' + this.labelsm;
31488 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31491 if(this.labelxs > 0){
31492 content[0].cls += ' col-xs-' + this.labelxs;
31493 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31501 cls : 'row clearfix',
31509 initEvents : function()
31511 this.managerEl = this.el.select('.roo-document-manager', true).first();
31512 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31514 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31515 this.selectorEl.hide();
31518 this.selectorEl.attr('multiple', 'multiple');
31521 this.selectorEl.on('change', this.onFileSelected, this);
31523 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31524 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31526 this.uploader.on('click', this.onUploaderClick, this);
31528 this.renderProgressDialog();
31532 window.addEventListener("resize", function() { _this.refresh(); } );
31534 this.fireEvent('initial', this);
31537 renderProgressDialog : function()
31541 this.progressDialog = new Roo.bootstrap.Modal({
31542 cls : 'roo-document-manager-progress-dialog',
31543 allow_close : false,
31554 btnclick : function() {
31555 _this.uploadCancel();
31561 this.progressDialog.render(Roo.get(document.body));
31563 this.progress = new Roo.bootstrap.Progress({
31564 cls : 'roo-document-manager-progress',
31569 this.progress.render(this.progressDialog.getChildContainer());
31571 this.progressBar = new Roo.bootstrap.ProgressBar({
31572 cls : 'roo-document-manager-progress-bar',
31575 aria_valuemax : 12,
31579 this.progressBar.render(this.progress.getChildContainer());
31582 onUploaderClick : function(e)
31584 e.preventDefault();
31586 if(this.fireEvent('beforeselectfile', this) != false){
31587 this.selectorEl.dom.click();
31592 onFileSelected : function(e)
31594 e.preventDefault();
31596 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31600 Roo.each(this.selectorEl.dom.files, function(file){
31601 if(this.fireEvent('inspect', this, file) != false){
31602 this.files.push(file);
31612 this.selectorEl.dom.value = '';
31614 if(!this.files || !this.files.length){
31618 if(this.boxes > 0 && this.files.length > this.boxes){
31619 this.files = this.files.slice(0, this.boxes);
31622 this.uploader.show();
31624 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31625 this.uploader.hide();
31634 Roo.each(this.files, function(file){
31636 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31637 var f = this.renderPreview(file);
31642 if(file.type.indexOf('image') != -1){
31643 this.delegates.push(
31645 _this.process(file);
31646 }).createDelegate(this)
31654 _this.process(file);
31655 }).createDelegate(this)
31660 this.files = files;
31662 this.delegates = this.delegates.concat(docs);
31664 if(!this.delegates.length){
31669 this.progressBar.aria_valuemax = this.delegates.length;
31676 arrange : function()
31678 if(!this.delegates.length){
31679 this.progressDialog.hide();
31684 var delegate = this.delegates.shift();
31686 this.progressDialog.show();
31688 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31690 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31695 refresh : function()
31697 this.uploader.show();
31699 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31700 this.uploader.hide();
31703 Roo.isTouch ? this.closable(false) : this.closable(true);
31705 this.fireEvent('refresh', this);
31708 onRemove : function(e, el, o)
31710 e.preventDefault();
31712 this.fireEvent('remove', this, o);
31716 remove : function(o)
31720 Roo.each(this.files, function(file){
31721 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31730 this.files = files;
31737 Roo.each(this.files, function(file){
31742 file.target.remove();
31751 onClick : function(e, el, o)
31753 e.preventDefault();
31755 this.fireEvent('click', this, o);
31759 closable : function(closable)
31761 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31763 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31775 xhrOnLoad : function(xhr)
31777 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31781 if (xhr.readyState !== 4) {
31783 this.fireEvent('exception', this, xhr);
31787 var response = Roo.decode(xhr.responseText);
31789 if(!response.success){
31791 this.fireEvent('exception', this, xhr);
31795 var file = this.renderPreview(response.data);
31797 this.files.push(file);
31801 this.fireEvent('afterupload', this, xhr);
31805 xhrOnError : function(xhr)
31807 Roo.log('xhr on error');
31809 var response = Roo.decode(xhr.responseText);
31816 process : function(file)
31818 if(this.fireEvent('process', this, file) !== false){
31819 if(this.editable && file.type.indexOf('image') != -1){
31820 this.fireEvent('edit', this, file);
31824 this.uploadStart(file, false);
31831 uploadStart : function(file, crop)
31833 this.xhr = new XMLHttpRequest();
31835 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31840 file.xhr = this.xhr;
31842 this.managerEl.createChild({
31844 cls : 'roo-document-manager-loading',
31848 tooltip : file.name,
31849 cls : 'roo-document-manager-thumb',
31850 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31856 this.xhr.open(this.method, this.url, true);
31859 "Accept": "application/json",
31860 "Cache-Control": "no-cache",
31861 "X-Requested-With": "XMLHttpRequest"
31864 for (var headerName in headers) {
31865 var headerValue = headers[headerName];
31867 this.xhr.setRequestHeader(headerName, headerValue);
31873 this.xhr.onload = function()
31875 _this.xhrOnLoad(_this.xhr);
31878 this.xhr.onerror = function()
31880 _this.xhrOnError(_this.xhr);
31883 var formData = new FormData();
31885 formData.append('returnHTML', 'NO');
31888 formData.append('crop', crop);
31891 formData.append(this.paramName, file, file.name);
31898 if(this.fireEvent('prepare', this, formData, options) != false){
31900 if(options.manually){
31904 this.xhr.send(formData);
31908 this.uploadCancel();
31911 uploadCancel : function()
31917 this.delegates = [];
31919 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31926 renderPreview : function(file)
31928 if(typeof(file.target) != 'undefined' && file.target){
31932 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31934 var previewEl = this.managerEl.createChild({
31936 cls : 'roo-document-manager-preview',
31940 tooltip : file[this.toolTipName],
31941 cls : 'roo-document-manager-thumb',
31942 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31947 html : '<i class="fa fa-times-circle"></i>'
31952 var close = previewEl.select('button.close', true).first();
31954 close.on('click', this.onRemove, this, file);
31956 file.target = previewEl;
31958 var image = previewEl.select('img', true).first();
31962 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31964 image.on('click', this.onClick, this, file);
31966 this.fireEvent('previewrendered', this, file);
31972 onPreviewLoad : function(file, image)
31974 if(typeof(file.target) == 'undefined' || !file.target){
31978 var width = image.dom.naturalWidth || image.dom.width;
31979 var height = image.dom.naturalHeight || image.dom.height;
31981 if(!this.previewResize) {
31985 if(width > height){
31986 file.target.addClass('wide');
31990 file.target.addClass('tall');
31995 uploadFromSource : function(file, crop)
31997 this.xhr = new XMLHttpRequest();
31999 this.managerEl.createChild({
32001 cls : 'roo-document-manager-loading',
32005 tooltip : file.name,
32006 cls : 'roo-document-manager-thumb',
32007 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32013 this.xhr.open(this.method, this.url, true);
32016 "Accept": "application/json",
32017 "Cache-Control": "no-cache",
32018 "X-Requested-With": "XMLHttpRequest"
32021 for (var headerName in headers) {
32022 var headerValue = headers[headerName];
32024 this.xhr.setRequestHeader(headerName, headerValue);
32030 this.xhr.onload = function()
32032 _this.xhrOnLoad(_this.xhr);
32035 this.xhr.onerror = function()
32037 _this.xhrOnError(_this.xhr);
32040 var formData = new FormData();
32042 formData.append('returnHTML', 'NO');
32044 formData.append('crop', crop);
32046 if(typeof(file.filename) != 'undefined'){
32047 formData.append('filename', file.filename);
32050 if(typeof(file.mimetype) != 'undefined'){
32051 formData.append('mimetype', file.mimetype);
32056 if(this.fireEvent('prepare', this, formData) != false){
32057 this.xhr.send(formData);
32067 * @class Roo.bootstrap.DocumentViewer
32068 * @extends Roo.bootstrap.Component
32069 * Bootstrap DocumentViewer class
32070 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32071 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32074 * Create a new DocumentViewer
32075 * @param {Object} config The config object
32078 Roo.bootstrap.DocumentViewer = function(config){
32079 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32084 * Fire after initEvent
32085 * @param {Roo.bootstrap.DocumentViewer} this
32091 * @param {Roo.bootstrap.DocumentViewer} this
32096 * Fire after download button
32097 * @param {Roo.bootstrap.DocumentViewer} this
32102 * Fire after trash button
32103 * @param {Roo.bootstrap.DocumentViewer} this
32110 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32112 showDownload : true,
32116 getAutoCreate : function()
32120 cls : 'roo-document-viewer',
32124 cls : 'roo-document-viewer-body',
32128 cls : 'roo-document-viewer-thumb',
32132 cls : 'roo-document-viewer-image'
32140 cls : 'roo-document-viewer-footer',
32143 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32147 cls : 'btn-group roo-document-viewer-download',
32151 cls : 'btn btn-default',
32152 html : '<i class="fa fa-download"></i>'
32158 cls : 'btn-group roo-document-viewer-trash',
32162 cls : 'btn btn-default',
32163 html : '<i class="fa fa-trash"></i>'
32176 initEvents : function()
32178 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32179 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32181 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32182 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32184 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32185 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32187 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32188 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32190 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32191 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32193 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32194 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32196 this.bodyEl.on('click', this.onClick, this);
32197 this.downloadBtn.on('click', this.onDownload, this);
32198 this.trashBtn.on('click', this.onTrash, this);
32200 this.downloadBtn.hide();
32201 this.trashBtn.hide();
32203 if(this.showDownload){
32204 this.downloadBtn.show();
32207 if(this.showTrash){
32208 this.trashBtn.show();
32211 if(!this.showDownload && !this.showTrash) {
32212 this.footerEl.hide();
32217 initial : function()
32219 this.fireEvent('initial', this);
32223 onClick : function(e)
32225 e.preventDefault();
32227 this.fireEvent('click', this);
32230 onDownload : function(e)
32232 e.preventDefault();
32234 this.fireEvent('download', this);
32237 onTrash : function(e)
32239 e.preventDefault();
32241 this.fireEvent('trash', this);
32253 * @class Roo.bootstrap.NavProgressBar
32254 * @extends Roo.bootstrap.Component
32255 * Bootstrap NavProgressBar class
32258 * Create a new nav progress bar
32259 * @param {Object} config The config object
32262 Roo.bootstrap.NavProgressBar = function(config){
32263 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32265 this.bullets = this.bullets || [];
32267 // Roo.bootstrap.NavProgressBar.register(this);
32271 * Fires when the active item changes
32272 * @param {Roo.bootstrap.NavProgressBar} this
32273 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32274 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32281 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32286 getAutoCreate : function()
32288 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32292 cls : 'roo-navigation-bar-group',
32296 cls : 'roo-navigation-top-bar'
32300 cls : 'roo-navigation-bullets-bar',
32304 cls : 'roo-navigation-bar'
32311 cls : 'roo-navigation-bottom-bar'
32321 initEvents: function()
32326 onRender : function(ct, position)
32328 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32330 if(this.bullets.length){
32331 Roo.each(this.bullets, function(b){
32340 addItem : function(cfg)
32342 var item = new Roo.bootstrap.NavProgressItem(cfg);
32344 item.parentId = this.id;
32345 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32348 var top = new Roo.bootstrap.Element({
32350 cls : 'roo-navigation-bar-text'
32353 var bottom = new Roo.bootstrap.Element({
32355 cls : 'roo-navigation-bar-text'
32358 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32359 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32361 var topText = new Roo.bootstrap.Element({
32363 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32366 var bottomText = new Roo.bootstrap.Element({
32368 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32371 topText.onRender(top.el, null);
32372 bottomText.onRender(bottom.el, null);
32375 item.bottomEl = bottom;
32378 this.barItems.push(item);
32383 getActive : function()
32385 var active = false;
32387 Roo.each(this.barItems, function(v){
32389 if (!v.isActive()) {
32401 setActiveItem : function(item)
32405 Roo.each(this.barItems, function(v){
32406 if (v.rid == item.rid) {
32410 if (v.isActive()) {
32411 v.setActive(false);
32416 item.setActive(true);
32418 this.fireEvent('changed', this, item, prev);
32421 getBarItem: function(rid)
32425 Roo.each(this.barItems, function(e) {
32426 if (e.rid != rid) {
32437 indexOfItem : function(item)
32441 Roo.each(this.barItems, function(v, i){
32443 if (v.rid != item.rid) {
32454 setActiveNext : function()
32456 var i = this.indexOfItem(this.getActive());
32458 if (i > this.barItems.length) {
32462 this.setActiveItem(this.barItems[i+1]);
32465 setActivePrev : function()
32467 var i = this.indexOfItem(this.getActive());
32473 this.setActiveItem(this.barItems[i-1]);
32476 format : function()
32478 if(!this.barItems.length){
32482 var width = 100 / this.barItems.length;
32484 Roo.each(this.barItems, function(i){
32485 i.el.setStyle('width', width + '%');
32486 i.topEl.el.setStyle('width', width + '%');
32487 i.bottomEl.el.setStyle('width', width + '%');
32496 * Nav Progress Item
32501 * @class Roo.bootstrap.NavProgressItem
32502 * @extends Roo.bootstrap.Component
32503 * Bootstrap NavProgressItem class
32504 * @cfg {String} rid the reference id
32505 * @cfg {Boolean} active (true|false) Is item active default false
32506 * @cfg {Boolean} disabled (true|false) Is item active default false
32507 * @cfg {String} html
32508 * @cfg {String} position (top|bottom) text position default bottom
32509 * @cfg {String} icon show icon instead of number
32512 * Create a new NavProgressItem
32513 * @param {Object} config The config object
32515 Roo.bootstrap.NavProgressItem = function(config){
32516 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32521 * The raw click event for the entire grid.
32522 * @param {Roo.bootstrap.NavProgressItem} this
32523 * @param {Roo.EventObject} e
32530 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32536 position : 'bottom',
32539 getAutoCreate : function()
32541 var iconCls = 'roo-navigation-bar-item-icon';
32543 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32547 cls: 'roo-navigation-bar-item',
32557 cfg.cls += ' active';
32560 cfg.cls += ' disabled';
32566 disable : function()
32568 this.setDisabled(true);
32571 enable : function()
32573 this.setDisabled(false);
32576 initEvents: function()
32578 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32580 this.iconEl.on('click', this.onClick, this);
32583 onClick : function(e)
32585 e.preventDefault();
32591 if(this.fireEvent('click', this, e) === false){
32595 this.parent().setActiveItem(this);
32598 isActive: function ()
32600 return this.active;
32603 setActive : function(state)
32605 if(this.active == state){
32609 this.active = state;
32612 this.el.addClass('active');
32616 this.el.removeClass('active');
32621 setDisabled : function(state)
32623 if(this.disabled == state){
32627 this.disabled = state;
32630 this.el.addClass('disabled');
32634 this.el.removeClass('disabled');
32637 tooltipEl : function()
32639 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32652 * @class Roo.bootstrap.FieldLabel
32653 * @extends Roo.bootstrap.Component
32654 * Bootstrap FieldLabel class
32655 * @cfg {String} html contents of the element
32656 * @cfg {String} tag tag of the element default label
32657 * @cfg {String} cls class of the element
32658 * @cfg {String} target label target
32659 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32660 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32661 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32662 * @cfg {String} iconTooltip default "This field is required"
32663 * @cfg {String} indicatorpos (left|right) default left
32666 * Create a new FieldLabel
32667 * @param {Object} config The config object
32670 Roo.bootstrap.FieldLabel = function(config){
32671 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32676 * Fires after the field has been marked as invalid.
32677 * @param {Roo.form.FieldLabel} this
32678 * @param {String} msg The validation message
32683 * Fires after the field has been validated with no errors.
32684 * @param {Roo.form.FieldLabel} this
32690 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32697 invalidClass : 'has-warning',
32698 validClass : 'has-success',
32699 iconTooltip : 'This field is required',
32700 indicatorpos : 'left',
32702 getAutoCreate : function(){
32705 if (!this.allowBlank) {
32711 cls : 'roo-bootstrap-field-label ' + this.cls,
32716 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32717 tooltip : this.iconTooltip
32726 if(this.indicatorpos == 'right'){
32729 cls : 'roo-bootstrap-field-label ' + this.cls,
32738 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32739 tooltip : this.iconTooltip
32748 initEvents: function()
32750 Roo.bootstrap.Element.superclass.initEvents.call(this);
32752 this.indicator = this.indicatorEl();
32754 if(this.indicator){
32755 this.indicator.removeClass('visible');
32756 this.indicator.addClass('invisible');
32759 Roo.bootstrap.FieldLabel.register(this);
32762 indicatorEl : function()
32764 var indicator = this.el.select('i.roo-required-indicator',true).first();
32775 * Mark this field as valid
32777 markValid : function()
32779 if(this.indicator){
32780 this.indicator.removeClass('visible');
32781 this.indicator.addClass('invisible');
32783 if (Roo.bootstrap.version == 3) {
32784 this.el.removeClass(this.invalidClass);
32785 this.el.addClass(this.validClass);
32787 this.el.removeClass('is-invalid');
32788 this.el.addClass('is-valid');
32792 this.fireEvent('valid', this);
32796 * Mark this field as invalid
32797 * @param {String} msg The validation message
32799 markInvalid : function(msg)
32801 if(this.indicator){
32802 this.indicator.removeClass('invisible');
32803 this.indicator.addClass('visible');
32805 if (Roo.bootstrap.version == 3) {
32806 this.el.removeClass(this.validClass);
32807 this.el.addClass(this.invalidClass);
32809 this.el.removeClass('is-valid');
32810 this.el.addClass('is-invalid');
32814 this.fireEvent('invalid', this, msg);
32820 Roo.apply(Roo.bootstrap.FieldLabel, {
32825 * register a FieldLabel Group
32826 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32828 register : function(label)
32830 if(this.groups.hasOwnProperty(label.target)){
32834 this.groups[label.target] = label;
32838 * fetch a FieldLabel Group based on the target
32839 * @param {string} target
32840 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32842 get: function(target) {
32843 if (typeof(this.groups[target]) == 'undefined') {
32847 return this.groups[target] ;
32856 * page DateSplitField.
32862 * @class Roo.bootstrap.DateSplitField
32863 * @extends Roo.bootstrap.Component
32864 * Bootstrap DateSplitField class
32865 * @cfg {string} fieldLabel - the label associated
32866 * @cfg {Number} labelWidth set the width of label (0-12)
32867 * @cfg {String} labelAlign (top|left)
32868 * @cfg {Boolean} dayAllowBlank (true|false) default false
32869 * @cfg {Boolean} monthAllowBlank (true|false) default false
32870 * @cfg {Boolean} yearAllowBlank (true|false) default false
32871 * @cfg {string} dayPlaceholder
32872 * @cfg {string} monthPlaceholder
32873 * @cfg {string} yearPlaceholder
32874 * @cfg {string} dayFormat default 'd'
32875 * @cfg {string} monthFormat default 'm'
32876 * @cfg {string} yearFormat default 'Y'
32877 * @cfg {Number} labellg set the width of label (1-12)
32878 * @cfg {Number} labelmd set the width of label (1-12)
32879 * @cfg {Number} labelsm set the width of label (1-12)
32880 * @cfg {Number} labelxs set the width of label (1-12)
32884 * Create a new DateSplitField
32885 * @param {Object} config The config object
32888 Roo.bootstrap.DateSplitField = function(config){
32889 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32895 * getting the data of years
32896 * @param {Roo.bootstrap.DateSplitField} this
32897 * @param {Object} years
32902 * getting the data of days
32903 * @param {Roo.bootstrap.DateSplitField} this
32904 * @param {Object} days
32909 * Fires after the field has been marked as invalid.
32910 * @param {Roo.form.Field} this
32911 * @param {String} msg The validation message
32916 * Fires after the field has been validated with no errors.
32917 * @param {Roo.form.Field} this
32923 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32926 labelAlign : 'top',
32928 dayAllowBlank : false,
32929 monthAllowBlank : false,
32930 yearAllowBlank : false,
32931 dayPlaceholder : '',
32932 monthPlaceholder : '',
32933 yearPlaceholder : '',
32937 isFormField : true,
32943 getAutoCreate : function()
32947 cls : 'row roo-date-split-field-group',
32952 cls : 'form-hidden-field roo-date-split-field-group-value',
32958 var labelCls = 'col-md-12';
32959 var contentCls = 'col-md-4';
32961 if(this.fieldLabel){
32965 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32969 html : this.fieldLabel
32974 if(this.labelAlign == 'left'){
32976 if(this.labelWidth > 12){
32977 label.style = "width: " + this.labelWidth + 'px';
32980 if(this.labelWidth < 13 && this.labelmd == 0){
32981 this.labelmd = this.labelWidth;
32984 if(this.labellg > 0){
32985 labelCls = ' col-lg-' + this.labellg;
32986 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32989 if(this.labelmd > 0){
32990 labelCls = ' col-md-' + this.labelmd;
32991 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32994 if(this.labelsm > 0){
32995 labelCls = ' col-sm-' + this.labelsm;
32996 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32999 if(this.labelxs > 0){
33000 labelCls = ' col-xs-' + this.labelxs;
33001 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33005 label.cls += ' ' + labelCls;
33007 cfg.cn.push(label);
33010 Roo.each(['day', 'month', 'year'], function(t){
33013 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33020 inputEl: function ()
33022 return this.el.select('.roo-date-split-field-group-value', true).first();
33025 onRender : function(ct, position)
33029 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33031 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33033 this.dayField = new Roo.bootstrap.ComboBox({
33034 allowBlank : this.dayAllowBlank,
33035 alwaysQuery : true,
33036 displayField : 'value',
33039 forceSelection : true,
33041 placeholder : this.dayPlaceholder,
33042 selectOnFocus : true,
33043 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33044 triggerAction : 'all',
33046 valueField : 'value',
33047 store : new Roo.data.SimpleStore({
33048 data : (function() {
33050 _this.fireEvent('days', _this, days);
33053 fields : [ 'value' ]
33056 select : function (_self, record, index)
33058 _this.setValue(_this.getValue());
33063 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33065 this.monthField = new Roo.bootstrap.MonthField({
33066 after : '<i class=\"fa fa-calendar\"></i>',
33067 allowBlank : this.monthAllowBlank,
33068 placeholder : this.monthPlaceholder,
33071 render : function (_self)
33073 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33074 e.preventDefault();
33078 select : function (_self, oldvalue, newvalue)
33080 _this.setValue(_this.getValue());
33085 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33087 this.yearField = new Roo.bootstrap.ComboBox({
33088 allowBlank : this.yearAllowBlank,
33089 alwaysQuery : true,
33090 displayField : 'value',
33093 forceSelection : true,
33095 placeholder : this.yearPlaceholder,
33096 selectOnFocus : true,
33097 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33098 triggerAction : 'all',
33100 valueField : 'value',
33101 store : new Roo.data.SimpleStore({
33102 data : (function() {
33104 _this.fireEvent('years', _this, years);
33107 fields : [ 'value' ]
33110 select : function (_self, record, index)
33112 _this.setValue(_this.getValue());
33117 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33120 setValue : function(v, format)
33122 this.inputEl.dom.value = v;
33124 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33126 var d = Date.parseDate(v, f);
33133 this.setDay(d.format(this.dayFormat));
33134 this.setMonth(d.format(this.monthFormat));
33135 this.setYear(d.format(this.yearFormat));
33142 setDay : function(v)
33144 this.dayField.setValue(v);
33145 this.inputEl.dom.value = this.getValue();
33150 setMonth : function(v)
33152 this.monthField.setValue(v, true);
33153 this.inputEl.dom.value = this.getValue();
33158 setYear : function(v)
33160 this.yearField.setValue(v);
33161 this.inputEl.dom.value = this.getValue();
33166 getDay : function()
33168 return this.dayField.getValue();
33171 getMonth : function()
33173 return this.monthField.getValue();
33176 getYear : function()
33178 return this.yearField.getValue();
33181 getValue : function()
33183 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33185 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33195 this.inputEl.dom.value = '';
33200 validate : function()
33202 var d = this.dayField.validate();
33203 var m = this.monthField.validate();
33204 var y = this.yearField.validate();
33209 (!this.dayAllowBlank && !d) ||
33210 (!this.monthAllowBlank && !m) ||
33211 (!this.yearAllowBlank && !y)
33216 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33225 this.markInvalid();
33230 markValid : function()
33233 var label = this.el.select('label', true).first();
33234 var icon = this.el.select('i.fa-star', true).first();
33240 this.fireEvent('valid', this);
33244 * Mark this field as invalid
33245 * @param {String} msg The validation message
33247 markInvalid : function(msg)
33250 var label = this.el.select('label', true).first();
33251 var icon = this.el.select('i.fa-star', true).first();
33253 if(label && !icon){
33254 this.el.select('.roo-date-split-field-label', true).createChild({
33256 cls : 'text-danger fa fa-lg fa-star',
33257 tooltip : 'This field is required',
33258 style : 'margin-right:5px;'
33262 this.fireEvent('invalid', this, msg);
33265 clearInvalid : function()
33267 var label = this.el.select('label', true).first();
33268 var icon = this.el.select('i.fa-star', true).first();
33274 this.fireEvent('valid', this);
33277 getName: function()
33287 * http://masonry.desandro.com
33289 * The idea is to render all the bricks based on vertical width...
33291 * The original code extends 'outlayer' - we might need to use that....
33297 * @class Roo.bootstrap.LayoutMasonry
33298 * @extends Roo.bootstrap.Component
33299 * Bootstrap Layout Masonry class
33302 * Create a new Element
33303 * @param {Object} config The config object
33306 Roo.bootstrap.LayoutMasonry = function(config){
33308 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33312 Roo.bootstrap.LayoutMasonry.register(this);
33318 * Fire after layout the items
33319 * @param {Roo.bootstrap.LayoutMasonry} this
33320 * @param {Roo.EventObject} e
33327 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33330 * @cfg {Boolean} isLayoutInstant = no animation?
33332 isLayoutInstant : false, // needed?
33335 * @cfg {Number} boxWidth width of the columns
33340 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33345 * @cfg {Number} padWidth padding below box..
33350 * @cfg {Number} gutter gutter width..
33355 * @cfg {Number} maxCols maximum number of columns
33361 * @cfg {Boolean} isAutoInitial defalut true
33363 isAutoInitial : true,
33368 * @cfg {Boolean} isHorizontal defalut false
33370 isHorizontal : false,
33372 currentSize : null,
33378 bricks: null, //CompositeElement
33382 _isLayoutInited : false,
33384 // isAlternative : false, // only use for vertical layout...
33387 * @cfg {Number} alternativePadWidth padding below box..
33389 alternativePadWidth : 50,
33391 selectedBrick : [],
33393 getAutoCreate : function(){
33395 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33399 cls: 'blog-masonary-wrapper ' + this.cls,
33401 cls : 'mas-boxes masonary'
33408 getChildContainer: function( )
33410 if (this.boxesEl) {
33411 return this.boxesEl;
33414 this.boxesEl = this.el.select('.mas-boxes').first();
33416 return this.boxesEl;
33420 initEvents : function()
33424 if(this.isAutoInitial){
33425 Roo.log('hook children rendered');
33426 this.on('childrenrendered', function() {
33427 Roo.log('children rendered');
33433 initial : function()
33435 this.selectedBrick = [];
33437 this.currentSize = this.el.getBox(true);
33439 Roo.EventManager.onWindowResize(this.resize, this);
33441 if(!this.isAutoInitial){
33449 //this.layout.defer(500,this);
33453 resize : function()
33455 var cs = this.el.getBox(true);
33458 this.currentSize.width == cs.width &&
33459 this.currentSize.x == cs.x &&
33460 this.currentSize.height == cs.height &&
33461 this.currentSize.y == cs.y
33463 Roo.log("no change in with or X or Y");
33467 this.currentSize = cs;
33473 layout : function()
33475 this._resetLayout();
33477 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33479 this.layoutItems( isInstant );
33481 this._isLayoutInited = true;
33483 this.fireEvent('layout', this);
33487 _resetLayout : function()
33489 if(this.isHorizontal){
33490 this.horizontalMeasureColumns();
33494 this.verticalMeasureColumns();
33498 verticalMeasureColumns : function()
33500 this.getContainerWidth();
33502 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33503 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33507 var boxWidth = this.boxWidth + this.padWidth;
33509 if(this.containerWidth < this.boxWidth){
33510 boxWidth = this.containerWidth
33513 var containerWidth = this.containerWidth;
33515 var cols = Math.floor(containerWidth / boxWidth);
33517 this.cols = Math.max( cols, 1 );
33519 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33521 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33523 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33525 this.colWidth = boxWidth + avail - this.padWidth;
33527 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33528 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33531 horizontalMeasureColumns : function()
33533 this.getContainerWidth();
33535 var boxWidth = this.boxWidth;
33537 if(this.containerWidth < boxWidth){
33538 boxWidth = this.containerWidth;
33541 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33543 this.el.setHeight(boxWidth);
33547 getContainerWidth : function()
33549 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33552 layoutItems : function( isInstant )
33554 Roo.log(this.bricks);
33556 var items = Roo.apply([], this.bricks);
33558 if(this.isHorizontal){
33559 this._horizontalLayoutItems( items , isInstant );
33563 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33564 // this._verticalAlternativeLayoutItems( items , isInstant );
33568 this._verticalLayoutItems( items , isInstant );
33572 _verticalLayoutItems : function ( items , isInstant)
33574 if ( !items || !items.length ) {
33579 ['xs', 'xs', 'xs', 'tall'],
33580 ['xs', 'xs', 'tall'],
33581 ['xs', 'xs', 'sm'],
33582 ['xs', 'xs', 'xs'],
33588 ['sm', 'xs', 'xs'],
33592 ['tall', 'xs', 'xs', 'xs'],
33593 ['tall', 'xs', 'xs'],
33605 Roo.each(items, function(item, k){
33607 switch (item.size) {
33608 // these layouts take up a full box,
33619 boxes.push([item]);
33642 var filterPattern = function(box, length)
33650 var pattern = box.slice(0, length);
33654 Roo.each(pattern, function(i){
33655 format.push(i.size);
33658 Roo.each(standard, function(s){
33660 if(String(s) != String(format)){
33669 if(!match && length == 1){
33674 filterPattern(box, length - 1);
33678 queue.push(pattern);
33680 box = box.slice(length, box.length);
33682 filterPattern(box, 4);
33688 Roo.each(boxes, function(box, k){
33694 if(box.length == 1){
33699 filterPattern(box, 4);
33703 this._processVerticalLayoutQueue( queue, isInstant );
33707 // _verticalAlternativeLayoutItems : function( items , isInstant )
33709 // if ( !items || !items.length ) {
33713 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33717 _horizontalLayoutItems : function ( items , isInstant)
33719 if ( !items || !items.length || items.length < 3) {
33725 var eItems = items.slice(0, 3);
33727 items = items.slice(3, items.length);
33730 ['xs', 'xs', 'xs', 'wide'],
33731 ['xs', 'xs', 'wide'],
33732 ['xs', 'xs', 'sm'],
33733 ['xs', 'xs', 'xs'],
33739 ['sm', 'xs', 'xs'],
33743 ['wide', 'xs', 'xs', 'xs'],
33744 ['wide', 'xs', 'xs'],
33757 Roo.each(items, function(item, k){
33759 switch (item.size) {
33770 boxes.push([item]);
33794 var filterPattern = function(box, length)
33802 var pattern = box.slice(0, length);
33806 Roo.each(pattern, function(i){
33807 format.push(i.size);
33810 Roo.each(standard, function(s){
33812 if(String(s) != String(format)){
33821 if(!match && length == 1){
33826 filterPattern(box, length - 1);
33830 queue.push(pattern);
33832 box = box.slice(length, box.length);
33834 filterPattern(box, 4);
33840 Roo.each(boxes, function(box, k){
33846 if(box.length == 1){
33851 filterPattern(box, 4);
33858 var pos = this.el.getBox(true);
33862 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33864 var hit_end = false;
33866 Roo.each(queue, function(box){
33870 Roo.each(box, function(b){
33872 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33882 Roo.each(box, function(b){
33884 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33887 mx = Math.max(mx, b.x);
33891 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33895 Roo.each(box, function(b){
33897 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33911 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33914 /** Sets position of item in DOM
33915 * @param {Element} item
33916 * @param {Number} x - horizontal position
33917 * @param {Number} y - vertical position
33918 * @param {Boolean} isInstant - disables transitions
33920 _processVerticalLayoutQueue : function( queue, isInstant )
33922 var pos = this.el.getBox(true);
33927 for (var i = 0; i < this.cols; i++){
33931 Roo.each(queue, function(box, k){
33933 var col = k % this.cols;
33935 Roo.each(box, function(b,kk){
33937 b.el.position('absolute');
33939 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33940 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33942 if(b.size == 'md-left' || b.size == 'md-right'){
33943 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33944 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33947 b.el.setWidth(width);
33948 b.el.setHeight(height);
33950 b.el.select('iframe',true).setSize(width,height);
33954 for (var i = 0; i < this.cols; i++){
33956 if(maxY[i] < maxY[col]){
33961 col = Math.min(col, i);
33965 x = pos.x + col * (this.colWidth + this.padWidth);
33969 var positions = [];
33971 switch (box.length){
33973 positions = this.getVerticalOneBoxColPositions(x, y, box);
33976 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33979 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33982 positions = this.getVerticalFourBoxColPositions(x, y, box);
33988 Roo.each(box, function(b,kk){
33990 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33992 var sz = b.el.getSize();
33994 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34002 for (var i = 0; i < this.cols; i++){
34003 mY = Math.max(mY, maxY[i]);
34006 this.el.setHeight(mY - pos.y);
34010 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34012 // var pos = this.el.getBox(true);
34015 // var maxX = pos.right;
34017 // var maxHeight = 0;
34019 // Roo.each(items, function(item, k){
34023 // item.el.position('absolute');
34025 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34027 // item.el.setWidth(width);
34029 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34031 // item.el.setHeight(height);
34034 // item.el.setXY([x, y], isInstant ? false : true);
34036 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34039 // y = y + height + this.alternativePadWidth;
34041 // maxHeight = maxHeight + height + this.alternativePadWidth;
34045 // this.el.setHeight(maxHeight);
34049 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34051 var pos = this.el.getBox(true);
34056 var maxX = pos.right;
34058 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34060 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34062 Roo.each(queue, function(box, k){
34064 Roo.each(box, function(b, kk){
34066 b.el.position('absolute');
34068 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34069 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34071 if(b.size == 'md-left' || b.size == 'md-right'){
34072 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34073 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34076 b.el.setWidth(width);
34077 b.el.setHeight(height);
34085 var positions = [];
34087 switch (box.length){
34089 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34092 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34095 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34098 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34104 Roo.each(box, function(b,kk){
34106 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34108 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34116 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34118 Roo.each(eItems, function(b,k){
34120 b.size = (k == 0) ? 'sm' : 'xs';
34121 b.x = (k == 0) ? 2 : 1;
34122 b.y = (k == 0) ? 2 : 1;
34124 b.el.position('absolute');
34126 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34128 b.el.setWidth(width);
34130 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34132 b.el.setHeight(height);
34136 var positions = [];
34139 x : maxX - this.unitWidth * 2 - this.gutter,
34144 x : maxX - this.unitWidth,
34145 y : minY + (this.unitWidth + this.gutter) * 2
34149 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34153 Roo.each(eItems, function(b,k){
34155 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34161 getVerticalOneBoxColPositions : function(x, y, box)
34165 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34167 if(box[0].size == 'md-left'){
34171 if(box[0].size == 'md-right'){
34176 x : x + (this.unitWidth + this.gutter) * rand,
34183 getVerticalTwoBoxColPositions : function(x, y, box)
34187 if(box[0].size == 'xs'){
34191 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34195 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34209 x : x + (this.unitWidth + this.gutter) * 2,
34210 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34217 getVerticalThreeBoxColPositions : function(x, y, box)
34221 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34229 x : x + (this.unitWidth + this.gutter) * 1,
34234 x : x + (this.unitWidth + this.gutter) * 2,
34242 if(box[0].size == 'xs' && box[1].size == 'xs'){
34251 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34255 x : x + (this.unitWidth + this.gutter) * 1,
34269 x : x + (this.unitWidth + this.gutter) * 2,
34274 x : x + (this.unitWidth + this.gutter) * 2,
34275 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34282 getVerticalFourBoxColPositions : function(x, y, box)
34286 if(box[0].size == 'xs'){
34295 y : y + (this.unitHeight + this.gutter) * 1
34300 y : y + (this.unitHeight + this.gutter) * 2
34304 x : x + (this.unitWidth + this.gutter) * 1,
34318 x : x + (this.unitWidth + this.gutter) * 2,
34323 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34324 y : y + (this.unitHeight + this.gutter) * 1
34328 x : x + (this.unitWidth + this.gutter) * 2,
34329 y : y + (this.unitWidth + this.gutter) * 2
34336 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34340 if(box[0].size == 'md-left'){
34342 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34349 if(box[0].size == 'md-right'){
34351 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34352 y : minY + (this.unitWidth + this.gutter) * 1
34358 var rand = Math.floor(Math.random() * (4 - box[0].y));
34361 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34362 y : minY + (this.unitWidth + this.gutter) * rand
34369 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34373 if(box[0].size == 'xs'){
34376 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34381 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34382 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34390 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34395 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34396 y : minY + (this.unitWidth + this.gutter) * 2
34403 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34407 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34410 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34415 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34416 y : minY + (this.unitWidth + this.gutter) * 1
34420 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34421 y : minY + (this.unitWidth + this.gutter) * 2
34428 if(box[0].size == 'xs' && box[1].size == 'xs'){
34431 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34436 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34441 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34442 y : minY + (this.unitWidth + this.gutter) * 1
34450 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34455 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34456 y : minY + (this.unitWidth + this.gutter) * 2
34460 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34461 y : minY + (this.unitWidth + this.gutter) * 2
34468 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34472 if(box[0].size == 'xs'){
34475 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34480 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34485 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),
34490 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34491 y : minY + (this.unitWidth + this.gutter) * 1
34499 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34504 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34505 y : minY + (this.unitWidth + this.gutter) * 2
34509 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34510 y : minY + (this.unitWidth + this.gutter) * 2
34514 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),
34515 y : minY + (this.unitWidth + this.gutter) * 2
34523 * remove a Masonry Brick
34524 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34526 removeBrick : function(brick_id)
34532 for (var i = 0; i<this.bricks.length; i++) {
34533 if (this.bricks[i].id == brick_id) {
34534 this.bricks.splice(i,1);
34535 this.el.dom.removeChild(Roo.get(brick_id).dom);
34542 * adds a Masonry Brick
34543 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34545 addBrick : function(cfg)
34547 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34548 //this.register(cn);
34549 cn.parentId = this.id;
34550 cn.render(this.el);
34555 * register a Masonry Brick
34556 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34559 register : function(brick)
34561 this.bricks.push(brick);
34562 brick.masonryId = this.id;
34566 * clear all the Masonry Brick
34568 clearAll : function()
34571 //this.getChildContainer().dom.innerHTML = "";
34572 this.el.dom.innerHTML = '';
34575 getSelected : function()
34577 if (!this.selectedBrick) {
34581 return this.selectedBrick;
34585 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34589 * register a Masonry Layout
34590 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34593 register : function(layout)
34595 this.groups[layout.id] = layout;
34598 * fetch a Masonry Layout based on the masonry layout ID
34599 * @param {string} the masonry layout to add
34600 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34603 get: function(layout_id) {
34604 if (typeof(this.groups[layout_id]) == 'undefined') {
34607 return this.groups[layout_id] ;
34619 * http://masonry.desandro.com
34621 * The idea is to render all the bricks based on vertical width...
34623 * The original code extends 'outlayer' - we might need to use that....
34629 * @class Roo.bootstrap.LayoutMasonryAuto
34630 * @extends Roo.bootstrap.Component
34631 * Bootstrap Layout Masonry class
34634 * Create a new Element
34635 * @param {Object} config The config object
34638 Roo.bootstrap.LayoutMasonryAuto = function(config){
34639 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34642 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34645 * @cfg {Boolean} isFitWidth - resize the width..
34647 isFitWidth : false, // options..
34649 * @cfg {Boolean} isOriginLeft = left align?
34651 isOriginLeft : true,
34653 * @cfg {Boolean} isOriginTop = top align?
34655 isOriginTop : false,
34657 * @cfg {Boolean} isLayoutInstant = no animation?
34659 isLayoutInstant : false, // needed?
34661 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34663 isResizingContainer : true,
34665 * @cfg {Number} columnWidth width of the columns
34671 * @cfg {Number} maxCols maximum number of columns
34676 * @cfg {Number} padHeight padding below box..
34682 * @cfg {Boolean} isAutoInitial defalut true
34685 isAutoInitial : true,
34691 initialColumnWidth : 0,
34692 currentSize : null,
34694 colYs : null, // array.
34701 bricks: null, //CompositeElement
34702 cols : 0, // array?
34703 // element : null, // wrapped now this.el
34704 _isLayoutInited : null,
34707 getAutoCreate : function(){
34711 cls: 'blog-masonary-wrapper ' + this.cls,
34713 cls : 'mas-boxes masonary'
34720 getChildContainer: function( )
34722 if (this.boxesEl) {
34723 return this.boxesEl;
34726 this.boxesEl = this.el.select('.mas-boxes').first();
34728 return this.boxesEl;
34732 initEvents : function()
34736 if(this.isAutoInitial){
34737 Roo.log('hook children rendered');
34738 this.on('childrenrendered', function() {
34739 Roo.log('children rendered');
34746 initial : function()
34748 this.reloadItems();
34750 this.currentSize = this.el.getBox(true);
34752 /// was window resize... - let's see if this works..
34753 Roo.EventManager.onWindowResize(this.resize, this);
34755 if(!this.isAutoInitial){
34760 this.layout.defer(500,this);
34763 reloadItems: function()
34765 this.bricks = this.el.select('.masonry-brick', true);
34767 this.bricks.each(function(b) {
34768 //Roo.log(b.getSize());
34769 if (!b.attr('originalwidth')) {
34770 b.attr('originalwidth', b.getSize().width);
34775 Roo.log(this.bricks.elements.length);
34778 resize : function()
34781 var cs = this.el.getBox(true);
34783 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34784 Roo.log("no change in with or X");
34787 this.currentSize = cs;
34791 layout : function()
34794 this._resetLayout();
34795 //this._manageStamps();
34797 // don't animate first layout
34798 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34799 this.layoutItems( isInstant );
34801 // flag for initalized
34802 this._isLayoutInited = true;
34805 layoutItems : function( isInstant )
34807 //var items = this._getItemsForLayout( this.items );
34808 // original code supports filtering layout items.. we just ignore it..
34810 this._layoutItems( this.bricks , isInstant );
34812 this._postLayout();
34814 _layoutItems : function ( items , isInstant)
34816 //this.fireEvent( 'layout', this, items );
34819 if ( !items || !items.elements.length ) {
34820 // no items, emit event with empty array
34825 items.each(function(item) {
34826 Roo.log("layout item");
34828 // get x/y object from method
34829 var position = this._getItemLayoutPosition( item );
34831 position.item = item;
34832 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34833 queue.push( position );
34836 this._processLayoutQueue( queue );
34838 /** Sets position of item in DOM
34839 * @param {Element} item
34840 * @param {Number} x - horizontal position
34841 * @param {Number} y - vertical position
34842 * @param {Boolean} isInstant - disables transitions
34844 _processLayoutQueue : function( queue )
34846 for ( var i=0, len = queue.length; i < len; i++ ) {
34847 var obj = queue[i];
34848 obj.item.position('absolute');
34849 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34855 * Any logic you want to do after each layout,
34856 * i.e. size the container
34858 _postLayout : function()
34860 this.resizeContainer();
34863 resizeContainer : function()
34865 if ( !this.isResizingContainer ) {
34868 var size = this._getContainerSize();
34870 this.el.setSize(size.width,size.height);
34871 this.boxesEl.setSize(size.width,size.height);
34877 _resetLayout : function()
34879 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34880 this.colWidth = this.el.getWidth();
34881 //this.gutter = this.el.getWidth();
34883 this.measureColumns();
34889 this.colYs.push( 0 );
34895 measureColumns : function()
34897 this.getContainerWidth();
34898 // if columnWidth is 0, default to outerWidth of first item
34899 if ( !this.columnWidth ) {
34900 var firstItem = this.bricks.first();
34901 Roo.log(firstItem);
34902 this.columnWidth = this.containerWidth;
34903 if (firstItem && firstItem.attr('originalwidth') ) {
34904 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34906 // columnWidth fall back to item of first element
34907 Roo.log("set column width?");
34908 this.initialColumnWidth = this.columnWidth ;
34910 // if first elem has no width, default to size of container
34915 if (this.initialColumnWidth) {
34916 this.columnWidth = this.initialColumnWidth;
34921 // column width is fixed at the top - however if container width get's smaller we should
34924 // this bit calcs how man columns..
34926 var columnWidth = this.columnWidth += this.gutter;
34928 // calculate columns
34929 var containerWidth = this.containerWidth + this.gutter;
34931 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34932 // fix rounding errors, typically with gutters
34933 var excess = columnWidth - containerWidth % columnWidth;
34936 // if overshoot is less than a pixel, round up, otherwise floor it
34937 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34938 cols = Math[ mathMethod ]( cols );
34939 this.cols = Math.max( cols, 1 );
34940 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34942 // padding positioning..
34943 var totalColWidth = this.cols * this.columnWidth;
34944 var padavail = this.containerWidth - totalColWidth;
34945 // so for 2 columns - we need 3 'pads'
34947 var padNeeded = (1+this.cols) * this.padWidth;
34949 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34951 this.columnWidth += padExtra
34952 //this.padWidth = Math.floor(padavail / ( this.cols));
34954 // adjust colum width so that padding is fixed??
34956 // we have 3 columns ... total = width * 3
34957 // we have X left over... that should be used by
34959 //if (this.expandC) {
34967 getContainerWidth : function()
34969 /* // container is parent if fit width
34970 var container = this.isFitWidth ? this.element.parentNode : this.element;
34971 // check that this.size and size are there
34972 // IE8 triggers resize on body size change, so they might not be
34974 var size = getSize( container ); //FIXME
34975 this.containerWidth = size && size.innerWidth; //FIXME
34978 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34982 _getItemLayoutPosition : function( item ) // what is item?
34984 // we resize the item to our columnWidth..
34986 item.setWidth(this.columnWidth);
34987 item.autoBoxAdjust = false;
34989 var sz = item.getSize();
34991 // how many columns does this brick span
34992 var remainder = this.containerWidth % this.columnWidth;
34994 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34995 // round if off by 1 pixel, otherwise use ceil
34996 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34997 colSpan = Math.min( colSpan, this.cols );
34999 // normally this should be '1' as we dont' currently allow multi width columns..
35001 var colGroup = this._getColGroup( colSpan );
35002 // get the minimum Y value from the columns
35003 var minimumY = Math.min.apply( Math, colGroup );
35004 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35006 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35008 // position the brick
35010 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35011 y: this.currentSize.y + minimumY + this.padHeight
35015 // apply setHeight to necessary columns
35016 var setHeight = minimumY + sz.height + this.padHeight;
35017 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35019 var setSpan = this.cols + 1 - colGroup.length;
35020 for ( var i = 0; i < setSpan; i++ ) {
35021 this.colYs[ shortColIndex + i ] = setHeight ;
35028 * @param {Number} colSpan - number of columns the element spans
35029 * @returns {Array} colGroup
35031 _getColGroup : function( colSpan )
35033 if ( colSpan < 2 ) {
35034 // if brick spans only one column, use all the column Ys
35039 // how many different places could this brick fit horizontally
35040 var groupCount = this.cols + 1 - colSpan;
35041 // for each group potential horizontal position
35042 for ( var i = 0; i < groupCount; i++ ) {
35043 // make an array of colY values for that one group
35044 var groupColYs = this.colYs.slice( i, i + colSpan );
35045 // and get the max value of the array
35046 colGroup[i] = Math.max.apply( Math, groupColYs );
35051 _manageStamp : function( stamp )
35053 var stampSize = stamp.getSize();
35054 var offset = stamp.getBox();
35055 // get the columns that this stamp affects
35056 var firstX = this.isOriginLeft ? offset.x : offset.right;
35057 var lastX = firstX + stampSize.width;
35058 var firstCol = Math.floor( firstX / this.columnWidth );
35059 firstCol = Math.max( 0, firstCol );
35061 var lastCol = Math.floor( lastX / this.columnWidth );
35062 // lastCol should not go over if multiple of columnWidth #425
35063 lastCol -= lastX % this.columnWidth ? 0 : 1;
35064 lastCol = Math.min( this.cols - 1, lastCol );
35066 // set colYs to bottom of the stamp
35067 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35070 for ( var i = firstCol; i <= lastCol; i++ ) {
35071 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35076 _getContainerSize : function()
35078 this.maxY = Math.max.apply( Math, this.colYs );
35083 if ( this.isFitWidth ) {
35084 size.width = this._getContainerFitWidth();
35090 _getContainerFitWidth : function()
35092 var unusedCols = 0;
35093 // count unused columns
35096 if ( this.colYs[i] !== 0 ) {
35101 // fit container to columns that have been used
35102 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35105 needsResizeLayout : function()
35107 var previousWidth = this.containerWidth;
35108 this.getContainerWidth();
35109 return previousWidth !== this.containerWidth;
35124 * @class Roo.bootstrap.MasonryBrick
35125 * @extends Roo.bootstrap.Component
35126 * Bootstrap MasonryBrick class
35129 * Create a new MasonryBrick
35130 * @param {Object} config The config object
35133 Roo.bootstrap.MasonryBrick = function(config){
35135 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35137 Roo.bootstrap.MasonryBrick.register(this);
35143 * When a MasonryBrick is clcik
35144 * @param {Roo.bootstrap.MasonryBrick} this
35145 * @param {Roo.EventObject} e
35151 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35154 * @cfg {String} title
35158 * @cfg {String} html
35162 * @cfg {String} bgimage
35166 * @cfg {String} videourl
35170 * @cfg {String} cls
35174 * @cfg {String} href
35178 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35183 * @cfg {String} placetitle (center|bottom)
35188 * @cfg {Boolean} isFitContainer defalut true
35190 isFitContainer : true,
35193 * @cfg {Boolean} preventDefault defalut false
35195 preventDefault : false,
35198 * @cfg {Boolean} inverse defalut false
35200 maskInverse : false,
35202 getAutoCreate : function()
35204 if(!this.isFitContainer){
35205 return this.getSplitAutoCreate();
35208 var cls = 'masonry-brick masonry-brick-full';
35210 if(this.href.length){
35211 cls += ' masonry-brick-link';
35214 if(this.bgimage.length){
35215 cls += ' masonry-brick-image';
35218 if(this.maskInverse){
35219 cls += ' mask-inverse';
35222 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35223 cls += ' enable-mask';
35227 cls += ' masonry-' + this.size + '-brick';
35230 if(this.placetitle.length){
35232 switch (this.placetitle) {
35234 cls += ' masonry-center-title';
35237 cls += ' masonry-bottom-title';
35244 if(!this.html.length && !this.bgimage.length){
35245 cls += ' masonry-center-title';
35248 if(!this.html.length && this.bgimage.length){
35249 cls += ' masonry-bottom-title';
35254 cls += ' ' + this.cls;
35258 tag: (this.href.length) ? 'a' : 'div',
35263 cls: 'masonry-brick-mask'
35267 cls: 'masonry-brick-paragraph',
35273 if(this.href.length){
35274 cfg.href = this.href;
35277 var cn = cfg.cn[1].cn;
35279 if(this.title.length){
35282 cls: 'masonry-brick-title',
35287 if(this.html.length){
35290 cls: 'masonry-brick-text',
35295 if (!this.title.length && !this.html.length) {
35296 cfg.cn[1].cls += ' hide';
35299 if(this.bgimage.length){
35302 cls: 'masonry-brick-image-view',
35307 if(this.videourl.length){
35308 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35309 // youtube support only?
35312 cls: 'masonry-brick-image-view',
35315 allowfullscreen : true
35323 getSplitAutoCreate : function()
35325 var cls = 'masonry-brick masonry-brick-split';
35327 if(this.href.length){
35328 cls += ' masonry-brick-link';
35331 if(this.bgimage.length){
35332 cls += ' masonry-brick-image';
35336 cls += ' masonry-' + this.size + '-brick';
35339 switch (this.placetitle) {
35341 cls += ' masonry-center-title';
35344 cls += ' masonry-bottom-title';
35347 if(!this.bgimage.length){
35348 cls += ' masonry-center-title';
35351 if(this.bgimage.length){
35352 cls += ' masonry-bottom-title';
35358 cls += ' ' + this.cls;
35362 tag: (this.href.length) ? 'a' : 'div',
35367 cls: 'masonry-brick-split-head',
35371 cls: 'masonry-brick-paragraph',
35378 cls: 'masonry-brick-split-body',
35384 if(this.href.length){
35385 cfg.href = this.href;
35388 if(this.title.length){
35389 cfg.cn[0].cn[0].cn.push({
35391 cls: 'masonry-brick-title',
35396 if(this.html.length){
35397 cfg.cn[1].cn.push({
35399 cls: 'masonry-brick-text',
35404 if(this.bgimage.length){
35405 cfg.cn[0].cn.push({
35407 cls: 'masonry-brick-image-view',
35412 if(this.videourl.length){
35413 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35414 // youtube support only?
35415 cfg.cn[0].cn.cn.push({
35417 cls: 'masonry-brick-image-view',
35420 allowfullscreen : true
35427 initEvents: function()
35429 switch (this.size) {
35462 this.el.on('touchstart', this.onTouchStart, this);
35463 this.el.on('touchmove', this.onTouchMove, this);
35464 this.el.on('touchend', this.onTouchEnd, this);
35465 this.el.on('contextmenu', this.onContextMenu, this);
35467 this.el.on('mouseenter' ,this.enter, this);
35468 this.el.on('mouseleave', this.leave, this);
35469 this.el.on('click', this.onClick, this);
35472 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35473 this.parent().bricks.push(this);
35478 onClick: function(e, el)
35480 var time = this.endTimer - this.startTimer;
35481 // Roo.log(e.preventDefault());
35484 e.preventDefault();
35489 if(!this.preventDefault){
35493 e.preventDefault();
35495 if (this.activeClass != '') {
35496 this.selectBrick();
35499 this.fireEvent('click', this, e);
35502 enter: function(e, el)
35504 e.preventDefault();
35506 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35510 if(this.bgimage.length && this.html.length){
35511 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35515 leave: function(e, el)
35517 e.preventDefault();
35519 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35523 if(this.bgimage.length && this.html.length){
35524 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35528 onTouchStart: function(e, el)
35530 // e.preventDefault();
35532 this.touchmoved = false;
35534 if(!this.isFitContainer){
35538 if(!this.bgimage.length || !this.html.length){
35542 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35544 this.timer = new Date().getTime();
35548 onTouchMove: function(e, el)
35550 this.touchmoved = true;
35553 onContextMenu : function(e,el)
35555 e.preventDefault();
35556 e.stopPropagation();
35560 onTouchEnd: function(e, el)
35562 // e.preventDefault();
35564 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35571 if(!this.bgimage.length || !this.html.length){
35573 if(this.href.length){
35574 window.location.href = this.href;
35580 if(!this.isFitContainer){
35584 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35586 window.location.href = this.href;
35589 //selection on single brick only
35590 selectBrick : function() {
35592 if (!this.parentId) {
35596 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35597 var index = m.selectedBrick.indexOf(this.id);
35600 m.selectedBrick.splice(index,1);
35601 this.el.removeClass(this.activeClass);
35605 for(var i = 0; i < m.selectedBrick.length; i++) {
35606 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35607 b.el.removeClass(b.activeClass);
35610 m.selectedBrick = [];
35612 m.selectedBrick.push(this.id);
35613 this.el.addClass(this.activeClass);
35617 isSelected : function(){
35618 return this.el.hasClass(this.activeClass);
35623 Roo.apply(Roo.bootstrap.MasonryBrick, {
35626 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35628 * register a Masonry Brick
35629 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35632 register : function(brick)
35634 //this.groups[brick.id] = brick;
35635 this.groups.add(brick.id, brick);
35638 * fetch a masonry brick based on the masonry brick ID
35639 * @param {string} the masonry brick to add
35640 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35643 get: function(brick_id)
35645 // if (typeof(this.groups[brick_id]) == 'undefined') {
35648 // return this.groups[brick_id] ;
35650 if(this.groups.key(brick_id)) {
35651 return this.groups.key(brick_id);
35669 * @class Roo.bootstrap.Brick
35670 * @extends Roo.bootstrap.Component
35671 * Bootstrap Brick class
35674 * Create a new Brick
35675 * @param {Object} config The config object
35678 Roo.bootstrap.Brick = function(config){
35679 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35685 * When a Brick is click
35686 * @param {Roo.bootstrap.Brick} this
35687 * @param {Roo.EventObject} e
35693 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35696 * @cfg {String} title
35700 * @cfg {String} html
35704 * @cfg {String} bgimage
35708 * @cfg {String} cls
35712 * @cfg {String} href
35716 * @cfg {String} video
35720 * @cfg {Boolean} square
35724 getAutoCreate : function()
35726 var cls = 'roo-brick';
35728 if(this.href.length){
35729 cls += ' roo-brick-link';
35732 if(this.bgimage.length){
35733 cls += ' roo-brick-image';
35736 if(!this.html.length && !this.bgimage.length){
35737 cls += ' roo-brick-center-title';
35740 if(!this.html.length && this.bgimage.length){
35741 cls += ' roo-brick-bottom-title';
35745 cls += ' ' + this.cls;
35749 tag: (this.href.length) ? 'a' : 'div',
35754 cls: 'roo-brick-paragraph',
35760 if(this.href.length){
35761 cfg.href = this.href;
35764 var cn = cfg.cn[0].cn;
35766 if(this.title.length){
35769 cls: 'roo-brick-title',
35774 if(this.html.length){
35777 cls: 'roo-brick-text',
35784 if(this.bgimage.length){
35787 cls: 'roo-brick-image-view',
35795 initEvents: function()
35797 if(this.title.length || this.html.length){
35798 this.el.on('mouseenter' ,this.enter, this);
35799 this.el.on('mouseleave', this.leave, this);
35802 Roo.EventManager.onWindowResize(this.resize, this);
35804 if(this.bgimage.length){
35805 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35806 this.imageEl.on('load', this.onImageLoad, this);
35813 onImageLoad : function()
35818 resize : function()
35820 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35822 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35824 if(this.bgimage.length){
35825 var image = this.el.select('.roo-brick-image-view', true).first();
35827 image.setWidth(paragraph.getWidth());
35830 image.setHeight(paragraph.getWidth());
35833 this.el.setHeight(image.getHeight());
35834 paragraph.setHeight(image.getHeight());
35840 enter: function(e, el)
35842 e.preventDefault();
35844 if(this.bgimage.length){
35845 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35846 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35850 leave: function(e, el)
35852 e.preventDefault();
35854 if(this.bgimage.length){
35855 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35856 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35871 * @class Roo.bootstrap.NumberField
35872 * @extends Roo.bootstrap.Input
35873 * Bootstrap NumberField class
35879 * Create a new NumberField
35880 * @param {Object} config The config object
35883 Roo.bootstrap.NumberField = function(config){
35884 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35887 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35890 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35892 allowDecimals : true,
35894 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35896 decimalSeparator : ".",
35898 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35900 decimalPrecision : 2,
35902 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35904 allowNegative : true,
35907 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35911 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35913 minValue : Number.NEGATIVE_INFINITY,
35915 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35917 maxValue : Number.MAX_VALUE,
35919 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35921 minText : "The minimum value for this field is {0}",
35923 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35925 maxText : "The maximum value for this field is {0}",
35927 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35928 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35930 nanText : "{0} is not a valid number",
35932 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35934 thousandsDelimiter : false,
35936 * @cfg {String} valueAlign alignment of value
35938 valueAlign : "left",
35940 getAutoCreate : function()
35942 var hiddenInput = {
35946 cls: 'hidden-number-input'
35950 hiddenInput.name = this.name;
35955 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35957 this.name = hiddenInput.name;
35959 if(cfg.cn.length > 0) {
35960 cfg.cn.push(hiddenInput);
35967 initEvents : function()
35969 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35971 var allowed = "0123456789";
35973 if(this.allowDecimals){
35974 allowed += this.decimalSeparator;
35977 if(this.allowNegative){
35981 if(this.thousandsDelimiter) {
35985 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35987 var keyPress = function(e){
35989 var k = e.getKey();
35991 var c = e.getCharCode();
35994 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35995 allowed.indexOf(String.fromCharCode(c)) === -1
36001 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36005 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36010 this.el.on("keypress", keyPress, this);
36013 validateValue : function(value)
36016 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36020 var num = this.parseValue(value);
36023 this.markInvalid(String.format(this.nanText, value));
36027 if(num < this.minValue){
36028 this.markInvalid(String.format(this.minText, this.minValue));
36032 if(num > this.maxValue){
36033 this.markInvalid(String.format(this.maxText, this.maxValue));
36040 getValue : function()
36042 var v = this.hiddenEl().getValue();
36044 return this.fixPrecision(this.parseValue(v));
36047 parseValue : function(value)
36049 if(this.thousandsDelimiter) {
36051 r = new RegExp(",", "g");
36052 value = value.replace(r, "");
36055 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36056 return isNaN(value) ? '' : value;
36059 fixPrecision : function(value)
36061 if(this.thousandsDelimiter) {
36063 r = new RegExp(",", "g");
36064 value = value.replace(r, "");
36067 var nan = isNaN(value);
36069 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36070 return nan ? '' : value;
36072 return parseFloat(value).toFixed(this.decimalPrecision);
36075 setValue : function(v)
36077 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36083 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36085 this.inputEl().dom.value = (v == '') ? '' :
36086 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36088 if(!this.allowZero && v === '0') {
36089 this.hiddenEl().dom.value = '';
36090 this.inputEl().dom.value = '';
36097 decimalPrecisionFcn : function(v)
36099 return Math.floor(v);
36102 beforeBlur : function()
36104 var v = this.parseValue(this.getRawValue());
36106 if(v || v === 0 || v === ''){
36111 hiddenEl : function()
36113 return this.el.select('input.hidden-number-input',true).first();
36125 * @class Roo.bootstrap.DocumentSlider
36126 * @extends Roo.bootstrap.Component
36127 * Bootstrap DocumentSlider class
36130 * Create a new DocumentViewer
36131 * @param {Object} config The config object
36134 Roo.bootstrap.DocumentSlider = function(config){
36135 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36142 * Fire after initEvent
36143 * @param {Roo.bootstrap.DocumentSlider} this
36148 * Fire after update
36149 * @param {Roo.bootstrap.DocumentSlider} this
36155 * @param {Roo.bootstrap.DocumentSlider} this
36161 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36167 getAutoCreate : function()
36171 cls : 'roo-document-slider',
36175 cls : 'roo-document-slider-header',
36179 cls : 'roo-document-slider-header-title'
36185 cls : 'roo-document-slider-body',
36189 cls : 'roo-document-slider-prev',
36193 cls : 'fa fa-chevron-left'
36199 cls : 'roo-document-slider-thumb',
36203 cls : 'roo-document-slider-image'
36209 cls : 'roo-document-slider-next',
36213 cls : 'fa fa-chevron-right'
36225 initEvents : function()
36227 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36228 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36230 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36231 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36233 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36234 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36236 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36237 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36239 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36240 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36242 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36243 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36245 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36246 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36248 this.thumbEl.on('click', this.onClick, this);
36250 this.prevIndicator.on('click', this.prev, this);
36252 this.nextIndicator.on('click', this.next, this);
36256 initial : function()
36258 if(this.files.length){
36259 this.indicator = 1;
36263 this.fireEvent('initial', this);
36266 update : function()
36268 this.imageEl.attr('src', this.files[this.indicator - 1]);
36270 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36272 this.prevIndicator.show();
36274 if(this.indicator == 1){
36275 this.prevIndicator.hide();
36278 this.nextIndicator.show();
36280 if(this.indicator == this.files.length){
36281 this.nextIndicator.hide();
36284 this.thumbEl.scrollTo('top');
36286 this.fireEvent('update', this);
36289 onClick : function(e)
36291 e.preventDefault();
36293 this.fireEvent('click', this);
36298 e.preventDefault();
36300 this.indicator = Math.max(1, this.indicator - 1);
36307 e.preventDefault();
36309 this.indicator = Math.min(this.files.length, this.indicator + 1);
36323 * @class Roo.bootstrap.RadioSet
36324 * @extends Roo.bootstrap.Input
36325 * Bootstrap RadioSet class
36326 * @cfg {String} indicatorpos (left|right) default left
36327 * @cfg {Boolean} inline (true|false) inline the element (default true)
36328 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36330 * Create a new RadioSet
36331 * @param {Object} config The config object
36334 Roo.bootstrap.RadioSet = function(config){
36336 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36340 Roo.bootstrap.RadioSet.register(this);
36345 * Fires when the element is checked or unchecked.
36346 * @param {Roo.bootstrap.RadioSet} this This radio
36347 * @param {Roo.bootstrap.Radio} item The checked item
36352 * Fires when the element is click.
36353 * @param {Roo.bootstrap.RadioSet} this This radio set
36354 * @param {Roo.bootstrap.Radio} item The checked item
36355 * @param {Roo.EventObject} e The event object
36362 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36370 indicatorpos : 'left',
36372 getAutoCreate : function()
36376 cls : 'roo-radio-set-label',
36380 html : this.fieldLabel
36384 if (Roo.bootstrap.version == 3) {
36387 if(this.indicatorpos == 'left'){
36390 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36391 tooltip : 'This field is required'
36396 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36397 tooltip : 'This field is required'
36403 cls : 'roo-radio-set-items'
36406 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36408 if (align === 'left' && this.fieldLabel.length) {
36411 cls : "roo-radio-set-right",
36417 if(this.labelWidth > 12){
36418 label.style = "width: " + this.labelWidth + 'px';
36421 if(this.labelWidth < 13 && this.labelmd == 0){
36422 this.labelmd = this.labelWidth;
36425 if(this.labellg > 0){
36426 label.cls += ' col-lg-' + this.labellg;
36427 items.cls += ' col-lg-' + (12 - this.labellg);
36430 if(this.labelmd > 0){
36431 label.cls += ' col-md-' + this.labelmd;
36432 items.cls += ' col-md-' + (12 - this.labelmd);
36435 if(this.labelsm > 0){
36436 label.cls += ' col-sm-' + this.labelsm;
36437 items.cls += ' col-sm-' + (12 - this.labelsm);
36440 if(this.labelxs > 0){
36441 label.cls += ' col-xs-' + this.labelxs;
36442 items.cls += ' col-xs-' + (12 - this.labelxs);
36448 cls : 'roo-radio-set',
36452 cls : 'roo-radio-set-input',
36455 value : this.value ? this.value : ''
36462 if(this.weight.length){
36463 cfg.cls += ' roo-radio-' + this.weight;
36467 cfg.cls += ' roo-radio-set-inline';
36471 ['xs','sm','md','lg'].map(function(size){
36472 if (settings[size]) {
36473 cfg.cls += ' col-' + size + '-' + settings[size];
36481 initEvents : function()
36483 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36484 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36486 if(!this.fieldLabel.length){
36487 this.labelEl.hide();
36490 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36491 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36493 this.indicator = this.indicatorEl();
36495 if(this.indicator){
36496 this.indicator.addClass('invisible');
36499 this.originalValue = this.getValue();
36503 inputEl: function ()
36505 return this.el.select('.roo-radio-set-input', true).first();
36508 getChildContainer : function()
36510 return this.itemsEl;
36513 register : function(item)
36515 this.radioes.push(item);
36519 validate : function()
36521 if(this.getVisibilityEl().hasClass('hidden')){
36527 Roo.each(this.radioes, function(i){
36536 if(this.allowBlank) {
36540 if(this.disabled || valid){
36545 this.markInvalid();
36550 markValid : function()
36552 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36553 this.indicatorEl().removeClass('visible');
36554 this.indicatorEl().addClass('invisible');
36558 if (Roo.bootstrap.version == 3) {
36559 this.el.removeClass([this.invalidClass, this.validClass]);
36560 this.el.addClass(this.validClass);
36562 this.el.removeClass(['is-invalid','is-valid']);
36563 this.el.addClass(['is-valid']);
36565 this.fireEvent('valid', this);
36568 markInvalid : function(msg)
36570 if(this.allowBlank || this.disabled){
36574 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36575 this.indicatorEl().removeClass('invisible');
36576 this.indicatorEl().addClass('visible');
36578 if (Roo.bootstrap.version == 3) {
36579 this.el.removeClass([this.invalidClass, this.validClass]);
36580 this.el.addClass(this.invalidClass);
36582 this.el.removeClass(['is-invalid','is-valid']);
36583 this.el.addClass(['is-invalid']);
36586 this.fireEvent('invalid', this, msg);
36590 setValue : function(v, suppressEvent)
36592 if(this.value === v){
36599 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36602 Roo.each(this.radioes, function(i){
36604 i.el.removeClass('checked');
36607 Roo.each(this.radioes, function(i){
36609 if(i.value === v || i.value.toString() === v.toString()){
36611 i.el.addClass('checked');
36613 if(suppressEvent !== true){
36614 this.fireEvent('check', this, i);
36625 clearInvalid : function(){
36627 if(!this.el || this.preventMark){
36631 this.el.removeClass([this.invalidClass]);
36633 this.fireEvent('valid', this);
36638 Roo.apply(Roo.bootstrap.RadioSet, {
36642 register : function(set)
36644 this.groups[set.name] = set;
36647 get: function(name)
36649 if (typeof(this.groups[name]) == 'undefined') {
36653 return this.groups[name] ;
36659 * Ext JS Library 1.1.1
36660 * Copyright(c) 2006-2007, Ext JS, LLC.
36662 * Originally Released Under LGPL - original licence link has changed is not relivant.
36665 * <script type="text/javascript">
36670 * @class Roo.bootstrap.SplitBar
36671 * @extends Roo.util.Observable
36672 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36676 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36677 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36678 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36679 split.minSize = 100;
36680 split.maxSize = 600;
36681 split.animate = true;
36682 split.on('moved', splitterMoved);
36685 * Create a new SplitBar
36686 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36687 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36688 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36689 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36690 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36691 position of the SplitBar).
36693 Roo.bootstrap.SplitBar = function(cfg){
36698 // dragElement : elm
36699 // resizingElement: el,
36701 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36702 // placement : Roo.bootstrap.SplitBar.LEFT ,
36703 // existingProxy ???
36706 this.el = Roo.get(cfg.dragElement, true);
36707 this.el.dom.unselectable = "on";
36709 this.resizingEl = Roo.get(cfg.resizingElement, true);
36713 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36714 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36717 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36720 * The minimum size of the resizing element. (Defaults to 0)
36726 * The maximum size of the resizing element. (Defaults to 2000)
36729 this.maxSize = 2000;
36732 * Whether to animate the transition to the new size
36735 this.animate = false;
36738 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36741 this.useShim = false;
36746 if(!cfg.existingProxy){
36748 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36750 this.proxy = Roo.get(cfg.existingProxy).dom;
36753 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36756 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36759 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36762 this.dragSpecs = {};
36765 * @private The adapter to use to positon and resize elements
36767 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36768 this.adapter.init(this);
36770 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36772 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36773 this.el.addClass("roo-splitbar-h");
36776 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36777 this.el.addClass("roo-splitbar-v");
36783 * Fires when the splitter is moved (alias for {@link #event-moved})
36784 * @param {Roo.bootstrap.SplitBar} this
36785 * @param {Number} newSize the new width or height
36790 * Fires when the splitter is moved
36791 * @param {Roo.bootstrap.SplitBar} this
36792 * @param {Number} newSize the new width or height
36796 * @event beforeresize
36797 * Fires before the splitter is dragged
36798 * @param {Roo.bootstrap.SplitBar} this
36800 "beforeresize" : true,
36802 "beforeapply" : true
36805 Roo.util.Observable.call(this);
36808 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36809 onStartProxyDrag : function(x, y){
36810 this.fireEvent("beforeresize", this);
36812 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36814 o.enableDisplayMode("block");
36815 // all splitbars share the same overlay
36816 Roo.bootstrap.SplitBar.prototype.overlay = o;
36818 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36819 this.overlay.show();
36820 Roo.get(this.proxy).setDisplayed("block");
36821 var size = this.adapter.getElementSize(this);
36822 this.activeMinSize = this.getMinimumSize();;
36823 this.activeMaxSize = this.getMaximumSize();;
36824 var c1 = size - this.activeMinSize;
36825 var c2 = Math.max(this.activeMaxSize - size, 0);
36826 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36827 this.dd.resetConstraints();
36828 this.dd.setXConstraint(
36829 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36830 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36832 this.dd.setYConstraint(0, 0);
36834 this.dd.resetConstraints();
36835 this.dd.setXConstraint(0, 0);
36836 this.dd.setYConstraint(
36837 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36838 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36841 this.dragSpecs.startSize = size;
36842 this.dragSpecs.startPoint = [x, y];
36843 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36847 * @private Called after the drag operation by the DDProxy
36849 onEndProxyDrag : function(e){
36850 Roo.get(this.proxy).setDisplayed(false);
36851 var endPoint = Roo.lib.Event.getXY(e);
36853 this.overlay.hide();
36856 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36857 newSize = this.dragSpecs.startSize +
36858 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36859 endPoint[0] - this.dragSpecs.startPoint[0] :
36860 this.dragSpecs.startPoint[0] - endPoint[0]
36863 newSize = this.dragSpecs.startSize +
36864 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36865 endPoint[1] - this.dragSpecs.startPoint[1] :
36866 this.dragSpecs.startPoint[1] - endPoint[1]
36869 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36870 if(newSize != this.dragSpecs.startSize){
36871 if(this.fireEvent('beforeapply', this, newSize) !== false){
36872 this.adapter.setElementSize(this, newSize);
36873 this.fireEvent("moved", this, newSize);
36874 this.fireEvent("resize", this, newSize);
36880 * Get the adapter this SplitBar uses
36881 * @return The adapter object
36883 getAdapter : function(){
36884 return this.adapter;
36888 * Set the adapter this SplitBar uses
36889 * @param {Object} adapter A SplitBar adapter object
36891 setAdapter : function(adapter){
36892 this.adapter = adapter;
36893 this.adapter.init(this);
36897 * Gets the minimum size for the resizing element
36898 * @return {Number} The minimum size
36900 getMinimumSize : function(){
36901 return this.minSize;
36905 * Sets the minimum size for the resizing element
36906 * @param {Number} minSize The minimum size
36908 setMinimumSize : function(minSize){
36909 this.minSize = minSize;
36913 * Gets the maximum size for the resizing element
36914 * @return {Number} The maximum size
36916 getMaximumSize : function(){
36917 return this.maxSize;
36921 * Sets the maximum size for the resizing element
36922 * @param {Number} maxSize The maximum size
36924 setMaximumSize : function(maxSize){
36925 this.maxSize = maxSize;
36929 * Sets the initialize size for the resizing element
36930 * @param {Number} size The initial size
36932 setCurrentSize : function(size){
36933 var oldAnimate = this.animate;
36934 this.animate = false;
36935 this.adapter.setElementSize(this, size);
36936 this.animate = oldAnimate;
36940 * Destroy this splitbar.
36941 * @param {Boolean} removeEl True to remove the element
36943 destroy : function(removeEl){
36945 this.shim.remove();
36948 this.proxy.parentNode.removeChild(this.proxy);
36956 * @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.
36958 Roo.bootstrap.SplitBar.createProxy = function(dir){
36959 var proxy = new Roo.Element(document.createElement("div"));
36960 proxy.unselectable();
36961 var cls = 'roo-splitbar-proxy';
36962 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36963 document.body.appendChild(proxy.dom);
36968 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36969 * Default Adapter. It assumes the splitter and resizing element are not positioned
36970 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36972 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36975 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36976 // do nothing for now
36977 init : function(s){
36981 * Called before drag operations to get the current size of the resizing element.
36982 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36984 getElementSize : function(s){
36985 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36986 return s.resizingEl.getWidth();
36988 return s.resizingEl.getHeight();
36993 * Called after drag operations to set the size of the resizing element.
36994 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36995 * @param {Number} newSize The new size to set
36996 * @param {Function} onComplete A function to be invoked when resizing is complete
36998 setElementSize : function(s, newSize, onComplete){
36999 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37001 s.resizingEl.setWidth(newSize);
37003 onComplete(s, newSize);
37006 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37011 s.resizingEl.setHeight(newSize);
37013 onComplete(s, newSize);
37016 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37023 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37024 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37025 * Adapter that moves the splitter element to align with the resized sizing element.
37026 * Used with an absolute positioned SplitBar.
37027 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37028 * document.body, make sure you assign an id to the body element.
37030 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37031 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37032 this.container = Roo.get(container);
37035 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37036 init : function(s){
37037 this.basic.init(s);
37040 getElementSize : function(s){
37041 return this.basic.getElementSize(s);
37044 setElementSize : function(s, newSize, onComplete){
37045 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37048 moveSplitter : function(s){
37049 var yes = Roo.bootstrap.SplitBar;
37050 switch(s.placement){
37052 s.el.setX(s.resizingEl.getRight());
37055 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37058 s.el.setY(s.resizingEl.getBottom());
37061 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37068 * Orientation constant - Create a vertical SplitBar
37072 Roo.bootstrap.SplitBar.VERTICAL = 1;
37075 * Orientation constant - Create a horizontal SplitBar
37079 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37082 * Placement constant - The resizing element is to the left of the splitter element
37086 Roo.bootstrap.SplitBar.LEFT = 1;
37089 * Placement constant - The resizing element is to the right of the splitter element
37093 Roo.bootstrap.SplitBar.RIGHT = 2;
37096 * Placement constant - The resizing element is positioned above the splitter element
37100 Roo.bootstrap.SplitBar.TOP = 3;
37103 * Placement constant - The resizing element is positioned under splitter element
37107 Roo.bootstrap.SplitBar.BOTTOM = 4;
37108 Roo.namespace("Roo.bootstrap.layout");/*
37110 * Ext JS Library 1.1.1
37111 * Copyright(c) 2006-2007, Ext JS, LLC.
37113 * Originally Released Under LGPL - original licence link has changed is not relivant.
37116 * <script type="text/javascript">
37120 * @class Roo.bootstrap.layout.Manager
37121 * @extends Roo.bootstrap.Component
37122 * Base class for layout managers.
37124 Roo.bootstrap.layout.Manager = function(config)
37126 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37132 /** false to disable window resize monitoring @type Boolean */
37133 this.monitorWindowResize = true;
37138 * Fires when a layout is performed.
37139 * @param {Roo.LayoutManager} this
37143 * @event regionresized
37144 * Fires when the user resizes a region.
37145 * @param {Roo.LayoutRegion} region The resized region
37146 * @param {Number} newSize The new size (width for east/west, height for north/south)
37148 "regionresized" : true,
37150 * @event regioncollapsed
37151 * Fires when a region is collapsed.
37152 * @param {Roo.LayoutRegion} region The collapsed region
37154 "regioncollapsed" : true,
37156 * @event regionexpanded
37157 * Fires when a region is expanded.
37158 * @param {Roo.LayoutRegion} region The expanded region
37160 "regionexpanded" : true
37162 this.updating = false;
37165 this.el = Roo.get(config.el);
37171 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37176 monitorWindowResize : true,
37182 onRender : function(ct, position)
37185 this.el = Roo.get(ct);
37188 //this.fireEvent('render',this);
37192 initEvents: function()
37196 // ie scrollbar fix
37197 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37198 document.body.scroll = "no";
37199 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37200 this.el.position('relative');
37202 this.id = this.el.id;
37203 this.el.addClass("roo-layout-container");
37204 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37205 if(this.el.dom != document.body ) {
37206 this.el.on('resize', this.layout,this);
37207 this.el.on('show', this.layout,this);
37213 * Returns true if this layout is currently being updated
37214 * @return {Boolean}
37216 isUpdating : function(){
37217 return this.updating;
37221 * Suspend the LayoutManager from doing auto-layouts while
37222 * making multiple add or remove calls
37224 beginUpdate : function(){
37225 this.updating = true;
37229 * Restore auto-layouts and optionally disable the manager from performing a layout
37230 * @param {Boolean} noLayout true to disable a layout update
37232 endUpdate : function(noLayout){
37233 this.updating = false;
37239 layout: function(){
37243 onRegionResized : function(region, newSize){
37244 this.fireEvent("regionresized", region, newSize);
37248 onRegionCollapsed : function(region){
37249 this.fireEvent("regioncollapsed", region);
37252 onRegionExpanded : function(region){
37253 this.fireEvent("regionexpanded", region);
37257 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37258 * performs box-model adjustments.
37259 * @return {Object} The size as an object {width: (the width), height: (the height)}
37261 getViewSize : function()
37264 if(this.el.dom != document.body){
37265 size = this.el.getSize();
37267 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37269 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37270 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37275 * Returns the Element this layout is bound to.
37276 * @return {Roo.Element}
37278 getEl : function(){
37283 * Returns the specified region.
37284 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37285 * @return {Roo.LayoutRegion}
37287 getRegion : function(target){
37288 return this.regions[target.toLowerCase()];
37291 onWindowResize : function(){
37292 if(this.monitorWindowResize){
37299 * Ext JS Library 1.1.1
37300 * Copyright(c) 2006-2007, Ext JS, LLC.
37302 * Originally Released Under LGPL - original licence link has changed is not relivant.
37305 * <script type="text/javascript">
37308 * @class Roo.bootstrap.layout.Border
37309 * @extends Roo.bootstrap.layout.Manager
37310 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37311 * please see: examples/bootstrap/nested.html<br><br>
37313 <b>The container the layout is rendered into can be either the body element or any other element.
37314 If it is not the body element, the container needs to either be an absolute positioned element,
37315 or you will need to add "position:relative" to the css of the container. You will also need to specify
37316 the container size if it is not the body element.</b>
37319 * Create a new Border
37320 * @param {Object} config Configuration options
37322 Roo.bootstrap.layout.Border = function(config){
37323 config = config || {};
37324 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37328 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37329 if(config[region]){
37330 config[region].region = region;
37331 this.addRegion(config[region]);
37337 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37339 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37341 parent : false, // this might point to a 'nest' or a ???
37344 * Creates and adds a new region if it doesn't already exist.
37345 * @param {String} target The target region key (north, south, east, west or center).
37346 * @param {Object} config The regions config object
37347 * @return {BorderLayoutRegion} The new region
37349 addRegion : function(config)
37351 if(!this.regions[config.region]){
37352 var r = this.factory(config);
37353 this.bindRegion(r);
37355 return this.regions[config.region];
37359 bindRegion : function(r){
37360 this.regions[r.config.region] = r;
37362 r.on("visibilitychange", this.layout, this);
37363 r.on("paneladded", this.layout, this);
37364 r.on("panelremoved", this.layout, this);
37365 r.on("invalidated", this.layout, this);
37366 r.on("resized", this.onRegionResized, this);
37367 r.on("collapsed", this.onRegionCollapsed, this);
37368 r.on("expanded", this.onRegionExpanded, this);
37372 * Performs a layout update.
37374 layout : function()
37376 if(this.updating) {
37380 // render all the rebions if they have not been done alreayd?
37381 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37382 if(this.regions[region] && !this.regions[region].bodyEl){
37383 this.regions[region].onRender(this.el)
37387 var size = this.getViewSize();
37388 var w = size.width;
37389 var h = size.height;
37394 //var x = 0, y = 0;
37396 var rs = this.regions;
37397 var north = rs["north"];
37398 var south = rs["south"];
37399 var west = rs["west"];
37400 var east = rs["east"];
37401 var center = rs["center"];
37402 //if(this.hideOnLayout){ // not supported anymore
37403 //c.el.setStyle("display", "none");
37405 if(north && north.isVisible()){
37406 var b = north.getBox();
37407 var m = north.getMargins();
37408 b.width = w - (m.left+m.right);
37411 centerY = b.height + b.y + m.bottom;
37412 centerH -= centerY;
37413 north.updateBox(this.safeBox(b));
37415 if(south && south.isVisible()){
37416 var b = south.getBox();
37417 var m = south.getMargins();
37418 b.width = w - (m.left+m.right);
37420 var totalHeight = (b.height + m.top + m.bottom);
37421 b.y = h - totalHeight + m.top;
37422 centerH -= totalHeight;
37423 south.updateBox(this.safeBox(b));
37425 if(west && west.isVisible()){
37426 var b = west.getBox();
37427 var m = west.getMargins();
37428 b.height = centerH - (m.top+m.bottom);
37430 b.y = centerY + m.top;
37431 var totalWidth = (b.width + m.left + m.right);
37432 centerX += totalWidth;
37433 centerW -= totalWidth;
37434 west.updateBox(this.safeBox(b));
37436 if(east && east.isVisible()){
37437 var b = east.getBox();
37438 var m = east.getMargins();
37439 b.height = centerH - (m.top+m.bottom);
37440 var totalWidth = (b.width + m.left + m.right);
37441 b.x = w - totalWidth + m.left;
37442 b.y = centerY + m.top;
37443 centerW -= totalWidth;
37444 east.updateBox(this.safeBox(b));
37447 var m = center.getMargins();
37449 x: centerX + m.left,
37450 y: centerY + m.top,
37451 width: centerW - (m.left+m.right),
37452 height: centerH - (m.top+m.bottom)
37454 //if(this.hideOnLayout){
37455 //center.el.setStyle("display", "block");
37457 center.updateBox(this.safeBox(centerBox));
37460 this.fireEvent("layout", this);
37464 safeBox : function(box){
37465 box.width = Math.max(0, box.width);
37466 box.height = Math.max(0, box.height);
37471 * Adds a ContentPanel (or subclass) to this layout.
37472 * @param {String} target The target region key (north, south, east, west or center).
37473 * @param {Roo.ContentPanel} panel The panel to add
37474 * @return {Roo.ContentPanel} The added panel
37476 add : function(target, panel){
37478 target = target.toLowerCase();
37479 return this.regions[target].add(panel);
37483 * Remove a ContentPanel (or subclass) to this layout.
37484 * @param {String} target The target region key (north, south, east, west or center).
37485 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37486 * @return {Roo.ContentPanel} The removed panel
37488 remove : function(target, panel){
37489 target = target.toLowerCase();
37490 return this.regions[target].remove(panel);
37494 * Searches all regions for a panel with the specified id
37495 * @param {String} panelId
37496 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37498 findPanel : function(panelId){
37499 var rs = this.regions;
37500 for(var target in rs){
37501 if(typeof rs[target] != "function"){
37502 var p = rs[target].getPanel(panelId);
37512 * Searches all regions for a panel with the specified id and activates (shows) it.
37513 * @param {String/ContentPanel} panelId The panels id or the panel itself
37514 * @return {Roo.ContentPanel} The shown panel or null
37516 showPanel : function(panelId) {
37517 var rs = this.regions;
37518 for(var target in rs){
37519 var r = rs[target];
37520 if(typeof r != "function"){
37521 if(r.hasPanel(panelId)){
37522 return r.showPanel(panelId);
37530 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37531 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37534 restoreState : function(provider){
37536 provider = Roo.state.Manager;
37538 var sm = new Roo.LayoutStateManager();
37539 sm.init(this, provider);
37545 * Adds a xtype elements to the layout.
37549 xtype : 'ContentPanel',
37556 xtype : 'NestedLayoutPanel',
37562 items : [ ... list of content panels or nested layout panels.. ]
37566 * @param {Object} cfg Xtype definition of item to add.
37568 addxtype : function(cfg)
37570 // basically accepts a pannel...
37571 // can accept a layout region..!?!?
37572 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37575 // theory? children can only be panels??
37577 //if (!cfg.xtype.match(/Panel$/)) {
37582 if (typeof(cfg.region) == 'undefined') {
37583 Roo.log("Failed to add Panel, region was not set");
37587 var region = cfg.region;
37593 xitems = cfg.items;
37598 if ( region == 'center') {
37599 Roo.log("Center: " + cfg.title);
37605 case 'Content': // ContentPanel (el, cfg)
37606 case 'Scroll': // ContentPanel (el, cfg)
37608 cfg.autoCreate = cfg.autoCreate || true;
37609 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37611 // var el = this.el.createChild();
37612 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37615 this.add(region, ret);
37619 case 'TreePanel': // our new panel!
37620 cfg.el = this.el.createChild();
37621 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37622 this.add(region, ret);
37627 // create a new Layout (which is a Border Layout...
37629 var clayout = cfg.layout;
37630 clayout.el = this.el.createChild();
37631 clayout.items = clayout.items || [];
37635 // replace this exitems with the clayout ones..
37636 xitems = clayout.items;
37638 // force background off if it's in center...
37639 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37640 cfg.background = false;
37642 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37645 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37646 //console.log('adding nested layout panel ' + cfg.toSource());
37647 this.add(region, ret);
37648 nb = {}; /// find first...
37653 // needs grid and region
37655 //var el = this.getRegion(region).el.createChild();
37657 *var el = this.el.createChild();
37658 // create the grid first...
37659 cfg.grid.container = el;
37660 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37663 if (region == 'center' && this.active ) {
37664 cfg.background = false;
37667 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37669 this.add(region, ret);
37671 if (cfg.background) {
37672 // render grid on panel activation (if panel background)
37673 ret.on('activate', function(gp) {
37674 if (!gp.grid.rendered) {
37675 // gp.grid.render(el);
37679 // cfg.grid.render(el);
37685 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37686 // it was the old xcomponent building that caused this before.
37687 // espeically if border is the top element in the tree.
37697 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37699 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37700 this.add(region, ret);
37704 throw "Can not add '" + cfg.xtype + "' to Border";
37710 this.beginUpdate();
37714 Roo.each(xitems, function(i) {
37715 region = nb && i.region ? i.region : false;
37717 var add = ret.addxtype(i);
37720 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37721 if (!i.background) {
37722 abn[region] = nb[region] ;
37729 // make the last non-background panel active..
37730 //if (nb) { Roo.log(abn); }
37733 for(var r in abn) {
37734 region = this.getRegion(r);
37736 // tried using nb[r], but it does not work..
37738 region.showPanel(abn[r]);
37749 factory : function(cfg)
37752 var validRegions = Roo.bootstrap.layout.Border.regions;
37754 var target = cfg.region;
37757 var r = Roo.bootstrap.layout;
37761 return new r.North(cfg);
37763 return new r.South(cfg);
37765 return new r.East(cfg);
37767 return new r.West(cfg);
37769 return new r.Center(cfg);
37771 throw 'Layout region "'+target+'" not supported.';
37778 * Ext JS Library 1.1.1
37779 * Copyright(c) 2006-2007, Ext JS, LLC.
37781 * Originally Released Under LGPL - original licence link has changed is not relivant.
37784 * <script type="text/javascript">
37788 * @class Roo.bootstrap.layout.Basic
37789 * @extends Roo.util.Observable
37790 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37791 * and does not have a titlebar, tabs or any other features. All it does is size and position
37792 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37793 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37794 * @cfg {string} region the region that it inhabits..
37795 * @cfg {bool} skipConfig skip config?
37799 Roo.bootstrap.layout.Basic = function(config){
37801 this.mgr = config.mgr;
37803 this.position = config.region;
37805 var skipConfig = config.skipConfig;
37809 * @scope Roo.BasicLayoutRegion
37813 * @event beforeremove
37814 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37815 * @param {Roo.LayoutRegion} this
37816 * @param {Roo.ContentPanel} panel The panel
37817 * @param {Object} e The cancel event object
37819 "beforeremove" : true,
37821 * @event invalidated
37822 * Fires when the layout for this region is changed.
37823 * @param {Roo.LayoutRegion} this
37825 "invalidated" : true,
37827 * @event visibilitychange
37828 * Fires when this region is shown or hidden
37829 * @param {Roo.LayoutRegion} this
37830 * @param {Boolean} visibility true or false
37832 "visibilitychange" : true,
37834 * @event paneladded
37835 * Fires when a panel is added.
37836 * @param {Roo.LayoutRegion} this
37837 * @param {Roo.ContentPanel} panel The panel
37839 "paneladded" : true,
37841 * @event panelremoved
37842 * Fires when a panel is removed.
37843 * @param {Roo.LayoutRegion} this
37844 * @param {Roo.ContentPanel} panel The panel
37846 "panelremoved" : true,
37848 * @event beforecollapse
37849 * Fires when this region before collapse.
37850 * @param {Roo.LayoutRegion} this
37852 "beforecollapse" : true,
37855 * Fires when this region is collapsed.
37856 * @param {Roo.LayoutRegion} this
37858 "collapsed" : true,
37861 * Fires when this region is expanded.
37862 * @param {Roo.LayoutRegion} this
37867 * Fires when this region is slid into view.
37868 * @param {Roo.LayoutRegion} this
37870 "slideshow" : true,
37873 * Fires when this region slides out of view.
37874 * @param {Roo.LayoutRegion} this
37876 "slidehide" : true,
37878 * @event panelactivated
37879 * Fires when a panel is activated.
37880 * @param {Roo.LayoutRegion} this
37881 * @param {Roo.ContentPanel} panel The activated panel
37883 "panelactivated" : true,
37886 * Fires when the user resizes this region.
37887 * @param {Roo.LayoutRegion} this
37888 * @param {Number} newSize The new size (width for east/west, height for north/south)
37892 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37893 this.panels = new Roo.util.MixedCollection();
37894 this.panels.getKey = this.getPanelId.createDelegate(this);
37896 this.activePanel = null;
37897 // ensure listeners are added...
37899 if (config.listeners || config.events) {
37900 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37901 listeners : config.listeners || {},
37902 events : config.events || {}
37906 if(skipConfig !== true){
37907 this.applyConfig(config);
37911 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37913 getPanelId : function(p){
37917 applyConfig : function(config){
37918 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37919 this.config = config;
37924 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37925 * the width, for horizontal (north, south) the height.
37926 * @param {Number} newSize The new width or height
37928 resizeTo : function(newSize){
37929 var el = this.el ? this.el :
37930 (this.activePanel ? this.activePanel.getEl() : null);
37932 switch(this.position){
37935 el.setWidth(newSize);
37936 this.fireEvent("resized", this, newSize);
37940 el.setHeight(newSize);
37941 this.fireEvent("resized", this, newSize);
37947 getBox : function(){
37948 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37951 getMargins : function(){
37952 return this.margins;
37955 updateBox : function(box){
37957 var el = this.activePanel.getEl();
37958 el.dom.style.left = box.x + "px";
37959 el.dom.style.top = box.y + "px";
37960 this.activePanel.setSize(box.width, box.height);
37964 * Returns the container element for this region.
37965 * @return {Roo.Element}
37967 getEl : function(){
37968 return this.activePanel;
37972 * Returns true if this region is currently visible.
37973 * @return {Boolean}
37975 isVisible : function(){
37976 return this.activePanel ? true : false;
37979 setActivePanel : function(panel){
37980 panel = this.getPanel(panel);
37981 if(this.activePanel && this.activePanel != panel){
37982 this.activePanel.setActiveState(false);
37983 this.activePanel.getEl().setLeftTop(-10000,-10000);
37985 this.activePanel = panel;
37986 panel.setActiveState(true);
37988 panel.setSize(this.box.width, this.box.height);
37990 this.fireEvent("panelactivated", this, panel);
37991 this.fireEvent("invalidated");
37995 * Show the specified panel.
37996 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37997 * @return {Roo.ContentPanel} The shown panel or null
37999 showPanel : function(panel){
38000 panel = this.getPanel(panel);
38002 this.setActivePanel(panel);
38008 * Get the active panel for this region.
38009 * @return {Roo.ContentPanel} The active panel or null
38011 getActivePanel : function(){
38012 return this.activePanel;
38016 * Add the passed ContentPanel(s)
38017 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38018 * @return {Roo.ContentPanel} The panel added (if only one was added)
38020 add : function(panel){
38021 if(arguments.length > 1){
38022 for(var i = 0, len = arguments.length; i < len; i++) {
38023 this.add(arguments[i]);
38027 if(this.hasPanel(panel)){
38028 this.showPanel(panel);
38031 var el = panel.getEl();
38032 if(el.dom.parentNode != this.mgr.el.dom){
38033 this.mgr.el.dom.appendChild(el.dom);
38035 if(panel.setRegion){
38036 panel.setRegion(this);
38038 this.panels.add(panel);
38039 el.setStyle("position", "absolute");
38040 if(!panel.background){
38041 this.setActivePanel(panel);
38042 if(this.config.initialSize && this.panels.getCount()==1){
38043 this.resizeTo(this.config.initialSize);
38046 this.fireEvent("paneladded", this, panel);
38051 * Returns true if the panel is in this region.
38052 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38053 * @return {Boolean}
38055 hasPanel : function(panel){
38056 if(typeof panel == "object"){ // must be panel obj
38057 panel = panel.getId();
38059 return this.getPanel(panel) ? true : false;
38063 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38064 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38065 * @param {Boolean} preservePanel Overrides the config preservePanel option
38066 * @return {Roo.ContentPanel} The panel that was removed
38068 remove : function(panel, preservePanel){
38069 panel = this.getPanel(panel);
38074 this.fireEvent("beforeremove", this, panel, e);
38075 if(e.cancel === true){
38078 var panelId = panel.getId();
38079 this.panels.removeKey(panelId);
38084 * Returns the panel specified or null if it's not in this region.
38085 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38086 * @return {Roo.ContentPanel}
38088 getPanel : function(id){
38089 if(typeof id == "object"){ // must be panel obj
38092 return this.panels.get(id);
38096 * Returns this regions position (north/south/east/west/center).
38099 getPosition: function(){
38100 return this.position;
38104 * Ext JS Library 1.1.1
38105 * Copyright(c) 2006-2007, Ext JS, LLC.
38107 * Originally Released Under LGPL - original licence link has changed is not relivant.
38110 * <script type="text/javascript">
38114 * @class Roo.bootstrap.layout.Region
38115 * @extends Roo.bootstrap.layout.Basic
38116 * This class represents a region in a layout manager.
38118 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38119 * @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})
38120 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38121 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38122 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38123 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38124 * @cfg {String} title The title for the region (overrides panel titles)
38125 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38126 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38127 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38128 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38129 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38130 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38131 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38132 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38133 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38134 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38136 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38137 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38138 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38139 * @cfg {Number} width For East/West panels
38140 * @cfg {Number} height For North/South panels
38141 * @cfg {Boolean} split To show the splitter
38142 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38144 * @cfg {string} cls Extra CSS classes to add to region
38146 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38147 * @cfg {string} region the region that it inhabits..
38150 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38151 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38153 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38154 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38155 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38157 Roo.bootstrap.layout.Region = function(config)
38159 this.applyConfig(config);
38161 var mgr = config.mgr;
38162 var pos = config.region;
38163 config.skipConfig = true;
38164 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38167 this.onRender(mgr.el);
38170 this.visible = true;
38171 this.collapsed = false;
38172 this.unrendered_panels = [];
38175 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38177 position: '', // set by wrapper (eg. north/south etc..)
38178 unrendered_panels : null, // unrendered panels.
38180 tabPosition : false,
38182 mgr: false, // points to 'Border'
38185 createBody : function(){
38186 /** This region's body element
38187 * @type Roo.Element */
38188 this.bodyEl = this.el.createChild({
38190 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38194 onRender: function(ctr, pos)
38196 var dh = Roo.DomHelper;
38197 /** This region's container element
38198 * @type Roo.Element */
38199 this.el = dh.append(ctr.dom, {
38201 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38203 /** This region's title element
38204 * @type Roo.Element */
38206 this.titleEl = dh.append(this.el.dom, {
38208 unselectable: "on",
38209 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38211 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38212 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38216 this.titleEl.enableDisplayMode();
38217 /** This region's title text element
38218 * @type HTMLElement */
38219 this.titleTextEl = this.titleEl.dom.firstChild;
38220 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38222 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38223 this.closeBtn.enableDisplayMode();
38224 this.closeBtn.on("click", this.closeClicked, this);
38225 this.closeBtn.hide();
38227 this.createBody(this.config);
38228 if(this.config.hideWhenEmpty){
38230 this.on("paneladded", this.validateVisibility, this);
38231 this.on("panelremoved", this.validateVisibility, this);
38233 if(this.autoScroll){
38234 this.bodyEl.setStyle("overflow", "auto");
38236 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38238 //if(c.titlebar !== false){
38239 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38240 this.titleEl.hide();
38242 this.titleEl.show();
38243 if(this.config.title){
38244 this.titleTextEl.innerHTML = this.config.title;
38248 if(this.config.collapsed){
38249 this.collapse(true);
38251 if(this.config.hidden){
38255 if (this.unrendered_panels && this.unrendered_panels.length) {
38256 for (var i =0;i< this.unrendered_panels.length; i++) {
38257 this.add(this.unrendered_panels[i]);
38259 this.unrendered_panels = null;
38265 applyConfig : function(c)
38268 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38269 var dh = Roo.DomHelper;
38270 if(c.titlebar !== false){
38271 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38272 this.collapseBtn.on("click", this.collapse, this);
38273 this.collapseBtn.enableDisplayMode();
38275 if(c.showPin === true || this.showPin){
38276 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38277 this.stickBtn.enableDisplayMode();
38278 this.stickBtn.on("click", this.expand, this);
38279 this.stickBtn.hide();
38284 /** This region's collapsed element
38285 * @type Roo.Element */
38288 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38289 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38292 if(c.floatable !== false){
38293 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38294 this.collapsedEl.on("click", this.collapseClick, this);
38297 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38298 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38299 id: "message", unselectable: "on", style:{"float":"left"}});
38300 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38302 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38303 this.expandBtn.on("click", this.expand, this);
38307 if(this.collapseBtn){
38308 this.collapseBtn.setVisible(c.collapsible == true);
38311 this.cmargins = c.cmargins || this.cmargins ||
38312 (this.position == "west" || this.position == "east" ?
38313 {top: 0, left: 2, right:2, bottom: 0} :
38314 {top: 2, left: 0, right:0, bottom: 2});
38316 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38319 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38321 this.autoScroll = c.autoScroll || false;
38326 this.duration = c.duration || .30;
38327 this.slideDuration = c.slideDuration || .45;
38332 * Returns true if this region is currently visible.
38333 * @return {Boolean}
38335 isVisible : function(){
38336 return this.visible;
38340 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38341 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38343 //setCollapsedTitle : function(title){
38344 // title = title || " ";
38345 // if(this.collapsedTitleTextEl){
38346 // this.collapsedTitleTextEl.innerHTML = title;
38350 getBox : function(){
38352 // if(!this.collapsed){
38353 b = this.el.getBox(false, true);
38355 // b = this.collapsedEl.getBox(false, true);
38360 getMargins : function(){
38361 return this.margins;
38362 //return this.collapsed ? this.cmargins : this.margins;
38365 highlight : function(){
38366 this.el.addClass("x-layout-panel-dragover");
38369 unhighlight : function(){
38370 this.el.removeClass("x-layout-panel-dragover");
38373 updateBox : function(box)
38375 if (!this.bodyEl) {
38376 return; // not rendered yet..
38380 if(!this.collapsed){
38381 this.el.dom.style.left = box.x + "px";
38382 this.el.dom.style.top = box.y + "px";
38383 this.updateBody(box.width, box.height);
38385 this.collapsedEl.dom.style.left = box.x + "px";
38386 this.collapsedEl.dom.style.top = box.y + "px";
38387 this.collapsedEl.setSize(box.width, box.height);
38390 this.tabs.autoSizeTabs();
38394 updateBody : function(w, h)
38397 this.el.setWidth(w);
38398 w -= this.el.getBorderWidth("rl");
38399 if(this.config.adjustments){
38400 w += this.config.adjustments[0];
38403 if(h !== null && h > 0){
38404 this.el.setHeight(h);
38405 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38406 h -= this.el.getBorderWidth("tb");
38407 if(this.config.adjustments){
38408 h += this.config.adjustments[1];
38410 this.bodyEl.setHeight(h);
38412 h = this.tabs.syncHeight(h);
38415 if(this.panelSize){
38416 w = w !== null ? w : this.panelSize.width;
38417 h = h !== null ? h : this.panelSize.height;
38419 if(this.activePanel){
38420 var el = this.activePanel.getEl();
38421 w = w !== null ? w : el.getWidth();
38422 h = h !== null ? h : el.getHeight();
38423 this.panelSize = {width: w, height: h};
38424 this.activePanel.setSize(w, h);
38426 if(Roo.isIE && this.tabs){
38427 this.tabs.el.repaint();
38432 * Returns the container element for this region.
38433 * @return {Roo.Element}
38435 getEl : function(){
38440 * Hides this region.
38443 //if(!this.collapsed){
38444 this.el.dom.style.left = "-2000px";
38447 // this.collapsedEl.dom.style.left = "-2000px";
38448 // this.collapsedEl.hide();
38450 this.visible = false;
38451 this.fireEvent("visibilitychange", this, false);
38455 * Shows this region if it was previously hidden.
38458 //if(!this.collapsed){
38461 // this.collapsedEl.show();
38463 this.visible = true;
38464 this.fireEvent("visibilitychange", this, true);
38467 closeClicked : function(){
38468 if(this.activePanel){
38469 this.remove(this.activePanel);
38473 collapseClick : function(e){
38475 e.stopPropagation();
38478 e.stopPropagation();
38484 * Collapses this region.
38485 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38488 collapse : function(skipAnim, skipCheck = false){
38489 if(this.collapsed) {
38493 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38495 this.collapsed = true;
38497 this.split.el.hide();
38499 if(this.config.animate && skipAnim !== true){
38500 this.fireEvent("invalidated", this);
38501 this.animateCollapse();
38503 this.el.setLocation(-20000,-20000);
38505 this.collapsedEl.show();
38506 this.fireEvent("collapsed", this);
38507 this.fireEvent("invalidated", this);
38513 animateCollapse : function(){
38518 * Expands this region if it was previously collapsed.
38519 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38520 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38523 expand : function(e, skipAnim){
38525 e.stopPropagation();
38527 if(!this.collapsed || this.el.hasActiveFx()) {
38531 this.afterSlideIn();
38534 this.collapsed = false;
38535 if(this.config.animate && skipAnim !== true){
38536 this.animateExpand();
38540 this.split.el.show();
38542 this.collapsedEl.setLocation(-2000,-2000);
38543 this.collapsedEl.hide();
38544 this.fireEvent("invalidated", this);
38545 this.fireEvent("expanded", this);
38549 animateExpand : function(){
38553 initTabs : function()
38555 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38557 var ts = new Roo.bootstrap.panel.Tabs({
38558 el: this.bodyEl.dom,
38560 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38561 disableTooltips: this.config.disableTabTips,
38562 toolbar : this.config.toolbar
38565 if(this.config.hideTabs){
38566 ts.stripWrap.setDisplayed(false);
38569 ts.resizeTabs = this.config.resizeTabs === true;
38570 ts.minTabWidth = this.config.minTabWidth || 40;
38571 ts.maxTabWidth = this.config.maxTabWidth || 250;
38572 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38573 ts.monitorResize = false;
38574 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38575 ts.bodyEl.addClass('roo-layout-tabs-body');
38576 this.panels.each(this.initPanelAsTab, this);
38579 initPanelAsTab : function(panel){
38580 var ti = this.tabs.addTab(
38584 this.config.closeOnTab && panel.isClosable(),
38587 if(panel.tabTip !== undefined){
38588 ti.setTooltip(panel.tabTip);
38590 ti.on("activate", function(){
38591 this.setActivePanel(panel);
38594 if(this.config.closeOnTab){
38595 ti.on("beforeclose", function(t, e){
38597 this.remove(panel);
38601 panel.tabItem = ti;
38606 updatePanelTitle : function(panel, title)
38608 if(this.activePanel == panel){
38609 this.updateTitle(title);
38612 var ti = this.tabs.getTab(panel.getEl().id);
38614 if(panel.tabTip !== undefined){
38615 ti.setTooltip(panel.tabTip);
38620 updateTitle : function(title){
38621 if(this.titleTextEl && !this.config.title){
38622 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38626 setActivePanel : function(panel)
38628 panel = this.getPanel(panel);
38629 if(this.activePanel && this.activePanel != panel){
38630 if(this.activePanel.setActiveState(false) === false){
38634 this.activePanel = panel;
38635 panel.setActiveState(true);
38636 if(this.panelSize){
38637 panel.setSize(this.panelSize.width, this.panelSize.height);
38640 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38642 this.updateTitle(panel.getTitle());
38644 this.fireEvent("invalidated", this);
38646 this.fireEvent("panelactivated", this, panel);
38650 * Shows the specified panel.
38651 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38652 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38654 showPanel : function(panel)
38656 panel = this.getPanel(panel);
38659 var tab = this.tabs.getTab(panel.getEl().id);
38660 if(tab.isHidden()){
38661 this.tabs.unhideTab(tab.id);
38665 this.setActivePanel(panel);
38672 * Get the active panel for this region.
38673 * @return {Roo.ContentPanel} The active panel or null
38675 getActivePanel : function(){
38676 return this.activePanel;
38679 validateVisibility : function(){
38680 if(this.panels.getCount() < 1){
38681 this.updateTitle(" ");
38682 this.closeBtn.hide();
38685 if(!this.isVisible()){
38692 * Adds the passed ContentPanel(s) to this region.
38693 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38694 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38696 add : function(panel)
38698 if(arguments.length > 1){
38699 for(var i = 0, len = arguments.length; i < len; i++) {
38700 this.add(arguments[i]);
38705 // if we have not been rendered yet, then we can not really do much of this..
38706 if (!this.bodyEl) {
38707 this.unrendered_panels.push(panel);
38714 if(this.hasPanel(panel)){
38715 this.showPanel(panel);
38718 panel.setRegion(this);
38719 this.panels.add(panel);
38720 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38721 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38722 // and hide them... ???
38723 this.bodyEl.dom.appendChild(panel.getEl().dom);
38724 if(panel.background !== true){
38725 this.setActivePanel(panel);
38727 this.fireEvent("paneladded", this, panel);
38734 this.initPanelAsTab(panel);
38738 if(panel.background !== true){
38739 this.tabs.activate(panel.getEl().id);
38741 this.fireEvent("paneladded", this, panel);
38746 * Hides the tab for the specified panel.
38747 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38749 hidePanel : function(panel){
38750 if(this.tabs && (panel = this.getPanel(panel))){
38751 this.tabs.hideTab(panel.getEl().id);
38756 * Unhides the tab for a previously hidden panel.
38757 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38759 unhidePanel : function(panel){
38760 if(this.tabs && (panel = this.getPanel(panel))){
38761 this.tabs.unhideTab(panel.getEl().id);
38765 clearPanels : function(){
38766 while(this.panels.getCount() > 0){
38767 this.remove(this.panels.first());
38772 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38773 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38774 * @param {Boolean} preservePanel Overrides the config preservePanel option
38775 * @return {Roo.ContentPanel} The panel that was removed
38777 remove : function(panel, preservePanel)
38779 panel = this.getPanel(panel);
38784 this.fireEvent("beforeremove", this, panel, e);
38785 if(e.cancel === true){
38788 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38789 var panelId = panel.getId();
38790 this.panels.removeKey(panelId);
38792 document.body.appendChild(panel.getEl().dom);
38795 this.tabs.removeTab(panel.getEl().id);
38796 }else if (!preservePanel){
38797 this.bodyEl.dom.removeChild(panel.getEl().dom);
38799 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38800 var p = this.panels.first();
38801 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38802 tempEl.appendChild(p.getEl().dom);
38803 this.bodyEl.update("");
38804 this.bodyEl.dom.appendChild(p.getEl().dom);
38806 this.updateTitle(p.getTitle());
38808 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38809 this.setActivePanel(p);
38811 panel.setRegion(null);
38812 if(this.activePanel == panel){
38813 this.activePanel = null;
38815 if(this.config.autoDestroy !== false && preservePanel !== true){
38816 try{panel.destroy();}catch(e){}
38818 this.fireEvent("panelremoved", this, panel);
38823 * Returns the TabPanel component used by this region
38824 * @return {Roo.TabPanel}
38826 getTabs : function(){
38830 createTool : function(parentEl, className){
38831 var btn = Roo.DomHelper.append(parentEl, {
38833 cls: "x-layout-tools-button",
38836 cls: "roo-layout-tools-button-inner " + className,
38840 btn.addClassOnOver("roo-layout-tools-button-over");
38845 * Ext JS Library 1.1.1
38846 * Copyright(c) 2006-2007, Ext JS, LLC.
38848 * Originally Released Under LGPL - original licence link has changed is not relivant.
38851 * <script type="text/javascript">
38857 * @class Roo.SplitLayoutRegion
38858 * @extends Roo.LayoutRegion
38859 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38861 Roo.bootstrap.layout.Split = function(config){
38862 this.cursor = config.cursor;
38863 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38866 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38868 splitTip : "Drag to resize.",
38869 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38870 useSplitTips : false,
38872 applyConfig : function(config){
38873 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38876 onRender : function(ctr,pos) {
38878 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38879 if(!this.config.split){
38884 var splitEl = Roo.DomHelper.append(ctr.dom, {
38886 id: this.el.id + "-split",
38887 cls: "roo-layout-split roo-layout-split-"+this.position,
38890 /** The SplitBar for this region
38891 * @type Roo.SplitBar */
38892 // does not exist yet...
38893 Roo.log([this.position, this.orientation]);
38895 this.split = new Roo.bootstrap.SplitBar({
38896 dragElement : splitEl,
38897 resizingElement: this.el,
38898 orientation : this.orientation
38901 this.split.on("moved", this.onSplitMove, this);
38902 this.split.useShim = this.config.useShim === true;
38903 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38904 if(this.useSplitTips){
38905 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38907 //if(config.collapsible){
38908 // this.split.el.on("dblclick", this.collapse, this);
38911 if(typeof this.config.minSize != "undefined"){
38912 this.split.minSize = this.config.minSize;
38914 if(typeof this.config.maxSize != "undefined"){
38915 this.split.maxSize = this.config.maxSize;
38917 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38918 this.hideSplitter();
38923 getHMaxSize : function(){
38924 var cmax = this.config.maxSize || 10000;
38925 var center = this.mgr.getRegion("center");
38926 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38929 getVMaxSize : function(){
38930 var cmax = this.config.maxSize || 10000;
38931 var center = this.mgr.getRegion("center");
38932 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38935 onSplitMove : function(split, newSize){
38936 this.fireEvent("resized", this, newSize);
38940 * Returns the {@link Roo.SplitBar} for this region.
38941 * @return {Roo.SplitBar}
38943 getSplitBar : function(){
38948 this.hideSplitter();
38949 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38952 hideSplitter : function(){
38954 this.split.el.setLocation(-2000,-2000);
38955 this.split.el.hide();
38961 this.split.el.show();
38963 Roo.bootstrap.layout.Split.superclass.show.call(this);
38966 beforeSlide: function(){
38967 if(Roo.isGecko){// firefox overflow auto bug workaround
38968 this.bodyEl.clip();
38970 this.tabs.bodyEl.clip();
38972 if(this.activePanel){
38973 this.activePanel.getEl().clip();
38975 if(this.activePanel.beforeSlide){
38976 this.activePanel.beforeSlide();
38982 afterSlide : function(){
38983 if(Roo.isGecko){// firefox overflow auto bug workaround
38984 this.bodyEl.unclip();
38986 this.tabs.bodyEl.unclip();
38988 if(this.activePanel){
38989 this.activePanel.getEl().unclip();
38990 if(this.activePanel.afterSlide){
38991 this.activePanel.afterSlide();
38997 initAutoHide : function(){
38998 if(this.autoHide !== false){
38999 if(!this.autoHideHd){
39000 var st = new Roo.util.DelayedTask(this.slideIn, this);
39001 this.autoHideHd = {
39002 "mouseout": function(e){
39003 if(!e.within(this.el, true)){
39007 "mouseover" : function(e){
39013 this.el.on(this.autoHideHd);
39017 clearAutoHide : function(){
39018 if(this.autoHide !== false){
39019 this.el.un("mouseout", this.autoHideHd.mouseout);
39020 this.el.un("mouseover", this.autoHideHd.mouseover);
39024 clearMonitor : function(){
39025 Roo.get(document).un("click", this.slideInIf, this);
39028 // these names are backwards but not changed for compat
39029 slideOut : function(){
39030 if(this.isSlid || this.el.hasActiveFx()){
39033 this.isSlid = true;
39034 if(this.collapseBtn){
39035 this.collapseBtn.hide();
39037 this.closeBtnState = this.closeBtn.getStyle('display');
39038 this.closeBtn.hide();
39040 this.stickBtn.show();
39043 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39044 this.beforeSlide();
39045 this.el.setStyle("z-index", 10001);
39046 this.el.slideIn(this.getSlideAnchor(), {
39047 callback: function(){
39049 this.initAutoHide();
39050 Roo.get(document).on("click", this.slideInIf, this);
39051 this.fireEvent("slideshow", this);
39058 afterSlideIn : function(){
39059 this.clearAutoHide();
39060 this.isSlid = false;
39061 this.clearMonitor();
39062 this.el.setStyle("z-index", "");
39063 if(this.collapseBtn){
39064 this.collapseBtn.show();
39066 this.closeBtn.setStyle('display', this.closeBtnState);
39068 this.stickBtn.hide();
39070 this.fireEvent("slidehide", this);
39073 slideIn : function(cb){
39074 if(!this.isSlid || this.el.hasActiveFx()){
39078 this.isSlid = false;
39079 this.beforeSlide();
39080 this.el.slideOut(this.getSlideAnchor(), {
39081 callback: function(){
39082 this.el.setLeftTop(-10000, -10000);
39084 this.afterSlideIn();
39092 slideInIf : function(e){
39093 if(!e.within(this.el)){
39098 animateCollapse : function(){
39099 this.beforeSlide();
39100 this.el.setStyle("z-index", 20000);
39101 var anchor = this.getSlideAnchor();
39102 this.el.slideOut(anchor, {
39103 callback : function(){
39104 this.el.setStyle("z-index", "");
39105 this.collapsedEl.slideIn(anchor, {duration:.3});
39107 this.el.setLocation(-10000,-10000);
39109 this.fireEvent("collapsed", this);
39116 animateExpand : function(){
39117 this.beforeSlide();
39118 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39119 this.el.setStyle("z-index", 20000);
39120 this.collapsedEl.hide({
39123 this.el.slideIn(this.getSlideAnchor(), {
39124 callback : function(){
39125 this.el.setStyle("z-index", "");
39128 this.split.el.show();
39130 this.fireEvent("invalidated", this);
39131 this.fireEvent("expanded", this);
39159 getAnchor : function(){
39160 return this.anchors[this.position];
39163 getCollapseAnchor : function(){
39164 return this.canchors[this.position];
39167 getSlideAnchor : function(){
39168 return this.sanchors[this.position];
39171 getAlignAdj : function(){
39172 var cm = this.cmargins;
39173 switch(this.position){
39189 getExpandAdj : function(){
39190 var c = this.collapsedEl, cm = this.cmargins;
39191 switch(this.position){
39193 return [-(cm.right+c.getWidth()+cm.left), 0];
39196 return [cm.right+c.getWidth()+cm.left, 0];
39199 return [0, -(cm.top+cm.bottom+c.getHeight())];
39202 return [0, cm.top+cm.bottom+c.getHeight()];
39208 * Ext JS Library 1.1.1
39209 * Copyright(c) 2006-2007, Ext JS, LLC.
39211 * Originally Released Under LGPL - original licence link has changed is not relivant.
39214 * <script type="text/javascript">
39217 * These classes are private internal classes
39219 Roo.bootstrap.layout.Center = function(config){
39220 config.region = "center";
39221 Roo.bootstrap.layout.Region.call(this, config);
39222 this.visible = true;
39223 this.minWidth = config.minWidth || 20;
39224 this.minHeight = config.minHeight || 20;
39227 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39229 // center panel can't be hidden
39233 // center panel can't be hidden
39236 getMinWidth: function(){
39237 return this.minWidth;
39240 getMinHeight: function(){
39241 return this.minHeight;
39255 Roo.bootstrap.layout.North = function(config)
39257 config.region = 'north';
39258 config.cursor = 'n-resize';
39260 Roo.bootstrap.layout.Split.call(this, config);
39264 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39265 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39266 this.split.el.addClass("roo-layout-split-v");
39268 var size = config.initialSize || config.height;
39269 if(typeof size != "undefined"){
39270 this.el.setHeight(size);
39273 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39275 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39279 getBox : function(){
39280 if(this.collapsed){
39281 return this.collapsedEl.getBox();
39283 var box = this.el.getBox();
39285 box.height += this.split.el.getHeight();
39290 updateBox : function(box){
39291 if(this.split && !this.collapsed){
39292 box.height -= this.split.el.getHeight();
39293 this.split.el.setLeft(box.x);
39294 this.split.el.setTop(box.y+box.height);
39295 this.split.el.setWidth(box.width);
39297 if(this.collapsed){
39298 this.updateBody(box.width, null);
39300 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39308 Roo.bootstrap.layout.South = function(config){
39309 config.region = 'south';
39310 config.cursor = 's-resize';
39311 Roo.bootstrap.layout.Split.call(this, config);
39313 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39314 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39315 this.split.el.addClass("roo-layout-split-v");
39317 var size = config.initialSize || config.height;
39318 if(typeof size != "undefined"){
39319 this.el.setHeight(size);
39323 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39324 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39325 getBox : function(){
39326 if(this.collapsed){
39327 return this.collapsedEl.getBox();
39329 var box = this.el.getBox();
39331 var sh = this.split.el.getHeight();
39338 updateBox : function(box){
39339 if(this.split && !this.collapsed){
39340 var sh = this.split.el.getHeight();
39343 this.split.el.setLeft(box.x);
39344 this.split.el.setTop(box.y-sh);
39345 this.split.el.setWidth(box.width);
39347 if(this.collapsed){
39348 this.updateBody(box.width, null);
39350 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39354 Roo.bootstrap.layout.East = function(config){
39355 config.region = "east";
39356 config.cursor = "e-resize";
39357 Roo.bootstrap.layout.Split.call(this, config);
39359 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39360 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39361 this.split.el.addClass("roo-layout-split-h");
39363 var size = config.initialSize || config.width;
39364 if(typeof size != "undefined"){
39365 this.el.setWidth(size);
39368 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39369 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39370 getBox : function(){
39371 if(this.collapsed){
39372 return this.collapsedEl.getBox();
39374 var box = this.el.getBox();
39376 var sw = this.split.el.getWidth();
39383 updateBox : function(box){
39384 if(this.split && !this.collapsed){
39385 var sw = this.split.el.getWidth();
39387 this.split.el.setLeft(box.x);
39388 this.split.el.setTop(box.y);
39389 this.split.el.setHeight(box.height);
39392 if(this.collapsed){
39393 this.updateBody(null, box.height);
39395 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39399 Roo.bootstrap.layout.West = function(config){
39400 config.region = "west";
39401 config.cursor = "w-resize";
39403 Roo.bootstrap.layout.Split.call(this, config);
39405 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39406 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39407 this.split.el.addClass("roo-layout-split-h");
39411 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39412 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39414 onRender: function(ctr, pos)
39416 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39417 var size = this.config.initialSize || this.config.width;
39418 if(typeof size != "undefined"){
39419 this.el.setWidth(size);
39423 getBox : function(){
39424 if(this.collapsed){
39425 return this.collapsedEl.getBox();
39427 var box = this.el.getBox();
39429 box.width += this.split.el.getWidth();
39434 updateBox : function(box){
39435 if(this.split && !this.collapsed){
39436 var sw = this.split.el.getWidth();
39438 this.split.el.setLeft(box.x+box.width);
39439 this.split.el.setTop(box.y);
39440 this.split.el.setHeight(box.height);
39442 if(this.collapsed){
39443 this.updateBody(null, box.height);
39445 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39447 });Roo.namespace("Roo.bootstrap.panel");/*
39449 * Ext JS Library 1.1.1
39450 * Copyright(c) 2006-2007, Ext JS, LLC.
39452 * Originally Released Under LGPL - original licence link has changed is not relivant.
39455 * <script type="text/javascript">
39458 * @class Roo.ContentPanel
39459 * @extends Roo.util.Observable
39460 * A basic ContentPanel element.
39461 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39462 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39463 * @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
39464 * @cfg {Boolean} closable True if the panel can be closed/removed
39465 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39466 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39467 * @cfg {Toolbar} toolbar A toolbar for this panel
39468 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39469 * @cfg {String} title The title for this panel
39470 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39471 * @cfg {String} url Calls {@link #setUrl} with this value
39472 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39473 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39474 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39475 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39476 * @cfg {Boolean} badges render the badges
39477 * @cfg {String} cls extra classes to use
39478 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39481 * Create a new ContentPanel.
39482 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39483 * @param {String/Object} config A string to set only the title or a config object
39484 * @param {String} content (optional) Set the HTML content for this panel
39485 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39487 Roo.bootstrap.panel.Content = function( config){
39489 this.tpl = config.tpl || false;
39491 var el = config.el;
39492 var content = config.content;
39494 if(config.autoCreate){ // xtype is available if this is called from factory
39497 this.el = Roo.get(el);
39498 if(!this.el && config && config.autoCreate){
39499 if(typeof config.autoCreate == "object"){
39500 if(!config.autoCreate.id){
39501 config.autoCreate.id = config.id||el;
39503 this.el = Roo.DomHelper.append(document.body,
39504 config.autoCreate, true);
39508 cls: (config.cls || '') +
39509 (config.background ? ' bg-' + config.background : '') +
39510 " roo-layout-inactive-content",
39514 elcfg.html = config.html;
39518 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39521 this.closable = false;
39522 this.loaded = false;
39523 this.active = false;
39526 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39528 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39530 this.wrapEl = this.el; //this.el.wrap();
39532 if (config.toolbar.items) {
39533 ti = config.toolbar.items ;
39534 delete config.toolbar.items ;
39538 this.toolbar.render(this.wrapEl, 'before');
39539 for(var i =0;i < ti.length;i++) {
39540 // Roo.log(['add child', items[i]]);
39541 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39543 this.toolbar.items = nitems;
39544 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39545 delete config.toolbar;
39549 // xtype created footer. - not sure if will work as we normally have to render first..
39550 if (this.footer && !this.footer.el && this.footer.xtype) {
39551 if (!this.wrapEl) {
39552 this.wrapEl = this.el.wrap();
39555 this.footer.container = this.wrapEl.createChild();
39557 this.footer = Roo.factory(this.footer, Roo);
39562 if(typeof config == "string"){
39563 this.title = config;
39565 Roo.apply(this, config);
39569 this.resizeEl = Roo.get(this.resizeEl, true);
39571 this.resizeEl = this.el;
39573 // handle view.xtype
39581 * Fires when this panel is activated.
39582 * @param {Roo.ContentPanel} this
39586 * @event deactivate
39587 * Fires when this panel is activated.
39588 * @param {Roo.ContentPanel} this
39590 "deactivate" : true,
39594 * Fires when this panel is resized if fitToFrame is true.
39595 * @param {Roo.ContentPanel} this
39596 * @param {Number} width The width after any component adjustments
39597 * @param {Number} height The height after any component adjustments
39603 * Fires when this tab is created
39604 * @param {Roo.ContentPanel} this
39615 if(this.autoScroll){
39616 this.resizeEl.setStyle("overflow", "auto");
39618 // fix randome scrolling
39619 //this.el.on('scroll', function() {
39620 // Roo.log('fix random scolling');
39621 // this.scrollTo('top',0);
39624 content = content || this.content;
39626 this.setContent(content);
39628 if(config && config.url){
39629 this.setUrl(this.url, this.params, this.loadOnce);
39634 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39636 if (this.view && typeof(this.view.xtype) != 'undefined') {
39637 this.view.el = this.el.appendChild(document.createElement("div"));
39638 this.view = Roo.factory(this.view);
39639 this.view.render && this.view.render(false, '');
39643 this.fireEvent('render', this);
39646 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39653 setRegion : function(region){
39654 this.region = region;
39655 this.setActiveClass(region && !this.background);
39659 setActiveClass: function(state)
39662 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39663 this.el.setStyle('position','relative');
39665 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39666 this.el.setStyle('position', 'absolute');
39671 * Returns the toolbar for this Panel if one was configured.
39672 * @return {Roo.Toolbar}
39674 getToolbar : function(){
39675 return this.toolbar;
39678 setActiveState : function(active)
39680 this.active = active;
39681 this.setActiveClass(active);
39683 if(this.fireEvent("deactivate", this) === false){
39688 this.fireEvent("activate", this);
39692 * Updates this panel's element
39693 * @param {String} content The new content
39694 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39696 setContent : function(content, loadScripts){
39697 this.el.update(content, loadScripts);
39700 ignoreResize : function(w, h){
39701 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39704 this.lastSize = {width: w, height: h};
39709 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39710 * @return {Roo.UpdateManager} The UpdateManager
39712 getUpdateManager : function(){
39713 return this.el.getUpdateManager();
39716 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39717 * @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:
39720 url: "your-url.php",
39721 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39722 callback: yourFunction,
39723 scope: yourObject, //(optional scope)
39726 text: "Loading...",
39731 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39732 * 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.
39733 * @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}
39734 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39735 * @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.
39736 * @return {Roo.ContentPanel} this
39739 var um = this.el.getUpdateManager();
39740 um.update.apply(um, arguments);
39746 * 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.
39747 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39748 * @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)
39749 * @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)
39750 * @return {Roo.UpdateManager} The UpdateManager
39752 setUrl : function(url, params, loadOnce){
39753 if(this.refreshDelegate){
39754 this.removeListener("activate", this.refreshDelegate);
39756 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39757 this.on("activate", this.refreshDelegate);
39758 return this.el.getUpdateManager();
39761 _handleRefresh : function(url, params, loadOnce){
39762 if(!loadOnce || !this.loaded){
39763 var updater = this.el.getUpdateManager();
39764 updater.update(url, params, this._setLoaded.createDelegate(this));
39768 _setLoaded : function(){
39769 this.loaded = true;
39773 * Returns this panel's id
39776 getId : function(){
39781 * Returns this panel's element - used by regiosn to add.
39782 * @return {Roo.Element}
39784 getEl : function(){
39785 return this.wrapEl || this.el;
39790 adjustForComponents : function(width, height)
39792 //Roo.log('adjustForComponents ');
39793 if(this.resizeEl != this.el){
39794 width -= this.el.getFrameWidth('lr');
39795 height -= this.el.getFrameWidth('tb');
39798 var te = this.toolbar.getEl();
39799 te.setWidth(width);
39800 height -= te.getHeight();
39803 var te = this.footer.getEl();
39804 te.setWidth(width);
39805 height -= te.getHeight();
39809 if(this.adjustments){
39810 width += this.adjustments[0];
39811 height += this.adjustments[1];
39813 return {"width": width, "height": height};
39816 setSize : function(width, height){
39817 if(this.fitToFrame && !this.ignoreResize(width, height)){
39818 if(this.fitContainer && this.resizeEl != this.el){
39819 this.el.setSize(width, height);
39821 var size = this.adjustForComponents(width, height);
39822 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39823 this.fireEvent('resize', this, size.width, size.height);
39828 * Returns this panel's title
39831 getTitle : function(){
39833 if (typeof(this.title) != 'object') {
39838 for (var k in this.title) {
39839 if (!this.title.hasOwnProperty(k)) {
39843 if (k.indexOf('-') >= 0) {
39844 var s = k.split('-');
39845 for (var i = 0; i<s.length; i++) {
39846 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39849 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39856 * Set this panel's title
39857 * @param {String} title
39859 setTitle : function(title){
39860 this.title = title;
39862 this.region.updatePanelTitle(this, title);
39867 * Returns true is this panel was configured to be closable
39868 * @return {Boolean}
39870 isClosable : function(){
39871 return this.closable;
39874 beforeSlide : function(){
39876 this.resizeEl.clip();
39879 afterSlide : function(){
39881 this.resizeEl.unclip();
39885 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39886 * Will fail silently if the {@link #setUrl} method has not been called.
39887 * This does not activate the panel, just updates its content.
39889 refresh : function(){
39890 if(this.refreshDelegate){
39891 this.loaded = false;
39892 this.refreshDelegate();
39897 * Destroys this panel
39899 destroy : function(){
39900 this.el.removeAllListeners();
39901 var tempEl = document.createElement("span");
39902 tempEl.appendChild(this.el.dom);
39903 tempEl.innerHTML = "";
39909 * form - if the content panel contains a form - this is a reference to it.
39910 * @type {Roo.form.Form}
39914 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39915 * This contains a reference to it.
39921 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39931 * @param {Object} cfg Xtype definition of item to add.
39935 getChildContainer: function () {
39936 return this.getEl();
39941 var ret = new Roo.factory(cfg);
39946 if (cfg.xtype.match(/^Form$/)) {
39949 //if (this.footer) {
39950 // el = this.footer.container.insertSibling(false, 'before');
39952 el = this.el.createChild();
39955 this.form = new Roo.form.Form(cfg);
39958 if ( this.form.allItems.length) {
39959 this.form.render(el.dom);
39963 // should only have one of theses..
39964 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39965 // views.. should not be just added - used named prop 'view''
39967 cfg.el = this.el.appendChild(document.createElement("div"));
39970 var ret = new Roo.factory(cfg);
39972 ret.render && ret.render(false, ''); // render blank..
39982 * @class Roo.bootstrap.panel.Grid
39983 * @extends Roo.bootstrap.panel.Content
39985 * Create a new GridPanel.
39986 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39987 * @param {Object} config A the config object
39993 Roo.bootstrap.panel.Grid = function(config)
39997 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39998 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40000 config.el = this.wrapper;
40001 //this.el = this.wrapper;
40003 if (config.container) {
40004 // ctor'ed from a Border/panel.grid
40007 this.wrapper.setStyle("overflow", "hidden");
40008 this.wrapper.addClass('roo-grid-container');
40013 if(config.toolbar){
40014 var tool_el = this.wrapper.createChild();
40015 this.toolbar = Roo.factory(config.toolbar);
40017 if (config.toolbar.items) {
40018 ti = config.toolbar.items ;
40019 delete config.toolbar.items ;
40023 this.toolbar.render(tool_el);
40024 for(var i =0;i < ti.length;i++) {
40025 // Roo.log(['add child', items[i]]);
40026 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40028 this.toolbar.items = nitems;
40030 delete config.toolbar;
40033 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40034 config.grid.scrollBody = true;;
40035 config.grid.monitorWindowResize = false; // turn off autosizing
40036 config.grid.autoHeight = false;
40037 config.grid.autoWidth = false;
40039 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40041 if (config.background) {
40042 // render grid on panel activation (if panel background)
40043 this.on('activate', function(gp) {
40044 if (!gp.grid.rendered) {
40045 gp.grid.render(this.wrapper);
40046 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40051 this.grid.render(this.wrapper);
40052 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40055 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40056 // ??? needed ??? config.el = this.wrapper;
40061 // xtype created footer. - not sure if will work as we normally have to render first..
40062 if (this.footer && !this.footer.el && this.footer.xtype) {
40064 var ctr = this.grid.getView().getFooterPanel(true);
40065 this.footer.dataSource = this.grid.dataSource;
40066 this.footer = Roo.factory(this.footer, Roo);
40067 this.footer.render(ctr);
40077 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40078 getId : function(){
40079 return this.grid.id;
40083 * Returns the grid for this panel
40084 * @return {Roo.bootstrap.Table}
40086 getGrid : function(){
40090 setSize : function(width, height){
40091 if(!this.ignoreResize(width, height)){
40092 var grid = this.grid;
40093 var size = this.adjustForComponents(width, height);
40094 // tfoot is not a footer?
40097 var gridel = grid.getGridEl();
40098 gridel.setSize(size.width, size.height);
40100 var tbd = grid.getGridEl().select('tbody', true).first();
40101 var thd = grid.getGridEl().select('thead',true).first();
40102 var tbf= grid.getGridEl().select('tfoot', true).first();
40105 size.height -= thd.getHeight();
40108 size.height -= thd.getHeight();
40111 tbd.setSize(size.width, size.height );
40112 // this is for the account management tab -seems to work there.
40113 var thd = grid.getGridEl().select('thead',true).first();
40115 // tbd.setSize(size.width, size.height - thd.getHeight());
40124 beforeSlide : function(){
40125 this.grid.getView().scroller.clip();
40128 afterSlide : function(){
40129 this.grid.getView().scroller.unclip();
40132 destroy : function(){
40133 this.grid.destroy();
40135 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40140 * @class Roo.bootstrap.panel.Nest
40141 * @extends Roo.bootstrap.panel.Content
40143 * Create a new Panel, that can contain a layout.Border.
40146 * @param {Roo.BorderLayout} layout The layout for this panel
40147 * @param {String/Object} config A string to set only the title or a config object
40149 Roo.bootstrap.panel.Nest = function(config)
40151 // construct with only one argument..
40152 /* FIXME - implement nicer consturctors
40153 if (layout.layout) {
40155 layout = config.layout;
40156 delete config.layout;
40158 if (layout.xtype && !layout.getEl) {
40159 // then layout needs constructing..
40160 layout = Roo.factory(layout, Roo);
40164 config.el = config.layout.getEl();
40166 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40168 config.layout.monitorWindowResize = false; // turn off autosizing
40169 this.layout = config.layout;
40170 this.layout.getEl().addClass("roo-layout-nested-layout");
40171 this.layout.parent = this;
40178 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40180 setSize : function(width, height){
40181 if(!this.ignoreResize(width, height)){
40182 var size = this.adjustForComponents(width, height);
40183 var el = this.layout.getEl();
40184 if (size.height < 1) {
40185 el.setWidth(size.width);
40187 el.setSize(size.width, size.height);
40189 var touch = el.dom.offsetWidth;
40190 this.layout.layout();
40191 // ie requires a double layout on the first pass
40192 if(Roo.isIE && !this.initialized){
40193 this.initialized = true;
40194 this.layout.layout();
40199 // activate all subpanels if not currently active..
40201 setActiveState : function(active){
40202 this.active = active;
40203 this.setActiveClass(active);
40206 this.fireEvent("deactivate", this);
40210 this.fireEvent("activate", this);
40211 // not sure if this should happen before or after..
40212 if (!this.layout) {
40213 return; // should not happen..
40216 for (var r in this.layout.regions) {
40217 reg = this.layout.getRegion(r);
40218 if (reg.getActivePanel()) {
40219 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40220 reg.setActivePanel(reg.getActivePanel());
40223 if (!reg.panels.length) {
40226 reg.showPanel(reg.getPanel(0));
40235 * Returns the nested BorderLayout for this panel
40236 * @return {Roo.BorderLayout}
40238 getLayout : function(){
40239 return this.layout;
40243 * Adds a xtype elements to the layout of the nested panel
40247 xtype : 'ContentPanel',
40254 xtype : 'NestedLayoutPanel',
40260 items : [ ... list of content panels or nested layout panels.. ]
40264 * @param {Object} cfg Xtype definition of item to add.
40266 addxtype : function(cfg) {
40267 return this.layout.addxtype(cfg);
40272 * Ext JS Library 1.1.1
40273 * Copyright(c) 2006-2007, Ext JS, LLC.
40275 * Originally Released Under LGPL - original licence link has changed is not relivant.
40278 * <script type="text/javascript">
40281 * @class Roo.TabPanel
40282 * @extends Roo.util.Observable
40283 * A lightweight tab container.
40287 // basic tabs 1, built from existing content
40288 var tabs = new Roo.TabPanel("tabs1");
40289 tabs.addTab("script", "View Script");
40290 tabs.addTab("markup", "View Markup");
40291 tabs.activate("script");
40293 // more advanced tabs, built from javascript
40294 var jtabs = new Roo.TabPanel("jtabs");
40295 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40297 // set up the UpdateManager
40298 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40299 var updater = tab2.getUpdateManager();
40300 updater.setDefaultUrl("ajax1.htm");
40301 tab2.on('activate', updater.refresh, updater, true);
40303 // Use setUrl for Ajax loading
40304 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40305 tab3.setUrl("ajax2.htm", null, true);
40308 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40311 jtabs.activate("jtabs-1");
40314 * Create a new TabPanel.
40315 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40316 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40318 Roo.bootstrap.panel.Tabs = function(config){
40320 * The container element for this TabPanel.
40321 * @type Roo.Element
40323 this.el = Roo.get(config.el);
40326 if(typeof config == "boolean"){
40327 this.tabPosition = config ? "bottom" : "top";
40329 Roo.apply(this, config);
40333 if(this.tabPosition == "bottom"){
40334 // if tabs are at the bottom = create the body first.
40335 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40336 this.el.addClass("roo-tabs-bottom");
40338 // next create the tabs holders
40340 if (this.tabPosition == "west"){
40342 var reg = this.region; // fake it..
40344 if (!reg.mgr.parent) {
40347 reg = reg.mgr.parent.region;
40349 Roo.log("got nest?");
40351 if (reg.mgr.getRegion('west')) {
40352 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40353 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40354 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40355 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40356 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40364 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40365 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40366 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40367 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40372 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40375 // finally - if tabs are at the top, then create the body last..
40376 if(this.tabPosition != "bottom"){
40377 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40378 * @type Roo.Element
40380 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40381 this.el.addClass("roo-tabs-top");
40385 this.bodyEl.setStyle("position", "relative");
40387 this.active = null;
40388 this.activateDelegate = this.activate.createDelegate(this);
40393 * Fires when the active tab changes
40394 * @param {Roo.TabPanel} this
40395 * @param {Roo.TabPanelItem} activePanel The new active tab
40399 * @event beforetabchange
40400 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40401 * @param {Roo.TabPanel} this
40402 * @param {Object} e Set cancel to true on this object to cancel the tab change
40403 * @param {Roo.TabPanelItem} tab The tab being changed to
40405 "beforetabchange" : true
40408 Roo.EventManager.onWindowResize(this.onResize, this);
40409 this.cpad = this.el.getPadding("lr");
40410 this.hiddenCount = 0;
40413 // toolbar on the tabbar support...
40414 if (this.toolbar) {
40415 alert("no toolbar support yet");
40416 this.toolbar = false;
40418 var tcfg = this.toolbar;
40419 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40420 this.toolbar = new Roo.Toolbar(tcfg);
40421 if (Roo.isSafari) {
40422 var tbl = tcfg.container.child('table', true);
40423 tbl.setAttribute('width', '100%');
40431 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40434 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40436 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40438 tabPosition : "top",
40440 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40442 currentTabWidth : 0,
40444 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40448 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40452 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40454 preferredTabWidth : 175,
40456 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40458 resizeTabs : false,
40460 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40462 monitorResize : true,
40464 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40466 toolbar : false, // set by caller..
40468 region : false, /// set by caller
40470 disableTooltips : true, // not used yet...
40473 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40474 * @param {String} id The id of the div to use <b>or create</b>
40475 * @param {String} text The text for the tab
40476 * @param {String} content (optional) Content to put in the TabPanelItem body
40477 * @param {Boolean} closable (optional) True to create a close icon on the tab
40478 * @return {Roo.TabPanelItem} The created TabPanelItem
40480 addTab : function(id, text, content, closable, tpl)
40482 var item = new Roo.bootstrap.panel.TabItem({
40486 closable : closable,
40489 this.addTabItem(item);
40491 item.setContent(content);
40497 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40498 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40499 * @return {Roo.TabPanelItem}
40501 getTab : function(id){
40502 return this.items[id];
40506 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40507 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40509 hideTab : function(id){
40510 var t = this.items[id];
40513 this.hiddenCount++;
40514 this.autoSizeTabs();
40519 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40520 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40522 unhideTab : function(id){
40523 var t = this.items[id];
40525 t.setHidden(false);
40526 this.hiddenCount--;
40527 this.autoSizeTabs();
40532 * Adds an existing {@link Roo.TabPanelItem}.
40533 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40535 addTabItem : function(item)
40537 this.items[item.id] = item;
40538 this.items.push(item);
40539 this.autoSizeTabs();
40540 // if(this.resizeTabs){
40541 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40542 // this.autoSizeTabs();
40544 // item.autoSize();
40549 * Removes a {@link Roo.TabPanelItem}.
40550 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40552 removeTab : function(id){
40553 var items = this.items;
40554 var tab = items[id];
40555 if(!tab) { return; }
40556 var index = items.indexOf(tab);
40557 if(this.active == tab && items.length > 1){
40558 var newTab = this.getNextAvailable(index);
40563 this.stripEl.dom.removeChild(tab.pnode.dom);
40564 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40565 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40567 items.splice(index, 1);
40568 delete this.items[tab.id];
40569 tab.fireEvent("close", tab);
40570 tab.purgeListeners();
40571 this.autoSizeTabs();
40574 getNextAvailable : function(start){
40575 var items = this.items;
40577 // look for a next tab that will slide over to
40578 // replace the one being removed
40579 while(index < items.length){
40580 var item = items[++index];
40581 if(item && !item.isHidden()){
40585 // if one isn't found select the previous tab (on the left)
40588 var item = items[--index];
40589 if(item && !item.isHidden()){
40597 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40598 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40600 disableTab : function(id){
40601 var tab = this.items[id];
40602 if(tab && this.active != tab){
40608 * Enables a {@link Roo.TabPanelItem} that is disabled.
40609 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40611 enableTab : function(id){
40612 var tab = this.items[id];
40617 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40618 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40619 * @return {Roo.TabPanelItem} The TabPanelItem.
40621 activate : function(id)
40623 //Roo.log('activite:' + id);
40625 var tab = this.items[id];
40629 if(tab == this.active || tab.disabled){
40633 this.fireEvent("beforetabchange", this, e, tab);
40634 if(e.cancel !== true && !tab.disabled){
40636 this.active.hide();
40638 this.active = this.items[id];
40639 this.active.show();
40640 this.fireEvent("tabchange", this, this.active);
40646 * Gets the active {@link Roo.TabPanelItem}.
40647 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40649 getActiveTab : function(){
40650 return this.active;
40654 * Updates the tab body element to fit the height of the container element
40655 * for overflow scrolling
40656 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40658 syncHeight : function(targetHeight){
40659 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40660 var bm = this.bodyEl.getMargins();
40661 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40662 this.bodyEl.setHeight(newHeight);
40666 onResize : function(){
40667 if(this.monitorResize){
40668 this.autoSizeTabs();
40673 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40675 beginUpdate : function(){
40676 this.updating = true;
40680 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40682 endUpdate : function(){
40683 this.updating = false;
40684 this.autoSizeTabs();
40688 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40690 autoSizeTabs : function()
40692 var count = this.items.length;
40693 var vcount = count - this.hiddenCount;
40696 this.stripEl.hide();
40698 this.stripEl.show();
40701 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40706 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40707 var availWidth = Math.floor(w / vcount);
40708 var b = this.stripBody;
40709 if(b.getWidth() > w){
40710 var tabs = this.items;
40711 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40712 if(availWidth < this.minTabWidth){
40713 /*if(!this.sleft){ // incomplete scrolling code
40714 this.createScrollButtons();
40717 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40720 if(this.currentTabWidth < this.preferredTabWidth){
40721 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40727 * Returns the number of tabs in this TabPanel.
40730 getCount : function(){
40731 return this.items.length;
40735 * Resizes all the tabs to the passed width
40736 * @param {Number} The new width
40738 setTabWidth : function(width){
40739 this.currentTabWidth = width;
40740 for(var i = 0, len = this.items.length; i < len; i++) {
40741 if(!this.items[i].isHidden()) {
40742 this.items[i].setWidth(width);
40748 * Destroys this TabPanel
40749 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40751 destroy : function(removeEl){
40752 Roo.EventManager.removeResizeListener(this.onResize, this);
40753 for(var i = 0, len = this.items.length; i < len; i++){
40754 this.items[i].purgeListeners();
40756 if(removeEl === true){
40757 this.el.update("");
40762 createStrip : function(container)
40764 var strip = document.createElement("nav");
40765 strip.className = Roo.bootstrap.version == 4 ?
40766 "navbar-light bg-light" :
40767 "navbar navbar-default"; //"x-tabs-wrap";
40768 container.appendChild(strip);
40772 createStripList : function(strip)
40774 // div wrapper for retard IE
40775 // returns the "tr" element.
40776 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40777 //'<div class="x-tabs-strip-wrap">'+
40778 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40779 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40780 return strip.firstChild; //.firstChild.firstChild.firstChild;
40782 createBody : function(container)
40784 var body = document.createElement("div");
40785 Roo.id(body, "tab-body");
40786 //Roo.fly(body).addClass("x-tabs-body");
40787 Roo.fly(body).addClass("tab-content");
40788 container.appendChild(body);
40791 createItemBody :function(bodyEl, id){
40792 var body = Roo.getDom(id);
40794 body = document.createElement("div");
40797 //Roo.fly(body).addClass("x-tabs-item-body");
40798 Roo.fly(body).addClass("tab-pane");
40799 bodyEl.insertBefore(body, bodyEl.firstChild);
40803 createStripElements : function(stripEl, text, closable, tpl)
40805 var td = document.createElement("li"); // was td..
40806 td.className = 'nav-item';
40808 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40811 stripEl.appendChild(td);
40813 td.className = "x-tabs-closable";
40814 if(!this.closeTpl){
40815 this.closeTpl = new Roo.Template(
40816 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40817 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40818 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40821 var el = this.closeTpl.overwrite(td, {"text": text});
40822 var close = el.getElementsByTagName("div")[0];
40823 var inner = el.getElementsByTagName("em")[0];
40824 return {"el": el, "close": close, "inner": inner};
40827 // not sure what this is..
40828 // if(!this.tabTpl){
40829 //this.tabTpl = new Roo.Template(
40830 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40831 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40833 // this.tabTpl = new Roo.Template(
40834 // '<a href="#">' +
40835 // '<span unselectable="on"' +
40836 // (this.disableTooltips ? '' : ' title="{text}"') +
40837 // ' >{text}</span></a>'
40843 var template = tpl || this.tabTpl || false;
40846 template = new Roo.Template(
40847 Roo.bootstrap.version == 4 ?
40849 '<a class="nav-link" href="#" unselectable="on"' +
40850 (this.disableTooltips ? '' : ' title="{text}"') +
40853 '<a class="nav-link" href="#">' +
40854 '<span unselectable="on"' +
40855 (this.disableTooltips ? '' : ' title="{text}"') +
40856 ' >{text}</span></a>'
40861 switch (typeof(template)) {
40865 template = new Roo.Template(template);
40871 var el = template.overwrite(td, {"text": text});
40873 var inner = el.getElementsByTagName("span")[0];
40875 return {"el": el, "inner": inner};
40883 * @class Roo.TabPanelItem
40884 * @extends Roo.util.Observable
40885 * Represents an individual item (tab plus body) in a TabPanel.
40886 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40887 * @param {String} id The id of this TabPanelItem
40888 * @param {String} text The text for the tab of this TabPanelItem
40889 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40891 Roo.bootstrap.panel.TabItem = function(config){
40893 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40894 * @type Roo.TabPanel
40896 this.tabPanel = config.panel;
40898 * The id for this TabPanelItem
40901 this.id = config.id;
40903 this.disabled = false;
40905 this.text = config.text;
40907 this.loaded = false;
40908 this.closable = config.closable;
40911 * The body element for this TabPanelItem.
40912 * @type Roo.Element
40914 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40915 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40916 this.bodyEl.setStyle("display", "block");
40917 this.bodyEl.setStyle("zoom", "1");
40918 //this.hideAction();
40920 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40922 this.el = Roo.get(els.el);
40923 this.inner = Roo.get(els.inner, true);
40924 this.textEl = Roo.bootstrap.version == 4 ?
40925 this.el : Roo.get(this.el.dom.firstChild, true);
40927 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40928 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40931 // this.el.on("mousedown", this.onTabMouseDown, this);
40932 this.el.on("click", this.onTabClick, this);
40934 if(config.closable){
40935 var c = Roo.get(els.close, true);
40936 c.dom.title = this.closeText;
40937 c.addClassOnOver("close-over");
40938 c.on("click", this.closeClick, this);
40944 * Fires when this tab becomes the active tab.
40945 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40946 * @param {Roo.TabPanelItem} this
40950 * @event beforeclose
40951 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40952 * @param {Roo.TabPanelItem} this
40953 * @param {Object} e Set cancel to true on this object to cancel the close.
40955 "beforeclose": true,
40958 * Fires when this tab is closed.
40959 * @param {Roo.TabPanelItem} this
40963 * @event deactivate
40964 * Fires when this tab is no longer the active tab.
40965 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40966 * @param {Roo.TabPanelItem} this
40968 "deactivate" : true
40970 this.hidden = false;
40972 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40975 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40977 purgeListeners : function(){
40978 Roo.util.Observable.prototype.purgeListeners.call(this);
40979 this.el.removeAllListeners();
40982 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40985 this.status_node.addClass("active");
40988 this.tabPanel.stripWrap.repaint();
40990 this.fireEvent("activate", this.tabPanel, this);
40994 * Returns true if this tab is the active tab.
40995 * @return {Boolean}
40997 isActive : function(){
40998 return this.tabPanel.getActiveTab() == this;
41002 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41005 this.status_node.removeClass("active");
41007 this.fireEvent("deactivate", this.tabPanel, this);
41010 hideAction : function(){
41011 this.bodyEl.hide();
41012 this.bodyEl.setStyle("position", "absolute");
41013 this.bodyEl.setLeft("-20000px");
41014 this.bodyEl.setTop("-20000px");
41017 showAction : function(){
41018 this.bodyEl.setStyle("position", "relative");
41019 this.bodyEl.setTop("");
41020 this.bodyEl.setLeft("");
41021 this.bodyEl.show();
41025 * Set the tooltip for the tab.
41026 * @param {String} tooltip The tab's tooltip
41028 setTooltip : function(text){
41029 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41030 this.textEl.dom.qtip = text;
41031 this.textEl.dom.removeAttribute('title');
41033 this.textEl.dom.title = text;
41037 onTabClick : function(e){
41038 e.preventDefault();
41039 this.tabPanel.activate(this.id);
41042 onTabMouseDown : function(e){
41043 e.preventDefault();
41044 this.tabPanel.activate(this.id);
41047 getWidth : function(){
41048 return this.inner.getWidth();
41051 setWidth : function(width){
41052 var iwidth = width - this.linode.getPadding("lr");
41053 this.inner.setWidth(iwidth);
41054 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41055 this.linode.setWidth(width);
41059 * Show or hide the tab
41060 * @param {Boolean} hidden True to hide or false to show.
41062 setHidden : function(hidden){
41063 this.hidden = hidden;
41064 this.linode.setStyle("display", hidden ? "none" : "");
41068 * Returns true if this tab is "hidden"
41069 * @return {Boolean}
41071 isHidden : function(){
41072 return this.hidden;
41076 * Returns the text for this tab
41079 getText : function(){
41083 autoSize : function(){
41084 //this.el.beginMeasure();
41085 this.textEl.setWidth(1);
41087 * #2804 [new] Tabs in Roojs
41088 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41090 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41091 //this.el.endMeasure();
41095 * Sets the text for the tab (Note: this also sets the tooltip text)
41096 * @param {String} text The tab's text and tooltip
41098 setText : function(text){
41100 this.textEl.update(text);
41101 this.setTooltip(text);
41102 //if(!this.tabPanel.resizeTabs){
41103 // this.autoSize();
41107 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41109 activate : function(){
41110 this.tabPanel.activate(this.id);
41114 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41116 disable : function(){
41117 if(this.tabPanel.active != this){
41118 this.disabled = true;
41119 this.status_node.addClass("disabled");
41124 * Enables this TabPanelItem if it was previously disabled.
41126 enable : function(){
41127 this.disabled = false;
41128 this.status_node.removeClass("disabled");
41132 * Sets the content for this TabPanelItem.
41133 * @param {String} content The content
41134 * @param {Boolean} loadScripts true to look for and load scripts
41136 setContent : function(content, loadScripts){
41137 this.bodyEl.update(content, loadScripts);
41141 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41142 * @return {Roo.UpdateManager} The UpdateManager
41144 getUpdateManager : function(){
41145 return this.bodyEl.getUpdateManager();
41149 * Set a URL to be used to load the content for this TabPanelItem.
41150 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41151 * @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)
41152 * @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)
41153 * @return {Roo.UpdateManager} The UpdateManager
41155 setUrl : function(url, params, loadOnce){
41156 if(this.refreshDelegate){
41157 this.un('activate', this.refreshDelegate);
41159 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41160 this.on("activate", this.refreshDelegate);
41161 return this.bodyEl.getUpdateManager();
41165 _handleRefresh : function(url, params, loadOnce){
41166 if(!loadOnce || !this.loaded){
41167 var updater = this.bodyEl.getUpdateManager();
41168 updater.update(url, params, this._setLoaded.createDelegate(this));
41173 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41174 * Will fail silently if the setUrl method has not been called.
41175 * This does not activate the panel, just updates its content.
41177 refresh : function(){
41178 if(this.refreshDelegate){
41179 this.loaded = false;
41180 this.refreshDelegate();
41185 _setLoaded : function(){
41186 this.loaded = true;
41190 closeClick : function(e){
41193 this.fireEvent("beforeclose", this, o);
41194 if(o.cancel !== true){
41195 this.tabPanel.removeTab(this.id);
41199 * The text displayed in the tooltip for the close icon.
41202 closeText : "Close this tab"
41205 * This script refer to:
41206 * Title: International Telephone Input
41207 * Author: Jack O'Connor
41208 * Code version: v12.1.12
41209 * Availability: https://github.com/jackocnr/intl-tel-input.git
41212 Roo.bootstrap.PhoneInputData = function() {
41215 "Afghanistan (افغانستان)",
41220 "Albania (Shqipëri)",
41225 "Algeria (الجزائر)",
41250 "Antigua and Barbuda",
41260 "Armenia (Հայաստան)",
41276 "Austria (Österreich)",
41281 "Azerbaijan (Azərbaycan)",
41291 "Bahrain (البحرين)",
41296 "Bangladesh (বাংলাদেশ)",
41306 "Belarus (Беларусь)",
41311 "Belgium (België)",
41341 "Bosnia and Herzegovina (Босна и Херцеговина)",
41356 "British Indian Ocean Territory",
41361 "British Virgin Islands",
41371 "Bulgaria (България)",
41381 "Burundi (Uburundi)",
41386 "Cambodia (កម្ពុជា)",
41391 "Cameroon (Cameroun)",
41400 ["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"]
41403 "Cape Verde (Kabu Verdi)",
41408 "Caribbean Netherlands",
41419 "Central African Republic (République centrafricaine)",
41439 "Christmas Island",
41445 "Cocos (Keeling) Islands",
41456 "Comoros (جزر القمر)",
41461 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41466 "Congo (Republic) (Congo-Brazzaville)",
41486 "Croatia (Hrvatska)",
41507 "Czech Republic (Česká republika)",
41512 "Denmark (Danmark)",
41527 "Dominican Republic (República Dominicana)",
41531 ["809", "829", "849"]
41549 "Equatorial Guinea (Guinea Ecuatorial)",
41569 "Falkland Islands (Islas Malvinas)",
41574 "Faroe Islands (Føroyar)",
41595 "French Guiana (Guyane française)",
41600 "French Polynesia (Polynésie française)",
41615 "Georgia (საქართველო)",
41620 "Germany (Deutschland)",
41640 "Greenland (Kalaallit Nunaat)",
41677 "Guinea-Bissau (Guiné Bissau)",
41702 "Hungary (Magyarország)",
41707 "Iceland (Ísland)",
41727 "Iraq (العراق)",
41743 "Israel (ישראל)",
41770 "Jordan (الأردن)",
41775 "Kazakhstan (Казахстан)",
41796 "Kuwait (الكويت)",
41801 "Kyrgyzstan (Кыргызстан)",
41811 "Latvia (Latvija)",
41816 "Lebanon (لبنان)",
41831 "Libya (ليبيا)",
41841 "Lithuania (Lietuva)",
41856 "Macedonia (FYROM) (Македонија)",
41861 "Madagascar (Madagasikara)",
41891 "Marshall Islands",
41901 "Mauritania (موريتانيا)",
41906 "Mauritius (Moris)",
41927 "Moldova (Republica Moldova)",
41937 "Mongolia (Монгол)",
41942 "Montenegro (Crna Gora)",
41952 "Morocco (المغرب)",
41958 "Mozambique (Moçambique)",
41963 "Myanmar (Burma) (မြန်မာ)",
41968 "Namibia (Namibië)",
41983 "Netherlands (Nederland)",
41988 "New Caledonia (Nouvelle-Calédonie)",
42023 "North Korea (조선 민주주의 인민 공화국)",
42028 "Northern Mariana Islands",
42044 "Pakistan (پاکستان)",
42054 "Palestine (فلسطين)",
42064 "Papua New Guinea",
42106 "Réunion (La Réunion)",
42112 "Romania (România)",
42128 "Saint Barthélemy",
42139 "Saint Kitts and Nevis",
42149 "Saint Martin (Saint-Martin (partie française))",
42155 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42160 "Saint Vincent and the Grenadines",
42175 "São Tomé and Príncipe (São Tomé e Príncipe)",
42180 "Saudi Arabia (المملكة العربية السعودية)",
42185 "Senegal (Sénégal)",
42215 "Slovakia (Slovensko)",
42220 "Slovenia (Slovenija)",
42230 "Somalia (Soomaaliya)",
42240 "South Korea (대한민국)",
42245 "South Sudan (جنوب السودان)",
42255 "Sri Lanka (ශ්රී ලංකාව)",
42260 "Sudan (السودان)",
42270 "Svalbard and Jan Mayen",
42281 "Sweden (Sverige)",
42286 "Switzerland (Schweiz)",
42291 "Syria (سوريا)",
42336 "Trinidad and Tobago",
42341 "Tunisia (تونس)",
42346 "Turkey (Türkiye)",
42356 "Turks and Caicos Islands",
42366 "U.S. Virgin Islands",
42376 "Ukraine (Україна)",
42381 "United Arab Emirates (الإمارات العربية المتحدة)",
42403 "Uzbekistan (Oʻzbekiston)",
42413 "Vatican City (Città del Vaticano)",
42424 "Vietnam (Việt Nam)",
42429 "Wallis and Futuna (Wallis-et-Futuna)",
42434 "Western Sahara (الصحراء الغربية)",
42440 "Yemen (اليمن)",
42464 * This script refer to:
42465 * Title: International Telephone Input
42466 * Author: Jack O'Connor
42467 * Code version: v12.1.12
42468 * Availability: https://github.com/jackocnr/intl-tel-input.git
42472 * @class Roo.bootstrap.PhoneInput
42473 * @extends Roo.bootstrap.TriggerField
42474 * An input with International dial-code selection
42476 * @cfg {String} defaultDialCode default '+852'
42477 * @cfg {Array} preferedCountries default []
42480 * Create a new PhoneInput.
42481 * @param {Object} config Configuration options
42484 Roo.bootstrap.PhoneInput = function(config) {
42485 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42488 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42490 listWidth: undefined,
42492 selectedClass: 'active',
42494 invalidClass : "has-warning",
42496 validClass: 'has-success',
42498 allowed: '0123456789',
42503 * @cfg {String} defaultDialCode The default dial code when initializing the input
42505 defaultDialCode: '+852',
42508 * @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
42510 preferedCountries: false,
42512 getAutoCreate : function()
42514 var data = Roo.bootstrap.PhoneInputData();
42515 var align = this.labelAlign || this.parentLabelAlign();
42518 this.allCountries = [];
42519 this.dialCodeMapping = [];
42521 for (var i = 0; i < data.length; i++) {
42523 this.allCountries[i] = {
42527 priority: c[3] || 0,
42528 areaCodes: c[4] || null
42530 this.dialCodeMapping[c[2]] = {
42533 priority: c[3] || 0,
42534 areaCodes: c[4] || null
42546 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42547 maxlength: this.max_length,
42548 cls : 'form-control tel-input',
42549 autocomplete: 'new-password'
42552 var hiddenInput = {
42555 cls: 'hidden-tel-input'
42559 hiddenInput.name = this.name;
42562 if (this.disabled) {
42563 input.disabled = true;
42566 var flag_container = {
42583 cls: this.hasFeedback ? 'has-feedback' : '',
42589 cls: 'dial-code-holder',
42596 cls: 'roo-select2-container input-group',
42603 if (this.fieldLabel.length) {
42606 tooltip: 'This field is required'
42612 cls: 'control-label',
42618 html: this.fieldLabel
42621 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42627 if(this.indicatorpos == 'right') {
42628 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42635 if(align == 'left') {
42643 if(this.labelWidth > 12){
42644 label.style = "width: " + this.labelWidth + 'px';
42646 if(this.labelWidth < 13 && this.labelmd == 0){
42647 this.labelmd = this.labelWidth;
42649 if(this.labellg > 0){
42650 label.cls += ' col-lg-' + this.labellg;
42651 input.cls += ' col-lg-' + (12 - this.labellg);
42653 if(this.labelmd > 0){
42654 label.cls += ' col-md-' + this.labelmd;
42655 container.cls += ' col-md-' + (12 - this.labelmd);
42657 if(this.labelsm > 0){
42658 label.cls += ' col-sm-' + this.labelsm;
42659 container.cls += ' col-sm-' + (12 - this.labelsm);
42661 if(this.labelxs > 0){
42662 label.cls += ' col-xs-' + this.labelxs;
42663 container.cls += ' col-xs-' + (12 - this.labelxs);
42673 var settings = this;
42675 ['xs','sm','md','lg'].map(function(size){
42676 if (settings[size]) {
42677 cfg.cls += ' col-' + size + '-' + settings[size];
42681 this.store = new Roo.data.Store({
42682 proxy : new Roo.data.MemoryProxy({}),
42683 reader : new Roo.data.JsonReader({
42694 'name' : 'dialCode',
42698 'name' : 'priority',
42702 'name' : 'areaCodes',
42709 if(!this.preferedCountries) {
42710 this.preferedCountries = [
42717 var p = this.preferedCountries.reverse();
42720 for (var i = 0; i < p.length; i++) {
42721 for (var j = 0; j < this.allCountries.length; j++) {
42722 if(this.allCountries[j].iso2 == p[i]) {
42723 var t = this.allCountries[j];
42724 this.allCountries.splice(j,1);
42725 this.allCountries.unshift(t);
42731 this.store.proxy.data = {
42733 data: this.allCountries
42739 initEvents : function()
42742 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42744 this.indicator = this.indicatorEl();
42745 this.flag = this.flagEl();
42746 this.dialCodeHolder = this.dialCodeHolderEl();
42748 this.trigger = this.el.select('div.flag-box',true).first();
42749 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42754 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42755 _this.list.setWidth(lw);
42758 this.list.on('mouseover', this.onViewOver, this);
42759 this.list.on('mousemove', this.onViewMove, this);
42760 this.inputEl().on("keyup", this.onKeyUp, this);
42761 this.inputEl().on("keypress", this.onKeyPress, this);
42763 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42765 this.view = new Roo.View(this.list, this.tpl, {
42766 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42769 this.view.on('click', this.onViewClick, this);
42770 this.setValue(this.defaultDialCode);
42773 onTriggerClick : function(e)
42775 Roo.log('trigger click');
42780 if(this.isExpanded()){
42782 this.hasFocus = false;
42784 this.store.load({});
42785 this.hasFocus = true;
42790 isExpanded : function()
42792 return this.list.isVisible();
42795 collapse : function()
42797 if(!this.isExpanded()){
42801 Roo.get(document).un('mousedown', this.collapseIf, this);
42802 Roo.get(document).un('mousewheel', this.collapseIf, this);
42803 this.fireEvent('collapse', this);
42807 expand : function()
42811 if(this.isExpanded() || !this.hasFocus){
42815 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42816 this.list.setWidth(lw);
42819 this.restrictHeight();
42821 Roo.get(document).on('mousedown', this.collapseIf, this);
42822 Roo.get(document).on('mousewheel', this.collapseIf, this);
42824 this.fireEvent('expand', this);
42827 restrictHeight : function()
42829 this.list.alignTo(this.inputEl(), this.listAlign);
42830 this.list.alignTo(this.inputEl(), this.listAlign);
42833 onViewOver : function(e, t)
42835 if(this.inKeyMode){
42838 var item = this.view.findItemFromChild(t);
42841 var index = this.view.indexOf(item);
42842 this.select(index, false);
42847 onViewClick : function(view, doFocus, el, e)
42849 var index = this.view.getSelectedIndexes()[0];
42851 var r = this.store.getAt(index);
42854 this.onSelect(r, index);
42856 if(doFocus !== false && !this.blockFocus){
42857 this.inputEl().focus();
42861 onViewMove : function(e, t)
42863 this.inKeyMode = false;
42866 select : function(index, scrollIntoView)
42868 this.selectedIndex = index;
42869 this.view.select(index);
42870 if(scrollIntoView !== false){
42871 var el = this.view.getNode(index);
42873 this.list.scrollChildIntoView(el, false);
42878 createList : function()
42880 this.list = Roo.get(document.body).createChild({
42882 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42883 style: 'display:none'
42886 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42889 collapseIf : function(e)
42891 var in_combo = e.within(this.el);
42892 var in_list = e.within(this.list);
42893 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42895 if (in_combo || in_list || is_list) {
42901 onSelect : function(record, index)
42903 if(this.fireEvent('beforeselect', this, record, index) !== false){
42905 this.setFlagClass(record.data.iso2);
42906 this.setDialCode(record.data.dialCode);
42907 this.hasFocus = false;
42909 this.fireEvent('select', this, record, index);
42913 flagEl : function()
42915 var flag = this.el.select('div.flag',true).first();
42922 dialCodeHolderEl : function()
42924 var d = this.el.select('input.dial-code-holder',true).first();
42931 setDialCode : function(v)
42933 this.dialCodeHolder.dom.value = '+'+v;
42936 setFlagClass : function(n)
42938 this.flag.dom.className = 'flag '+n;
42941 getValue : function()
42943 var v = this.inputEl().getValue();
42944 if(this.dialCodeHolder) {
42945 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42950 setValue : function(v)
42952 var d = this.getDialCode(v);
42954 //invalid dial code
42955 if(v.length == 0 || !d || d.length == 0) {
42957 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42958 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42964 this.setFlagClass(this.dialCodeMapping[d].iso2);
42965 this.setDialCode(d);
42966 this.inputEl().dom.value = v.replace('+'+d,'');
42967 this.hiddenEl().dom.value = this.getValue();
42972 getDialCode : function(v)
42976 if (v.length == 0) {
42977 return this.dialCodeHolder.dom.value;
42981 if (v.charAt(0) != "+") {
42984 var numericChars = "";
42985 for (var i = 1; i < v.length; i++) {
42986 var c = v.charAt(i);
42989 if (this.dialCodeMapping[numericChars]) {
42990 dialCode = v.substr(1, i);
42992 if (numericChars.length == 4) {
43002 this.setValue(this.defaultDialCode);
43006 hiddenEl : function()
43008 return this.el.select('input.hidden-tel-input',true).first();
43011 // after setting val
43012 onKeyUp : function(e){
43013 this.setValue(this.getValue());
43016 onKeyPress : function(e){
43017 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43024 * @class Roo.bootstrap.MoneyField
43025 * @extends Roo.bootstrap.ComboBox
43026 * Bootstrap MoneyField class
43029 * Create a new MoneyField.
43030 * @param {Object} config Configuration options
43033 Roo.bootstrap.MoneyField = function(config) {
43035 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43039 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43042 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43044 allowDecimals : true,
43046 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43048 decimalSeparator : ".",
43050 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43052 decimalPrecision : 0,
43054 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43056 allowNegative : true,
43058 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43062 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43064 minValue : Number.NEGATIVE_INFINITY,
43066 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43068 maxValue : Number.MAX_VALUE,
43070 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43072 minText : "The minimum value for this field is {0}",
43074 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43076 maxText : "The maximum value for this field is {0}",
43078 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43079 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43081 nanText : "{0} is not a valid number",
43083 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43087 * @cfg {String} defaults currency of the MoneyField
43088 * value should be in lkey
43090 defaultCurrency : false,
43092 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43094 thousandsDelimiter : false,
43096 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43107 getAutoCreate : function()
43109 var align = this.labelAlign || this.parentLabelAlign();
43121 cls : 'form-control roo-money-amount-input',
43122 autocomplete: 'new-password'
43125 var hiddenInput = {
43129 cls: 'hidden-number-input'
43132 if(this.max_length) {
43133 input.maxlength = this.max_length;
43137 hiddenInput.name = this.name;
43140 if (this.disabled) {
43141 input.disabled = true;
43144 var clg = 12 - this.inputlg;
43145 var cmd = 12 - this.inputmd;
43146 var csm = 12 - this.inputsm;
43147 var cxs = 12 - this.inputxs;
43151 cls : 'row roo-money-field',
43155 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43159 cls: 'roo-select2-container input-group',
43163 cls : 'form-control roo-money-currency-input',
43164 autocomplete: 'new-password',
43166 name : this.currencyName
43170 cls : 'input-group-addon',
43184 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43188 cls: this.hasFeedback ? 'has-feedback' : '',
43199 if (this.fieldLabel.length) {
43202 tooltip: 'This field is required'
43208 cls: 'control-label',
43214 html: this.fieldLabel
43217 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43223 if(this.indicatorpos == 'right') {
43224 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43231 if(align == 'left') {
43239 if(this.labelWidth > 12){
43240 label.style = "width: " + this.labelWidth + 'px';
43242 if(this.labelWidth < 13 && this.labelmd == 0){
43243 this.labelmd = this.labelWidth;
43245 if(this.labellg > 0){
43246 label.cls += ' col-lg-' + this.labellg;
43247 input.cls += ' col-lg-' + (12 - this.labellg);
43249 if(this.labelmd > 0){
43250 label.cls += ' col-md-' + this.labelmd;
43251 container.cls += ' col-md-' + (12 - this.labelmd);
43253 if(this.labelsm > 0){
43254 label.cls += ' col-sm-' + this.labelsm;
43255 container.cls += ' col-sm-' + (12 - this.labelsm);
43257 if(this.labelxs > 0){
43258 label.cls += ' col-xs-' + this.labelxs;
43259 container.cls += ' col-xs-' + (12 - this.labelxs);
43270 var settings = this;
43272 ['xs','sm','md','lg'].map(function(size){
43273 if (settings[size]) {
43274 cfg.cls += ' col-' + size + '-' + settings[size];
43281 initEvents : function()
43283 this.indicator = this.indicatorEl();
43285 this.initCurrencyEvent();
43287 this.initNumberEvent();
43290 initCurrencyEvent : function()
43293 throw "can not find store for combo";
43296 this.store = Roo.factory(this.store, Roo.data);
43297 this.store.parent = this;
43301 this.triggerEl = this.el.select('.input-group-addon', true).first();
43303 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43308 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43309 _this.list.setWidth(lw);
43312 this.list.on('mouseover', this.onViewOver, this);
43313 this.list.on('mousemove', this.onViewMove, this);
43314 this.list.on('scroll', this.onViewScroll, this);
43317 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43320 this.view = new Roo.View(this.list, this.tpl, {
43321 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43324 this.view.on('click', this.onViewClick, this);
43326 this.store.on('beforeload', this.onBeforeLoad, this);
43327 this.store.on('load', this.onLoad, this);
43328 this.store.on('loadexception', this.onLoadException, this);
43330 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43331 "up" : function(e){
43332 this.inKeyMode = true;
43336 "down" : function(e){
43337 if(!this.isExpanded()){
43338 this.onTriggerClick();
43340 this.inKeyMode = true;
43345 "enter" : function(e){
43348 if(this.fireEvent("specialkey", this, e)){
43349 this.onViewClick(false);
43355 "esc" : function(e){
43359 "tab" : function(e){
43362 if(this.fireEvent("specialkey", this, e)){
43363 this.onViewClick(false);
43371 doRelay : function(foo, bar, hname){
43372 if(hname == 'down' || this.scope.isExpanded()){
43373 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43381 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43385 initNumberEvent : function(e)
43387 this.inputEl().on("keydown" , this.fireKey, this);
43388 this.inputEl().on("focus", this.onFocus, this);
43389 this.inputEl().on("blur", this.onBlur, this);
43391 this.inputEl().relayEvent('keyup', this);
43393 if(this.indicator){
43394 this.indicator.addClass('invisible');
43397 this.originalValue = this.getValue();
43399 if(this.validationEvent == 'keyup'){
43400 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43401 this.inputEl().on('keyup', this.filterValidation, this);
43403 else if(this.validationEvent !== false){
43404 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43407 if(this.selectOnFocus){
43408 this.on("focus", this.preFocus, this);
43411 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43412 this.inputEl().on("keypress", this.filterKeys, this);
43414 this.inputEl().relayEvent('keypress', this);
43417 var allowed = "0123456789";
43419 if(this.allowDecimals){
43420 allowed += this.decimalSeparator;
43423 if(this.allowNegative){
43427 if(this.thousandsDelimiter) {
43431 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43433 var keyPress = function(e){
43435 var k = e.getKey();
43437 var c = e.getCharCode();
43440 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43441 allowed.indexOf(String.fromCharCode(c)) === -1
43447 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43451 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43456 this.inputEl().on("keypress", keyPress, this);
43460 onTriggerClick : function(e)
43467 this.loadNext = false;
43469 if(this.isExpanded()){
43474 this.hasFocus = true;
43476 if(this.triggerAction == 'all') {
43477 this.doQuery(this.allQuery, true);
43481 this.doQuery(this.getRawValue());
43484 getCurrency : function()
43486 var v = this.currencyEl().getValue();
43491 restrictHeight : function()
43493 this.list.alignTo(this.currencyEl(), this.listAlign);
43494 this.list.alignTo(this.currencyEl(), this.listAlign);
43497 onViewClick : function(view, doFocus, el, e)
43499 var index = this.view.getSelectedIndexes()[0];
43501 var r = this.store.getAt(index);
43504 this.onSelect(r, index);
43508 onSelect : function(record, index){
43510 if(this.fireEvent('beforeselect', this, record, index) !== false){
43512 this.setFromCurrencyData(index > -1 ? record.data : false);
43516 this.fireEvent('select', this, record, index);
43520 setFromCurrencyData : function(o)
43524 this.lastCurrency = o;
43526 if (this.currencyField) {
43527 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43529 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43532 this.lastSelectionText = currency;
43534 //setting default currency
43535 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43536 this.setCurrency(this.defaultCurrency);
43540 this.setCurrency(currency);
43543 setFromData : function(o)
43547 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43549 this.setFromCurrencyData(c);
43554 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43556 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43559 this.setValue(value);
43563 setCurrency : function(v)
43565 this.currencyValue = v;
43568 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43573 setValue : function(v)
43575 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43581 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43583 this.inputEl().dom.value = (v == '') ? '' :
43584 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43586 if(!this.allowZero && v === '0') {
43587 this.hiddenEl().dom.value = '';
43588 this.inputEl().dom.value = '';
43595 getRawValue : function()
43597 var v = this.inputEl().getValue();
43602 getValue : function()
43604 return this.fixPrecision(this.parseValue(this.getRawValue()));
43607 parseValue : function(value)
43609 if(this.thousandsDelimiter) {
43611 r = new RegExp(",", "g");
43612 value = value.replace(r, "");
43615 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43616 return isNaN(value) ? '' : value;
43620 fixPrecision : function(value)
43622 if(this.thousandsDelimiter) {
43624 r = new RegExp(",", "g");
43625 value = value.replace(r, "");
43628 var nan = isNaN(value);
43630 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43631 return nan ? '' : value;
43633 return parseFloat(value).toFixed(this.decimalPrecision);
43636 decimalPrecisionFcn : function(v)
43638 return Math.floor(v);
43641 validateValue : function(value)
43643 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43647 var num = this.parseValue(value);
43650 this.markInvalid(String.format(this.nanText, value));
43654 if(num < this.minValue){
43655 this.markInvalid(String.format(this.minText, this.minValue));
43659 if(num > this.maxValue){
43660 this.markInvalid(String.format(this.maxText, this.maxValue));
43667 validate : function()
43669 if(this.disabled || this.allowBlank){
43674 var currency = this.getCurrency();
43676 if(this.validateValue(this.getRawValue()) && currency.length){
43681 this.markInvalid();
43685 getName: function()
43690 beforeBlur : function()
43696 var v = this.parseValue(this.getRawValue());
43703 onBlur : function()
43707 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43708 //this.el.removeClass(this.focusClass);
43711 this.hasFocus = false;
43713 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43717 var v = this.getValue();
43719 if(String(v) !== String(this.startValue)){
43720 this.fireEvent('change', this, v, this.startValue);
43723 this.fireEvent("blur", this);
43726 inputEl : function()
43728 return this.el.select('.roo-money-amount-input', true).first();
43731 currencyEl : function()
43733 return this.el.select('.roo-money-currency-input', true).first();
43736 hiddenEl : function()
43738 return this.el.select('input.hidden-number-input',true).first();
43742 * @class Roo.bootstrap.BezierSignature
43743 * @extends Roo.bootstrap.Component
43744 * Bootstrap BezierSignature class
43745 * This script refer to:
43746 * Title: Signature Pad
43748 * Availability: https://github.com/szimek/signature_pad
43751 * Create a new BezierSignature
43752 * @param {Object} config The config object
43755 Roo.bootstrap.BezierSignature = function(config){
43756 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43762 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43769 mouse_btn_down: true,
43772 * @cfg {int} canvas height
43774 canvas_height: '200px',
43777 * @cfg {float|function} Radius of a single dot.
43782 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43787 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43792 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43797 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43802 * @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.
43804 bg_color: 'rgba(0, 0, 0, 0)',
43807 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43809 dot_color: 'black',
43812 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43814 velocity_filter_weight: 0.7,
43817 * @cfg {function} Callback when stroke begin.
43822 * @cfg {function} Callback when stroke end.
43826 getAutoCreate : function()
43828 var cls = 'roo-signature column';
43831 cls += ' ' + this.cls;
43841 for(var i = 0; i < col_sizes.length; i++) {
43842 if(this[col_sizes[i]]) {
43843 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43853 cls: 'roo-signature-body',
43857 cls: 'roo-signature-body-canvas',
43858 height: this.canvas_height,
43859 width: this.canvas_width
43866 style: 'display: none'
43874 initEvents: function()
43876 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43878 var canvas = this.canvasEl();
43880 // mouse && touch event swapping...
43881 canvas.dom.style.touchAction = 'none';
43882 canvas.dom.style.msTouchAction = 'none';
43884 this.mouse_btn_down = false;
43885 canvas.on('mousedown', this._handleMouseDown, this);
43886 canvas.on('mousemove', this._handleMouseMove, this);
43887 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43889 if (window.PointerEvent) {
43890 canvas.on('pointerdown', this._handleMouseDown, this);
43891 canvas.on('pointermove', this._handleMouseMove, this);
43892 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43895 if ('ontouchstart' in window) {
43896 canvas.on('touchstart', this._handleTouchStart, this);
43897 canvas.on('touchmove', this._handleTouchMove, this);
43898 canvas.on('touchend', this._handleTouchEnd, this);
43901 Roo.EventManager.onWindowResize(this.resize, this, true);
43903 // file input event
43904 this.fileEl().on('change', this.uploadImage, this);
43911 resize: function(){
43913 var canvas = this.canvasEl().dom;
43914 var ctx = this.canvasElCtx();
43915 var img_data = false;
43917 if(canvas.width > 0) {
43918 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43920 // setting canvas width will clean img data
43923 var style = window.getComputedStyle ?
43924 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43926 var padding_left = parseInt(style.paddingLeft) || 0;
43927 var padding_right = parseInt(style.paddingRight) || 0;
43929 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43932 ctx.putImageData(img_data, 0, 0);
43936 _handleMouseDown: function(e)
43938 if (e.browserEvent.which === 1) {
43939 this.mouse_btn_down = true;
43940 this.strokeBegin(e);
43944 _handleMouseMove: function (e)
43946 if (this.mouse_btn_down) {
43947 this.strokeMoveUpdate(e);
43951 _handleMouseUp: function (e)
43953 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43954 this.mouse_btn_down = false;
43959 _handleTouchStart: function (e) {
43961 e.preventDefault();
43962 if (e.browserEvent.targetTouches.length === 1) {
43963 // var touch = e.browserEvent.changedTouches[0];
43964 // this.strokeBegin(touch);
43966 this.strokeBegin(e); // assume e catching the correct xy...
43970 _handleTouchMove: function (e) {
43971 e.preventDefault();
43972 // var touch = event.targetTouches[0];
43973 // _this._strokeMoveUpdate(touch);
43974 this.strokeMoveUpdate(e);
43977 _handleTouchEnd: function (e) {
43978 var wasCanvasTouched = e.target === this.canvasEl().dom;
43979 if (wasCanvasTouched) {
43980 e.preventDefault();
43981 // var touch = event.changedTouches[0];
43982 // _this._strokeEnd(touch);
43987 reset: function () {
43988 this._lastPoints = [];
43989 this._lastVelocity = 0;
43990 this._lastWidth = (this.min_width + this.max_width) / 2;
43991 this.canvasElCtx().fillStyle = this.dot_color;
43994 strokeMoveUpdate: function(e)
43996 this.strokeUpdate(e);
43998 if (this.throttle) {
43999 this.throttleStroke(this.strokeUpdate, this.throttle);
44002 this.strokeUpdate(e);
44006 strokeBegin: function(e)
44008 var newPointGroup = {
44009 color: this.dot_color,
44013 if (typeof this.onBegin === 'function') {
44017 this.curve_data.push(newPointGroup);
44019 this.strokeUpdate(e);
44022 strokeUpdate: function(e)
44024 var rect = this.canvasEl().dom.getBoundingClientRect();
44025 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44026 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44027 var lastPoints = lastPointGroup.points;
44028 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44029 var isLastPointTooClose = lastPoint
44030 ? point.distanceTo(lastPoint) <= this.min_distance
44032 var color = lastPointGroup.color;
44033 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44034 var curve = this.addPoint(point);
44036 this.drawDot({color: color, point: point});
44039 this.drawCurve({color: color, curve: curve});
44049 strokeEnd: function(e)
44051 this.strokeUpdate(e);
44052 if (typeof this.onEnd === 'function') {
44057 addPoint: function (point) {
44058 var _lastPoints = this._lastPoints;
44059 _lastPoints.push(point);
44060 if (_lastPoints.length > 2) {
44061 if (_lastPoints.length === 3) {
44062 _lastPoints.unshift(_lastPoints[0]);
44064 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44065 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44066 _lastPoints.shift();
44072 calculateCurveWidths: function (startPoint, endPoint) {
44073 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44074 (1 - this.velocity_filter_weight) * this._lastVelocity;
44076 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44079 start: this._lastWidth
44082 this._lastVelocity = velocity;
44083 this._lastWidth = newWidth;
44087 drawDot: function (_a) {
44088 var color = _a.color, point = _a.point;
44089 var ctx = this.canvasElCtx();
44090 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44092 this.drawCurveSegment(point.x, point.y, width);
44094 ctx.fillStyle = color;
44098 drawCurve: function (_a) {
44099 var color = _a.color, curve = _a.curve;
44100 var ctx = this.canvasElCtx();
44101 var widthDelta = curve.endWidth - curve.startWidth;
44102 var drawSteps = Math.floor(curve.length()) * 2;
44104 ctx.fillStyle = color;
44105 for (var i = 0; i < drawSteps; i += 1) {
44106 var t = i / drawSteps;
44112 var x = uuu * curve.startPoint.x;
44113 x += 3 * uu * t * curve.control1.x;
44114 x += 3 * u * tt * curve.control2.x;
44115 x += ttt * curve.endPoint.x;
44116 var y = uuu * curve.startPoint.y;
44117 y += 3 * uu * t * curve.control1.y;
44118 y += 3 * u * tt * curve.control2.y;
44119 y += ttt * curve.endPoint.y;
44120 var width = curve.startWidth + ttt * widthDelta;
44121 this.drawCurveSegment(x, y, width);
44127 drawCurveSegment: function (x, y, width) {
44128 var ctx = this.canvasElCtx();
44130 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44131 this.is_empty = false;
44136 var ctx = this.canvasElCtx();
44137 var canvas = this.canvasEl().dom;
44138 ctx.fillStyle = this.bg_color;
44139 ctx.clearRect(0, 0, canvas.width, canvas.height);
44140 ctx.fillRect(0, 0, canvas.width, canvas.height);
44141 this.curve_data = [];
44143 this.is_empty = true;
44148 return this.el.select('input',true).first();
44151 canvasEl: function()
44153 return this.el.select('canvas',true).first();
44156 canvasElCtx: function()
44158 return this.el.select('canvas',true).first().dom.getContext('2d');
44161 getImage: function(type)
44163 if(this.is_empty) {
44168 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44171 drawFromImage: function(img_src)
44173 var img = new Image();
44175 img.onload = function(){
44176 this.canvasElCtx().drawImage(img, 0, 0);
44181 this.is_empty = false;
44184 selectImage: function()
44186 this.fileEl().dom.click();
44189 uploadImage: function(e)
44191 var reader = new FileReader();
44193 reader.onload = function(e){
44194 var img = new Image();
44195 img.onload = function(){
44197 this.canvasElCtx().drawImage(img, 0, 0);
44199 img.src = e.target.result;
44202 reader.readAsDataURL(e.target.files[0]);
44205 // Bezier Point Constructor
44206 Point: (function () {
44207 function Point(x, y, time) {
44210 this.time = time || Date.now();
44212 Point.prototype.distanceTo = function (start) {
44213 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44215 Point.prototype.equals = function (other) {
44216 return this.x === other.x && this.y === other.y && this.time === other.time;
44218 Point.prototype.velocityFrom = function (start) {
44219 return this.time !== start.time
44220 ? this.distanceTo(start) / (this.time - start.time)
44227 // Bezier Constructor
44228 Bezier: (function () {
44229 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44230 this.startPoint = startPoint;
44231 this.control2 = control2;
44232 this.control1 = control1;
44233 this.endPoint = endPoint;
44234 this.startWidth = startWidth;
44235 this.endWidth = endWidth;
44237 Bezier.fromPoints = function (points, widths, scope) {
44238 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44239 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44240 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44242 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44243 var dx1 = s1.x - s2.x;
44244 var dy1 = s1.y - s2.y;
44245 var dx2 = s2.x - s3.x;
44246 var dy2 = s2.y - s3.y;
44247 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44248 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44249 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44250 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44251 var dxm = m1.x - m2.x;
44252 var dym = m1.y - m2.y;
44253 var k = l2 / (l1 + l2);
44254 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44255 var tx = s2.x - cm.x;
44256 var ty = s2.y - cm.y;
44258 c1: new scope.Point(m1.x + tx, m1.y + ty),
44259 c2: new scope.Point(m2.x + tx, m2.y + ty)
44262 Bezier.prototype.length = function () {
44267 for (var i = 0; i <= steps; i += 1) {
44269 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44270 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44272 var xdiff = cx - px;
44273 var ydiff = cy - py;
44274 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44281 Bezier.prototype.point = function (t, start, c1, c2, end) {
44282 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44283 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44284 + (3.0 * c2 * (1.0 - t) * t * t)
44285 + (end * t * t * t);
44290 throttleStroke: function(fn, wait) {
44291 if (wait === void 0) { wait = 250; }
44293 var timeout = null;
44297 var later = function () {
44298 previous = Date.now();
44300 result = fn.apply(storedContext, storedArgs);
44302 storedContext = null;
44306 return function wrapper() {
44308 for (var _i = 0; _i < arguments.length; _i++) {
44309 args[_i] = arguments[_i];
44311 var now = Date.now();
44312 var remaining = wait - (now - previous);
44313 storedContext = this;
44315 if (remaining <= 0 || remaining > wait) {
44317 clearTimeout(timeout);
44321 result = fn.apply(storedContext, storedArgs);
44323 storedContext = null;
44327 else if (!timeout) {
44328 timeout = window.setTimeout(later, remaining);