2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2523 var dom = move_card.el.dom;
2524 dom.parentNode.removeChild(dom);
2525 dom.style.width = ''; // clear with - which is set by drag.
2527 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528 var cardel = next_to_card.el.dom;
2530 if (position == 'above' ) {
2531 cardel.parentNode.insertBefore(dom, cardel);
2532 } else if (cardel.nextSibling) {
2533 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535 cardel.parentNode.append(dom);
2538 // card container???
2539 this.containerEl.dom.append(dom);
2542 //FIXME HANDLE card = true
2544 // add this to the correct place in items.
2548 // remove Card from items.
2550 var old_parent = move_card.parent();
2552 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2554 if (this.items.length) {
2556 //Roo.log([info.items_n, info.position, this.items.length]);
2557 for (var i =0; i < this.items.length; i++) {
2558 if (i == to_items_n && position == 'above') {
2559 nitems.push(move_card);
2561 nitems.push(this.items[i]);
2562 if (i == to_items_n && position == 'below') {
2563 nitems.push(move_card);
2566 this.items = nitems;
2567 Roo.log(this.items);
2569 this.items.push(move_card);
2572 move_card.parentId = this.id;
2580 /** Decide whether to drop above or below a View node. */
2581 getDropPoint : function(e, n, dd)
2586 if (n == this.containerEl.dom) {
2589 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590 var c = t + (b - t) / 2;
2591 var y = Roo.lib.Event.getPageY(e);
2598 onToggleCollapse : function(e)
2600 if (this.collapsed) {
2601 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602 this.collapsableEl.addClass('show');
2603 this.collapsed = false;
2606 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607 this.collapsableEl.removeClass('show');
2608 this.collapsed = true;
2613 onToggleRotate : function(e)
2615 this.collapsableEl.removeClass('show');
2616 this.footerEl.removeClass('d-none');
2617 this.el.removeClass('roo-card-rotated');
2618 this.el.removeClass('d-none');
2621 this.collapsableEl.addClass('show');
2622 this.rotated = false;
2623 this.fireEvent('rotate', this, this.rotated);
2626 this.el.addClass('roo-card-rotated');
2627 this.footerEl.addClass('d-none');
2628 this.el.select('.roo-collapsable').removeClass('show');
2630 this.rotated = true;
2631 this.fireEvent('rotate', this, this.rotated);
2635 dropPlaceHolder: function (action, info, data)
2637 if (this.dropEl === false) {
2638 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2642 this.dropEl.removeClass(['d-none', 'd-block']);
2643 if (action == 'hide') {
2645 this.dropEl.addClass('d-none');
2648 // FIXME - info.card == true!!!
2649 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2651 if (info.card !== true) {
2652 var cardel = info.card.el.dom;
2654 if (info.position == 'above') {
2655 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656 } else if (cardel.nextSibling) {
2657 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2659 cardel.parentNode.append(this.dropEl.dom);
2662 // card container???
2663 this.containerEl.dom.append(this.dropEl.dom);
2666 this.dropEl.addClass('d-block roo-card-dropzone');
2668 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2675 setHeaderText: function(html)
2677 this.headerContainerEl.dom.innerHTML = html;
2686 * Card header - holder for the card header elements.
2691 * @class Roo.bootstrap.CardHeader
2692 * @extends Roo.bootstrap.Element
2693 * Bootstrap CardHeader class
2695 * Create a new Card Header - that you can embed children into
2696 * @param {Object} config The config object
2699 Roo.bootstrap.CardHeader = function(config){
2700 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2706 container_method : 'getCardHeader'
2719 * Card footer - holder for the card footer elements.
2724 * @class Roo.bootstrap.CardFooter
2725 * @extends Roo.bootstrap.Element
2726 * Bootstrap CardFooter class
2728 * Create a new Card Footer - that you can embed children into
2729 * @param {Object} config The config object
2732 Roo.bootstrap.CardFooter = function(config){
2733 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2739 container_method : 'getCardFooter'
2752 * Card header - holder for the card header elements.
2757 * @class Roo.bootstrap.CardImageTop
2758 * @extends Roo.bootstrap.Element
2759 * Bootstrap CardImageTop class
2761 * Create a new Card Image Top container
2762 * @param {Object} config The config object
2765 Roo.bootstrap.CardImageTop = function(config){
2766 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2772 container_method : 'getCardImageTop'
2790 * @class Roo.bootstrap.Img
2791 * @extends Roo.bootstrap.Component
2792 * Bootstrap Img class
2793 * @cfg {Boolean} imgResponsive false | true
2794 * @cfg {String} border rounded | circle | thumbnail
2795 * @cfg {String} src image source
2796 * @cfg {String} alt image alternative text
2797 * @cfg {String} href a tag href
2798 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799 * @cfg {String} xsUrl xs image source
2800 * @cfg {String} smUrl sm image source
2801 * @cfg {String} mdUrl md image source
2802 * @cfg {String} lgUrl lg image source
2805 * Create a new Input
2806 * @param {Object} config The config object
2809 Roo.bootstrap.Img = function(config){
2810 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816 * The img click event for the img.
2817 * @param {Roo.EventObject} e
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2825 imgResponsive: true,
2835 getAutoCreate : function()
2837 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838 return this.createSingleImg();
2843 cls: 'roo-image-responsive-group',
2848 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2850 if(!_this[size + 'Url']){
2856 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857 html: _this.html || cfg.html,
2858 src: _this[size + 'Url']
2861 img.cls += ' roo-image-responsive-' + size;
2863 var s = ['xs', 'sm', 'md', 'lg'];
2865 s.splice(s.indexOf(size), 1);
2867 Roo.each(s, function(ss){
2868 img.cls += ' hidden-' + ss;
2871 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872 cfg.cls += ' img-' + _this.border;
2876 cfg.alt = _this.alt;
2889 a.target = _this.target;
2893 cfg.cn.push((_this.href) ? a : img);
2900 createSingleImg : function()
2904 cls: (this.imgResponsive) ? 'img-responsive' : '',
2906 src : 'about:blank' // just incase src get's set to undefined?!?
2909 cfg.html = this.html || cfg.html;
2911 cfg.src = this.src || cfg.src;
2913 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914 cfg.cls += ' img-' + this.border;
2931 a.target = this.target;
2936 return (this.href) ? a : cfg;
2939 initEvents: function()
2942 this.el.on('click', this.onClick, this);
2947 onClick : function(e)
2949 Roo.log('img onclick');
2950 this.fireEvent('click', this, e);
2953 * Sets the url of the image - used to update it
2954 * @param {String} url the url of the image
2957 setSrc : function(url)
2961 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962 this.el.dom.src = url;
2966 this.el.select('img', true).first().dom.src = url;
2982 * @class Roo.bootstrap.Link
2983 * @extends Roo.bootstrap.Component
2984 * Bootstrap Link Class
2985 * @cfg {String} alt image alternative text
2986 * @cfg {String} href a tag href
2987 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988 * @cfg {String} html the content of the link.
2989 * @cfg {String} anchor name for the anchor link
2990 * @cfg {String} fa - favicon
2992 * @cfg {Boolean} preventDefault (true | false) default false
2996 * Create a new Input
2997 * @param {Object} config The config object
3000 Roo.bootstrap.Link = function(config){
3001 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007 * The img click event for the img.
3008 * @param {Roo.EventObject} e
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3018 preventDefault: false,
3024 getAutoCreate : function()
3026 var html = this.html || '';
3028 if (this.fa !== false) {
3029 html = '<i class="fa fa-' + this.fa + '"></i>';
3034 // anchor's do not require html/href...
3035 if (this.anchor === false) {
3037 cfg.href = this.href || '#';
3039 cfg.name = this.anchor;
3040 if (this.html !== false || this.fa !== false) {
3043 if (this.href !== false) {
3044 cfg.href = this.href;
3048 if(this.alt !== false){
3053 if(this.target !== false) {
3054 cfg.target = this.target;
3060 initEvents: function() {
3062 if(!this.href || this.preventDefault){
3063 this.el.on('click', this.onClick, this);
3067 onClick : function(e)
3069 if(this.preventDefault){
3072 //Roo.log('img onclick');
3073 this.fireEvent('click', this, e);
3086 * @class Roo.bootstrap.Header
3087 * @extends Roo.bootstrap.Component
3088 * Bootstrap Header class
3089 * @cfg {String} html content of header
3090 * @cfg {Number} level (1|2|3|4|5|6) default 1
3093 * Create a new Header
3094 * @param {Object} config The config object
3098 Roo.bootstrap.Header = function(config){
3099 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3110 getAutoCreate : function(){
3115 tag: 'h' + (1 *this.level),
3116 html: this.html || ''
3128 * Ext JS Library 1.1.1
3129 * Copyright(c) 2006-2007, Ext JS, LLC.
3131 * Originally Released Under LGPL - original licence link has changed is not relivant.
3134 * <script type="text/javascript">
3138 * @class Roo.bootstrap.MenuMgr
3139 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3142 Roo.bootstrap.MenuMgr = function(){
3143 var menus, active, groups = {}, attached = false, lastShow = new Date();
3145 // private - called when first menu is created
3148 active = new Roo.util.MixedCollection();
3149 Roo.get(document).addKeyListener(27, function(){
3150 if(active.length > 0){
3158 if(active && active.length > 0){
3159 var c = active.clone();
3169 if(active.length < 1){
3170 Roo.get(document).un("mouseup", onMouseDown);
3178 var last = active.last();
3179 lastShow = new Date();
3182 Roo.get(document).on("mouseup", onMouseDown);
3187 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188 m.parentMenu.activeChild = m;
3189 }else if(last && last.isVisible()){
3190 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3195 function onBeforeHide(m){
3197 m.activeChild.hide();
3199 if(m.autoHideTimer){
3200 clearTimeout(m.autoHideTimer);
3201 delete m.autoHideTimer;
3206 function onBeforeShow(m){
3207 var pm = m.parentMenu;
3208 if(!pm && !m.allowOtherMenus){
3210 }else if(pm && pm.activeChild && active != m){
3211 pm.activeChild.hide();
3215 // private this should really trigger on mouseup..
3216 function onMouseDown(e){
3217 Roo.log("on Mouse Up");
3219 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220 Roo.log("MenuManager hideAll");
3229 function onBeforeCheck(mi, state){
3231 var g = groups[mi.group];
3232 for(var i = 0, l = g.length; i < l; i++){
3234 g[i].setChecked(false);
3243 * Hides all menus that are currently visible
3245 hideAll : function(){
3250 register : function(menu){
3254 menus[menu.id] = menu;
3255 menu.on("beforehide", onBeforeHide);
3256 menu.on("hide", onHide);
3257 menu.on("beforeshow", onBeforeShow);
3258 menu.on("show", onShow);
3260 if(g && menu.events["checkchange"]){
3264 groups[g].push(menu);
3265 menu.on("checkchange", onCheck);
3270 * Returns a {@link Roo.menu.Menu} object
3271 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272 * be used to generate and return a new Menu instance.
3274 get : function(menu){
3275 if(typeof menu == "string"){ // menu id
3277 }else if(menu.events){ // menu instance
3280 /*else if(typeof menu.length == 'number'){ // array of menu items?
3281 return new Roo.bootstrap.Menu({items:menu});
3282 }else{ // otherwise, must be a config
3283 return new Roo.bootstrap.Menu(menu);
3290 unregister : function(menu){
3291 delete menus[menu.id];
3292 menu.un("beforehide", onBeforeHide);
3293 menu.un("hide", onHide);
3294 menu.un("beforeshow", onBeforeShow);
3295 menu.un("show", onShow);
3297 if(g && menu.events["checkchange"]){
3298 groups[g].remove(menu);
3299 menu.un("checkchange", onCheck);
3304 registerCheckable : function(menuItem){
3305 var g = menuItem.group;
3310 groups[g].push(menuItem);
3311 menuItem.on("beforecheckchange", onBeforeCheck);
3316 unregisterCheckable : function(menuItem){
3317 var g = menuItem.group;
3319 groups[g].remove(menuItem);
3320 menuItem.un("beforecheckchange", onBeforeCheck);
3332 * @class Roo.bootstrap.Menu
3333 * @extends Roo.bootstrap.Component
3334 * Bootstrap Menu class - container for MenuItems
3335 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336 * @cfg {bool} hidden if the menu should be hidden when rendered.
3337 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3338 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3342 * @param {Object} config The config object
3346 Roo.bootstrap.Menu = function(config){
3347 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348 if (this.registerMenu && this.type != 'treeview') {
3349 Roo.bootstrap.MenuMgr.register(this);
3356 * Fires before this menu is displayed (return false to block)
3357 * @param {Roo.menu.Menu} this
3362 * Fires before this menu is hidden (return false to block)
3363 * @param {Roo.menu.Menu} this
3368 * Fires after this menu is displayed
3369 * @param {Roo.menu.Menu} this
3374 * Fires after this menu is hidden
3375 * @param {Roo.menu.Menu} this
3380 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381 * @param {Roo.menu.Menu} this
3382 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383 * @param {Roo.EventObject} e
3388 * Fires when the mouse is hovering over this menu
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.EventObject} e
3391 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3396 * Fires when the mouse exits this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when a menu item contained in this menu is clicked
3405 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406 * @param {Roo.EventObject} e
3410 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3417 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3420 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3422 registerMenu : true,
3424 menuItems :false, // stores the menu items..
3434 getChildContainer : function() {
3438 getAutoCreate : function(){
3440 //if (['right'].indexOf(this.align)!==-1) {
3441 // cfg.cn[1].cls += ' pull-right'
3447 cls : 'dropdown-menu' ,
3448 style : 'z-index:1000'
3452 if (this.type === 'submenu') {
3453 cfg.cls = 'submenu active';
3455 if (this.type === 'treeview') {
3456 cfg.cls = 'treeview-menu';
3461 initEvents : function() {
3463 // Roo.log("ADD event");
3464 // Roo.log(this.triggerEl.dom);
3466 this.triggerEl.on('click', this.onTriggerClick, this);
3468 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3471 if (this.triggerEl.hasClass('nav-item')) {
3472 // dropdown toggle on the 'a' in BS4?
3473 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3475 this.triggerEl.addClass('dropdown-toggle');
3478 this.el.on('touchstart' , this.onTouch, this);
3480 this.el.on('click' , this.onClick, this);
3482 this.el.on("mouseover", this.onMouseOver, this);
3483 this.el.on("mouseout", this.onMouseOut, this);
3487 findTargetItem : function(e)
3489 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3493 //Roo.log(t); Roo.log(t.id);
3495 //Roo.log(this.menuitems);
3496 return this.menuitems.get(t.id);
3498 //return this.items.get(t.menuItemId);
3504 onTouch : function(e)
3506 Roo.log("menu.onTouch");
3507 //e.stopEvent(); this make the user popdown broken
3511 onClick : function(e)
3513 Roo.log("menu.onClick");
3515 var t = this.findTargetItem(e);
3516 if(!t || t.isContainer){
3521 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3522 if(t == this.activeItem && t.shouldDeactivate(e)){
3523 this.activeItem.deactivate();
3524 delete this.activeItem;
3528 this.setActiveItem(t, true);
3536 Roo.log('pass click event');
3540 this.fireEvent("click", this, t, e);
3544 if(!t.href.length || t.href == '#'){
3545 (function() { _this.hide(); }).defer(100);
3550 onMouseOver : function(e){
3551 var t = this.findTargetItem(e);
3554 // if(t.canActivate && !t.disabled){
3555 // this.setActiveItem(t, true);
3559 this.fireEvent("mouseover", this, e, t);
3561 isVisible : function(){
3562 return !this.hidden;
3564 onMouseOut : function(e){
3565 var t = this.findTargetItem(e);
3568 // if(t == this.activeItem && t.shouldDeactivate(e)){
3569 // this.activeItem.deactivate();
3570 // delete this.activeItem;
3573 this.fireEvent("mouseout", this, e, t);
3578 * Displays this menu relative to another element
3579 * @param {String/HTMLElement/Roo.Element} element The element to align to
3580 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581 * the element (defaults to this.defaultAlign)
3582 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3584 show : function(el, pos, parentMenu)
3586 if (false === this.fireEvent("beforeshow", this)) {
3587 Roo.log("show canceled");
3590 this.parentMenu = parentMenu;
3595 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3598 * Displays this menu at a specific xy position
3599 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3602 showAt : function(xy, parentMenu, /* private: */_e){
3603 this.parentMenu = parentMenu;
3608 this.fireEvent("beforeshow", this);
3609 //xy = this.el.adjustForConstraints(xy);
3613 this.hideMenuItems();
3614 this.hidden = false;
3615 this.triggerEl.addClass('open');
3616 this.el.addClass('show');
3618 // reassign x when hitting right
3619 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3623 // reassign y when hitting bottom
3624 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3628 // but the list may align on trigger left or trigger top... should it be a properity?
3630 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3635 this.fireEvent("show", this);
3641 this.doFocus.defer(50, this);
3645 doFocus : function(){
3647 this.focusEl.focus();
3652 * Hides this menu and optionally all parent menus
3653 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3655 hide : function(deep)
3657 if (false === this.fireEvent("beforehide", this)) {
3658 Roo.log("hide canceled");
3661 this.hideMenuItems();
3662 if(this.el && this.isVisible()){
3664 if(this.activeItem){
3665 this.activeItem.deactivate();
3666 this.activeItem = null;
3668 this.triggerEl.removeClass('open');;
3669 this.el.removeClass('show');
3671 this.fireEvent("hide", this);
3673 if(deep === true && this.parentMenu){
3674 this.parentMenu.hide(true);
3678 onTriggerClick : function(e)
3680 Roo.log('trigger click');
3682 var target = e.getTarget();
3684 Roo.log(target.nodeName.toLowerCase());
3686 if(target.nodeName.toLowerCase() === 'i'){
3692 onTriggerPress : function(e)
3694 Roo.log('trigger press');
3695 //Roo.log(e.getTarget());
3696 // Roo.log(this.triggerEl.dom);
3698 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699 var pel = Roo.get(e.getTarget());
3700 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701 Roo.log('is treeview or dropdown?');
3705 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3709 if (this.isVisible()) {
3714 this.show(this.triggerEl, '?', false);
3717 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3724 hideMenuItems : function()
3726 Roo.log("hide Menu Items");
3731 this.el.select('.open',true).each(function(aa) {
3733 aa.removeClass('open');
3737 addxtypeChild : function (tree, cntr) {
3738 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3740 this.menuitems.add(comp);
3752 this.getEl().dom.innerHTML = '';
3753 this.menuitems.clear();
3767 * @class Roo.bootstrap.MenuItem
3768 * @extends Roo.bootstrap.Component
3769 * Bootstrap MenuItem class
3770 * @cfg {String} html the menu label
3771 * @cfg {String} href the link
3772 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774 * @cfg {Boolean} active used on sidebars to highlight active itesm
3775 * @cfg {String} fa favicon to show on left of menu item.
3776 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3780 * Create a new MenuItem
3781 * @param {Object} config The config object
3785 Roo.bootstrap.MenuItem = function(config){
3786 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3791 * The raw click event for the entire grid.
3792 * @param {Roo.bootstrap.MenuItem} this
3793 * @param {Roo.EventObject} e
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3803 preventDefault: false,
3804 isContainer : false,
3808 getAutoCreate : function(){
3810 if(this.isContainer){
3813 cls: 'dropdown-menu-item '
3823 cls : 'dropdown-item',
3828 if (this.fa !== false) {
3831 cls : 'fa fa-' + this.fa
3840 cls: 'dropdown-menu-item',
3843 if (this.parent().type == 'treeview') {
3844 cfg.cls = 'treeview-menu';
3847 cfg.cls += ' active';
3852 anc.href = this.href || cfg.cn[0].href ;
3853 ctag.html = this.html || cfg.cn[0].html ;
3857 initEvents: function()
3859 if (this.parent().type == 'treeview') {
3860 this.el.select('a').on('click', this.onClick, this);
3864 this.menu.parentType = this.xtype;
3865 this.menu.triggerEl = this.el;
3866 this.menu = this.addxtype(Roo.apply({}, this.menu));
3870 onClick : function(e)
3872 Roo.log('item on click ');
3874 if(this.preventDefault){
3877 //this.parent().hideMenuItems();
3879 this.fireEvent('click', this, e);
3898 * @class Roo.bootstrap.MenuSeparator
3899 * @extends Roo.bootstrap.Component
3900 * Bootstrap MenuSeparator class
3903 * Create a new MenuItem
3904 * @param {Object} config The config object
3908 Roo.bootstrap.MenuSeparator = function(config){
3909 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3914 getAutoCreate : function(){
3933 * @class Roo.bootstrap.Modal
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap Modal class
3936 * @cfg {String} title Title of dialog
3937 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3939 * @cfg {Boolean} specificTitle default false
3940 * @cfg {Array} buttons Array of buttons or standard button set..
3941 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942 * @cfg {Boolean} animate default true
3943 * @cfg {Boolean} allow_close default true
3944 * @cfg {Boolean} fitwindow default false
3945 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947 * @cfg {String} size (sm|lg|xl) default empty
3948 * @cfg {Number} max_width set the max width of modal
3949 * @cfg {Boolean} editableTitle can the title be edited
3954 * Create a new Modal Dialog
3955 * @param {Object} config The config object
3958 Roo.bootstrap.Modal = function(config){
3959 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3964 * The raw btnclick event for the button
3965 * @param {Roo.EventObject} e
3970 * Fire when dialog resize
3971 * @param {Roo.bootstrap.Modal} this
3972 * @param {Roo.EventObject} e
3976 * @event titlechanged
3977 * Fire when the editable title has been changed
3978 * @param {Roo.bootstrap.Modal} this
3979 * @param {Roo.EventObject} value
3981 "titlechanged" : true
3984 this.buttons = this.buttons || [];
3987 this.tmpl = Roo.factory(this.tmpl);
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3994 title : 'test dialog',
4004 specificTitle: false,
4006 buttonPosition: 'right',
4028 editableTitle : false,
4030 onRender : function(ct, position)
4032 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4035 var cfg = Roo.apply({}, this.getAutoCreate());
4038 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4040 //if (!cfg.name.length) {
4044 cfg.cls += ' ' + this.cls;
4047 cfg.style = this.style;
4049 this.el = Roo.get(document.body).createChild(cfg, position);
4051 //var type = this.el.dom.type;
4054 if(this.tabIndex !== undefined){
4055 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4058 this.dialogEl = this.el.select('.modal-dialog',true).first();
4059 this.bodyEl = this.el.select('.modal-body',true).first();
4060 this.closeEl = this.el.select('.modal-header .close', true).first();
4061 this.headerEl = this.el.select('.modal-header',true).first();
4062 this.titleEl = this.el.select('.modal-title',true).first();
4063 this.footerEl = this.el.select('.modal-footer',true).first();
4065 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4067 //this.el.addClass("x-dlg-modal");
4069 if (this.buttons.length) {
4070 Roo.each(this.buttons, function(bb) {
4071 var b = Roo.apply({}, bb);
4072 b.xns = b.xns || Roo.bootstrap;
4073 b.xtype = b.xtype || 'Button';
4074 if (typeof(b.listeners) == 'undefined') {
4075 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4078 var btn = Roo.factory(b);
4080 btn.render(this.getButtonContainer());
4084 // render the children.
4087 if(typeof(this.items) != 'undefined'){
4088 var items = this.items;
4091 for(var i =0;i < items.length;i++) {
4092 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4096 this.items = nitems;
4098 // where are these used - they used to be body/close/footer
4102 //this.el.addClass([this.fieldClass, this.cls]);
4106 getAutoCreate : function()
4108 // we will default to modal-body-overflow - might need to remove or make optional later.
4110 cls : 'modal-body enable-modal-body-overflow ',
4111 html : this.html || ''
4116 cls : 'modal-title',
4120 if(this.specificTitle){ // WTF is this?
4125 if (this.allow_close && Roo.bootstrap.version == 3) {
4135 if (this.editableTitle) {
4137 cls: 'form-control roo-editable-title d-none',
4143 if (this.allow_close && Roo.bootstrap.version == 4) {
4153 if(this.size.length){
4154 size = 'modal-' + this.size;
4157 var footer = Roo.bootstrap.version == 3 ?
4159 cls : 'modal-footer',
4163 cls: 'btn-' + this.buttonPosition
4168 { // BS4 uses mr-auto on left buttons....
4169 cls : 'modal-footer'
4180 cls: "modal-dialog " + size,
4183 cls : "modal-content",
4186 cls : 'modal-header',
4201 modal.cls += ' fade';
4207 getChildContainer : function() {
4212 getButtonContainer : function() {
4214 return Roo.bootstrap.version == 4 ?
4215 this.el.select('.modal-footer',true).first()
4216 : this.el.select('.modal-footer div',true).first();
4219 initEvents : function()
4221 if (this.allow_close) {
4222 this.closeEl.on('click', this.hide, this);
4224 Roo.EventManager.onWindowResize(this.resize, this, true);
4225 if (this.editableTitle) {
4226 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4227 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228 this.headerEditEl.on('keyup', function(e) {
4229 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230 this.toggleHeaderInput(false)
4233 this.headerEditEl.on('blur', function(e) {
4234 this.toggleHeaderInput(false)
4243 this.maskEl.setSize(
4244 Roo.lib.Dom.getViewWidth(true),
4245 Roo.lib.Dom.getViewHeight(true)
4248 if (this.fitwindow) {
4252 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4258 if(this.max_width !== 0) {
4260 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4263 this.setSize(w, this.height);
4267 if(this.max_height) {
4268 this.setSize(w,Math.min(
4270 Roo.lib.Dom.getViewportHeight(true) - 60
4276 if(!this.fit_content) {
4277 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4281 this.setSize(w, Math.min(
4283 this.headerEl.getHeight() +
4284 this.footerEl.getHeight() +
4285 this.getChildHeight(this.bodyEl.dom.childNodes),
4286 Roo.lib.Dom.getViewportHeight(true) - 60)
4292 setSize : function(w,h)
4303 if (!this.rendered) {
4306 this.toggleHeaderInput(false);
4307 //this.el.setStyle('display', 'block');
4308 this.el.removeClass('hideing');
4309 this.el.dom.style.display='block';
4311 Roo.get(document.body).addClass('modal-open');
4313 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4316 this.el.addClass('show');
4317 this.el.addClass('in');
4320 this.el.addClass('show');
4321 this.el.addClass('in');
4324 // not sure how we can show data in here..
4326 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4329 Roo.get(document.body).addClass("x-body-masked");
4331 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4332 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333 this.maskEl.dom.style.display = 'block';
4334 this.maskEl.addClass('show');
4339 this.fireEvent('show', this);
4341 // set zindex here - otherwise it appears to be ignored...
4342 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4345 this.items.forEach( function(e) {
4346 e.layout ? e.layout() : false;
4354 if(this.fireEvent("beforehide", this) !== false){
4356 this.maskEl.removeClass('show');
4358 this.maskEl.dom.style.display = '';
4359 Roo.get(document.body).removeClass("x-body-masked");
4360 this.el.removeClass('in');
4361 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4363 if(this.animate){ // why
4364 this.el.addClass('hideing');
4365 this.el.removeClass('show');
4367 if (!this.el.hasClass('hideing')) {
4368 return; // it's been shown again...
4371 this.el.dom.style.display='';
4373 Roo.get(document.body).removeClass('modal-open');
4374 this.el.removeClass('hideing');
4378 this.el.removeClass('show');
4379 this.el.dom.style.display='';
4380 Roo.get(document.body).removeClass('modal-open');
4383 this.fireEvent('hide', this);
4386 isVisible : function()
4389 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4393 addButton : function(str, cb)
4397 var b = Roo.apply({}, { html : str } );
4398 b.xns = b.xns || Roo.bootstrap;
4399 b.xtype = b.xtype || 'Button';
4400 if (typeof(b.listeners) == 'undefined') {
4401 b.listeners = { click : cb.createDelegate(this) };
4404 var btn = Roo.factory(b);
4406 btn.render(this.getButtonContainer());
4412 setDefaultButton : function(btn)
4414 //this.el.select('.modal-footer').()
4417 resizeTo: function(w,h)
4419 this.dialogEl.setWidth(w);
4421 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4423 this.bodyEl.setHeight(h - diff);
4425 this.fireEvent('resize', this);
4428 setContentSize : function(w, h)
4432 onButtonClick: function(btn,e)
4435 this.fireEvent('btnclick', btn.name, e);
4438 * Set the title of the Dialog
4439 * @param {String} str new Title
4441 setTitle: function(str) {
4442 this.titleEl.dom.innerHTML = str;
4446 * Set the body of the Dialog
4447 * @param {String} str new Title
4449 setBody: function(str) {
4450 this.bodyEl.dom.innerHTML = str;
4453 * Set the body of the Dialog using the template
4454 * @param {Obj} data - apply this data to the template and replace the body contents.
4456 applyBody: function(obj)
4459 Roo.log("Error - using apply Body without a template");
4462 this.tmpl.overwrite(this.bodyEl, obj);
4465 getChildHeight : function(child_nodes)
4469 child_nodes.length == 0
4474 var child_height = 0;
4476 for(var i = 0; i < child_nodes.length; i++) {
4479 * for modal with tabs...
4480 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4482 var layout_childs = child_nodes[i].childNodes;
4484 for(var j = 0; j < layout_childs.length; j++) {
4486 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4488 var layout_body_childs = layout_childs[j].childNodes;
4490 for(var k = 0; k < layout_body_childs.length; k++) {
4492 if(layout_body_childs[k].classList.contains('navbar')) {
4493 child_height += layout_body_childs[k].offsetHeight;
4497 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4499 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4501 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4503 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4519 child_height += child_nodes[i].offsetHeight;
4520 // Roo.log(child_nodes[i].offsetHeight);
4523 return child_height;
4525 toggleHeaderInput : function(is_edit)
4527 if (!this.editableTitle) {
4528 return; // not editable.
4530 if (is_edit && this.is_header_editing) {
4531 return; // already editing..
4535 this.headerEditEl.dom.value = this.title;
4536 this.headerEditEl.removeClass('d-none');
4537 this.headerEditEl.dom.focus();
4538 this.titleEl.addClass('d-none');
4540 this.is_header_editing = true;
4543 // flip back to not editing.
4544 this.title = this.headerEditEl.dom.value;
4545 this.headerEditEl.addClass('d-none');
4546 this.titleEl.removeClass('d-none');
4547 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548 this.is_header_editing = false;
4549 this.fireEvent('titlechanged', this, this.title);
4558 Roo.apply(Roo.bootstrap.Modal, {
4560 * Button config that displays a single OK button
4569 * Button config that displays Yes and No buttons
4585 * Button config that displays OK and Cancel buttons
4600 * Button config that displays Yes, No and Cancel buttons
4625 * messagebox - can be used as a replace
4629 * @class Roo.MessageBox
4630 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4639 // process text value...
4643 // Show a dialog using config options:
4645 title:'Save Changes?',
4646 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647 buttons: Roo.Msg.YESNOCANCEL,
4654 Roo.bootstrap.MessageBox = function(){
4655 var dlg, opt, mask, waitTimer;
4656 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657 var buttons, activeTextEl, bwidth;
4661 var handleButton = function(button){
4663 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4667 var handleHide = function(){
4669 dlg.el.removeClass(opt.cls);
4672 // Roo.TaskMgr.stop(waitTimer);
4673 // waitTimer = null;
4678 var updateButtons = function(b){
4681 buttons["ok"].hide();
4682 buttons["cancel"].hide();
4683 buttons["yes"].hide();
4684 buttons["no"].hide();
4685 dlg.footerEl.hide();
4689 dlg.footerEl.show();
4690 for(var k in buttons){
4691 if(typeof buttons[k] != "function"){
4694 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695 width += buttons[k].el.getWidth()+15;
4705 var handleEsc = function(d, k, e){
4706 if(opt && opt.closable !== false){
4716 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717 * @return {Roo.BasicDialog} The BasicDialog element
4719 getDialog : function(){
4721 dlg = new Roo.bootstrap.Modal( {
4724 //constraintoviewport:false,
4726 //collapsible : false,
4731 //buttonAlign:"center",
4732 closeClick : function(){
4733 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4736 handleButton("cancel");
4741 dlg.on("hide", handleHide);
4743 //dlg.addKeyListener(27, handleEsc);
4745 this.buttons = buttons;
4746 var bt = this.buttonText;
4747 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4752 bodyEl = dlg.bodyEl.createChild({
4754 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755 '<textarea class="roo-mb-textarea"></textarea>' +
4756 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4758 msgEl = bodyEl.dom.firstChild;
4759 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760 textboxEl.enableDisplayMode();
4761 textboxEl.addKeyListener([10,13], function(){
4762 if(dlg.isVisible() && opt && opt.buttons){
4765 }else if(opt.buttons.yes){
4766 handleButton("yes");
4770 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771 textareaEl.enableDisplayMode();
4772 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773 progressEl.enableDisplayMode();
4775 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776 var pf = progressEl.dom.firstChild;
4778 pp = Roo.get(pf.firstChild);
4779 pp.setHeight(pf.offsetHeight);
4787 * Updates the message box body text
4788 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789 * the XHTML-compliant non-breaking space character '&#160;')
4790 * @return {Roo.MessageBox} This message box
4792 updateText : function(text)
4794 if(!dlg.isVisible() && !opt.width){
4795 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4798 msgEl.innerHTML = text || ' ';
4800 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4803 Math.min(opt.width || cw , this.maxWidth),
4804 Math.max(opt.minWidth || this.minWidth, bwidth)
4807 activeTextEl.setWidth(w);
4809 if(dlg.isVisible()){
4810 dlg.fixedcenter = false;
4812 // to big, make it scroll. = But as usual stupid IE does not support
4815 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4819 bodyEl.dom.style.height = '';
4820 bodyEl.dom.style.overflowY = '';
4823 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.overflowX = '';
4828 dlg.setContentSize(w, bodyEl.getHeight());
4829 if(dlg.isVisible()){
4830 dlg.fixedcenter = true;
4836 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4837 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840 * @return {Roo.MessageBox} This message box
4842 updateProgress : function(value, text){
4844 this.updateText(text);
4847 if (pp) { // weird bug on my firefox - for some reason this is not defined
4848 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855 * Returns true if the message box is currently displayed
4856 * @return {Boolean} True if the message box is visible, else false
4858 isVisible : function(){
4859 return dlg && dlg.isVisible();
4863 * Hides the message box if it is displayed
4866 if(this.isVisible()){
4872 * Displays a new message box, or reinitializes an existing message box, based on the config options
4873 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874 * The following config object properties are supported:
4876 Property Type Description
4877 ---------- --------------- ------------------------------------------------------------------------------------
4878 animEl String/Element An id or Element from which the message box should animate as it opens and
4879 closes (defaults to undefined)
4880 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable Boolean False to hide the top-right close button (defaults to true). Note that
4883 progress and wait dialogs will ignore this property and always hide the
4884 close button as they can only be closed programmatically.
4885 cls String A custom CSS class to apply to the message box element
4886 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4887 displayed (defaults to 75)
4888 fn Function A callback function to execute after closing the dialog. The arguments to the
4889 function will be btn (the name of the button that was clicked, if applicable,
4890 e.g. "ok"), and text (the value of the active text field, if applicable).
4891 Progress and wait dialogs will ignore this option since they do not respond to
4892 user actions and can only be closed programmatically, so any required function
4893 should be called by the same code after it closes the dialog.
4894 icon String A CSS class that provides a background image to be used as an icon for
4895 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4897 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4898 modal Boolean False to allow user interaction with the page while the message box is
4899 displayed (defaults to true)
4900 msg String A string that will replace the existing message box body text (defaults
4901 to the XHTML-compliant non-breaking space character ' ')
4902 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4903 progress Boolean True to display a progress bar (defaults to false)
4904 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4907 title String The title text
4908 value String The string value to set into the active textbox element if displayed
4909 wait Boolean True to display a progress bar (defaults to false)
4910 width Number The width of the dialog in pixels
4917 msg: 'Please enter your address:',
4919 buttons: Roo.MessageBox.OKCANCEL,
4922 animEl: 'addAddressBtn'
4925 * @param {Object} config Configuration options
4926 * @return {Roo.MessageBox} This message box
4928 show : function(options)
4931 // this causes nightmares if you show one dialog after another
4932 // especially on callbacks..
4934 if(this.isVisible()){
4937 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4939 Roo.log("New Dialog Message:" + options.msg )
4940 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4944 var d = this.getDialog();
4946 d.setTitle(opt.title || " ");
4947 d.closeEl.setDisplayed(opt.closable !== false);
4948 activeTextEl = textboxEl;
4949 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4954 textareaEl.setHeight(typeof opt.multiline == "number" ?
4955 opt.multiline : this.defaultTextHeight);
4956 activeTextEl = textareaEl;
4965 progressEl.setDisplayed(opt.progress === true);
4967 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4969 this.updateProgress(0);
4970 activeTextEl.dom.value = opt.value || "";
4972 dlg.setDefaultButton(activeTextEl);
4974 var bs = opt.buttons;
4978 }else if(bs && bs.yes){
4979 db = buttons["yes"];
4981 dlg.setDefaultButton(db);
4983 bwidth = updateButtons(opt.buttons);
4984 this.updateText(opt.msg);
4986 d.el.addClass(opt.cls);
4988 d.proxyDrag = opt.proxyDrag === true;
4989 d.modal = opt.modal !== false;
4990 d.mask = opt.modal !== false ? mask : false;
4992 // force it to the end of the z-index stack so it gets a cursor in FF
4993 document.body.appendChild(dlg.el.dom);
4994 d.animateTarget = null;
4995 d.show(options.animEl);
5001 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5002 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003 * and closing the message box when the process is complete.
5004 * @param {String} title The title bar text
5005 * @param {String} msg The message box body text
5006 * @return {Roo.MessageBox} This message box
5008 progress : function(title, msg){
5015 minWidth: this.minProgressWidth,
5022 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023 * If a callback function is passed it will be called after the user clicks the button, and the
5024 * id of the button that was clicked will be passed as the only parameter to the callback
5025 * (could also be the top-right close button).
5026 * @param {String} title The title bar text
5027 * @param {String} msg The message box body text
5028 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029 * @param {Object} scope (optional) The scope of the callback function
5030 * @return {Roo.MessageBox} This message box
5032 alert : function(title, msg, fn, scope)
5047 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5048 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049 * You are responsible for closing the message box when the process is complete.
5050 * @param {String} msg The message box body text
5051 * @param {String} title (optional) The title bar text
5052 * @return {Roo.MessageBox} This message box
5054 wait : function(msg, title){
5065 waitTimer = Roo.TaskMgr.start({
5067 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5075 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078 * @param {String} title The title bar text
5079 * @param {String} msg The message box body text
5080 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081 * @param {Object} scope (optional) The scope of the callback function
5082 * @return {Roo.MessageBox} This message box
5084 confirm : function(title, msg, fn, scope){
5088 buttons: this.YESNO,
5097 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5099 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100 * (could also be the top-right close button) and the text that was entered will be passed as the two
5101 * parameters to the callback.
5102 * @param {String} title The title bar text
5103 * @param {String} msg The message box body text
5104 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105 * @param {Object} scope (optional) The scope of the callback function
5106 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108 * @return {Roo.MessageBox} This message box
5110 prompt : function(title, msg, fn, scope, multiline){
5114 buttons: this.OKCANCEL,
5119 multiline: multiline,
5126 * Button config that displays a single OK button
5131 * Button config that displays Yes and No buttons
5134 YESNO : {yes:true, no:true},
5136 * Button config that displays OK and Cancel buttons
5139 OKCANCEL : {ok:true, cancel:true},
5141 * Button config that displays Yes, No and Cancel buttons
5144 YESNOCANCEL : {yes:true, no:true, cancel:true},
5147 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5150 defaultTextHeight : 75,
5152 * The maximum width in pixels of the message box (defaults to 600)
5157 * The minimum width in pixels of the message box (defaults to 100)
5162 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5163 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5166 minProgressWidth : 250,
5168 * An object containing the default button text strings that can be overriden for localized language support.
5169 * Supported properties are: ok, cancel, yes and no.
5170 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5183 * Shorthand for {@link Roo.MessageBox}
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5195 * @class Roo.bootstrap.Navbar
5196 * @extends Roo.bootstrap.Component
5197 * Bootstrap Navbar class
5200 * Create a new Navbar
5201 * @param {Object} config The config object
5205 Roo.bootstrap.Navbar = function(config){
5206 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5210 * @event beforetoggle
5211 * Fire before toggle the menu
5212 * @param {Roo.EventObject} e
5214 "beforetoggle" : true
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5227 getAutoCreate : function(){
5230 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5234 initEvents :function ()
5236 //Roo.log(this.el.select('.navbar-toggle',true));
5237 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5246 var size = this.el.getSize();
5247 this.maskEl.setSize(size.width, size.height);
5248 this.maskEl.enableDisplayMode("block");
5257 getChildContainer : function()
5259 if (this.el && this.el.select('.collapse').getCount()) {
5260 return this.el.select('.collapse',true).first();
5275 onToggle : function()
5278 if(this.fireEvent('beforetoggle', this) === false){
5281 var ce = this.el.select('.navbar-collapse',true).first();
5283 if (!ce.hasClass('show')) {
5293 * Expand the navbar pulldown
5295 expand : function ()
5298 var ce = this.el.select('.navbar-collapse',true).first();
5299 if (ce.hasClass('collapsing')) {
5302 ce.dom.style.height = '';
5304 ce.addClass('in'); // old...
5305 ce.removeClass('collapse');
5306 ce.addClass('show');
5307 var h = ce.getHeight();
5309 ce.removeClass('show');
5310 // at this point we should be able to see it..
5311 ce.addClass('collapsing');
5313 ce.setHeight(0); // resize it ...
5314 ce.on('transitionend', function() {
5315 //Roo.log('done transition');
5316 ce.removeClass('collapsing');
5317 ce.addClass('show');
5318 ce.removeClass('collapse');
5320 ce.dom.style.height = '';
5321 }, this, { single: true} );
5323 ce.dom.scrollTop = 0;
5326 * Collapse the navbar pulldown
5328 collapse : function()
5330 var ce = this.el.select('.navbar-collapse',true).first();
5332 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333 // it's collapsed or collapsing..
5336 ce.removeClass('in'); // old...
5337 ce.setHeight(ce.getHeight());
5338 ce.removeClass('show');
5339 ce.addClass('collapsing');
5341 ce.on('transitionend', function() {
5342 ce.dom.style.height = '';
5343 ce.removeClass('collapsing');
5344 ce.addClass('collapse');
5345 }, this, { single: true} );
5365 * @class Roo.bootstrap.NavSimplebar
5366 * @extends Roo.bootstrap.Navbar
5367 * Bootstrap Sidebar class
5369 * @cfg {Boolean} inverse is inverted color
5371 * @cfg {String} type (nav | pills | tabs)
5372 * @cfg {Boolean} arrangement stacked | justified
5373 * @cfg {String} align (left | right) alignment
5375 * @cfg {Boolean} main (true|false) main nav bar? default false
5376 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5378 * @cfg {String} tag (header|footer|nav|div) default is nav
5380 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5384 * Create a new Sidebar
5385 * @param {Object} config The config object
5389 Roo.bootstrap.NavSimplebar = function(config){
5390 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5409 getAutoCreate : function(){
5413 tag : this.tag || 'div',
5414 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5416 if (['light','white'].indexOf(this.weight) > -1) {
5417 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5419 cfg.cls += ' bg-' + this.weight;
5422 cfg.cls += ' navbar-inverse';
5426 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5428 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5437 cls: 'nav nav-' + this.xtype,
5443 this.type = this.type || 'nav';
5444 if (['tabs','pills'].indexOf(this.type) != -1) {
5445 cfg.cn[0].cls += ' nav-' + this.type
5449 if (this.type!=='nav') {
5450 Roo.log('nav type must be nav/tabs/pills')
5452 cfg.cn[0].cls += ' navbar-nav'
5458 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459 cfg.cn[0].cls += ' nav-' + this.arrangement;
5463 if (this.align === 'right') {
5464 cfg.cn[0].cls += ' navbar-right';
5489 * navbar-expand-md fixed-top
5493 * @class Roo.bootstrap.NavHeaderbar
5494 * @extends Roo.bootstrap.NavSimplebar
5495 * Bootstrap Sidebar class
5497 * @cfg {String} brand what is brand
5498 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499 * @cfg {String} brand_href href of the brand
5500 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5501 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5506 * Create a new Sidebar
5507 * @param {Object} config The config object
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5523 desktopCenter : false,
5526 getAutoCreate : function(){
5529 tag: this.nav || 'nav',
5530 cls: 'navbar navbar-expand-md',
5536 if (this.desktopCenter) {
5537 cn.push({cls : 'container', cn : []});
5545 cls: 'navbar-toggle navbar-toggler',
5546 'data-toggle': 'collapse',
5551 html: 'Toggle navigation'
5555 cls: 'icon-bar navbar-toggler-icon'
5568 cn.push( Roo.bootstrap.version == 4 ? btn : {
5570 cls: 'navbar-header',
5579 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5583 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5585 if (['light','white'].indexOf(this.weight) > -1) {
5586 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5588 cfg.cls += ' bg-' + this.weight;
5591 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5594 // tag can override this..
5596 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5599 if (this.brand !== '') {
5600 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5603 href: this.brand_href ? this.brand_href : '#',
5604 cls: 'navbar-brand',
5612 cfg.cls += ' main-nav';
5620 getHeaderChildContainer : function()
5622 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623 return this.el.select('.navbar-header',true).first();
5626 return this.getChildContainer();
5629 getChildContainer : function()
5632 return this.el.select('.roo-navbar-collapse',true).first();
5637 initEvents : function()
5639 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5641 if (this.autohide) {
5646 Roo.get(document).on('scroll',function(e) {
5647 var ns = Roo.get(document).getScroll().top;
5648 var os = prevScroll;
5652 ft.removeClass('slideDown');
5653 ft.addClass('slideUp');
5656 ft.removeClass('slideUp');
5657 ft.addClass('slideDown');
5678 * @class Roo.bootstrap.NavSidebar
5679 * @extends Roo.bootstrap.Navbar
5680 * Bootstrap Sidebar class
5683 * Create a new Sidebar
5684 * @param {Object} config The config object
5688 Roo.bootstrap.NavSidebar = function(config){
5689 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5694 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5696 getAutoCreate : function(){
5701 cls: 'sidebar sidebar-nav'
5723 * @class Roo.bootstrap.NavGroup
5724 * @extends Roo.bootstrap.Component
5725 * Bootstrap NavGroup class
5726 * @cfg {String} align (left|right)
5727 * @cfg {Boolean} inverse
5728 * @cfg {String} type (nav|pills|tab) default nav
5729 * @cfg {String} navId - reference Id for navbar.
5733 * Create a new nav group
5734 * @param {Object} config The config object
5737 Roo.bootstrap.NavGroup = function(config){
5738 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5741 Roo.bootstrap.NavGroup.register(this);
5745 * Fires when the active item changes
5746 * @param {Roo.bootstrap.NavGroup} this
5747 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5766 getAutoCreate : function()
5768 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774 if (Roo.bootstrap.version == 4) {
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cls += ' nav-' + this.type;
5778 // trying to remove so header bar can right align top?
5779 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780 // do not use on header bar...
5781 cfg.cls += ' navbar-nav';
5786 if (['tabs','pills'].indexOf(this.type) != -1) {
5787 cfg.cls += ' nav-' + this.type
5789 if (this.type !== 'nav') {
5790 Roo.log('nav type must be nav/tabs/pills')
5792 cfg.cls += ' navbar-nav'
5796 if (this.parent() && this.parent().sidebar) {
5799 cls: 'dashboard-menu sidebar-menu'
5805 if (this.form === true) {
5808 cls: 'navbar-form form-inline'
5810 //nav navbar-right ml-md-auto
5811 if (this.align === 'right') {
5812 cfg.cls += ' navbar-right ml-md-auto';
5814 cfg.cls += ' navbar-left';
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' mr-auto';
5825 cfg.cls += ' navbar-inverse';
5833 * sets the active Navigation item
5834 * @param {Roo.bootstrap.NavItem} the new current navitem
5836 setActiveItem : function(item)
5839 Roo.each(this.navItems, function(v){
5844 v.setActive(false, true);
5851 item.setActive(true, true);
5852 this.fireEvent('changed', this, item, prev);
5857 * gets the active Navigation item
5858 * @return {Roo.bootstrap.NavItem} the current navitem
5860 getActive : function()
5864 Roo.each(this.navItems, function(v){
5875 indexOfNav : function()
5879 Roo.each(this.navItems, function(v,i){
5890 * adds a Navigation item
5891 * @param {Roo.bootstrap.NavItem} the navitem to add
5893 addItem : function(cfg)
5895 if (this.form && Roo.bootstrap.version == 4) {
5898 var cn = new Roo.bootstrap.NavItem(cfg);
5900 cn.parentId = this.id;
5901 cn.onRender(this.el, null);
5905 * register a Navigation item
5906 * @param {Roo.bootstrap.NavItem} the navitem to add
5908 register : function(item)
5910 this.navItems.push( item);
5911 item.navId = this.navId;
5916 * clear all the Navigation item
5919 clearAll : function()
5922 this.el.dom.innerHTML = '';
5925 getNavItem: function(tabId)
5928 Roo.each(this.navItems, function(e) {
5929 if (e.tabId == tabId) {
5939 setActiveNext : function()
5941 var i = this.indexOfNav(this.getActive());
5942 if (i > this.navItems.length) {
5945 this.setActiveItem(this.navItems[i+1]);
5947 setActivePrev : function()
5949 var i = this.indexOfNav(this.getActive());
5953 this.setActiveItem(this.navItems[i-1]);
5955 clearWasActive : function(except) {
5956 Roo.each(this.navItems, function(e) {
5957 if (e.tabId != except.tabId && e.was_active) {
5958 e.was_active = false;
5965 getWasActive : function ()
5968 Roo.each(this.navItems, function(e) {
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5987 * register a Navigation Group
5988 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5990 register : function(navgrp)
5992 this.groups[navgrp.navId] = navgrp;
5996 * fetch a Navigation Group based on the navigation ID
5997 * @param {string} the navgroup to add
5998 * @returns {Roo.bootstrap.NavGroup} the navgroup
6000 get: function(navId) {
6001 if (typeof(this.groups[navId]) == 'undefined') {
6003 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6005 return this.groups[navId] ;
6020 * @class Roo.bootstrap.NavItem
6021 * @extends Roo.bootstrap.Component
6022 * Bootstrap Navbar.NavItem class
6023 * @cfg {String} href link to
6024 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6026 * @cfg {String} html content of button
6027 * @cfg {String} badge text inside badge
6028 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029 * @cfg {String} glyphicon DEPRICATED - use fa
6030 * @cfg {String} icon DEPRICATED - use fa
6031 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032 * @cfg {Boolean} active Is item active
6033 * @cfg {Boolean} disabled Is item disabled
6035 * @cfg {Boolean} preventDefault (true | false) default false
6036 * @cfg {String} tabId the tab that this item activates.
6037 * @cfg {String} tagtype (a|span) render as a href or span?
6038 * @cfg {Boolean} animateRef (true|false) link to element default false
6041 * Create a new Navbar Item
6042 * @param {Object} config The config object
6044 Roo.bootstrap.NavItem = function(config){
6045 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6050 * The raw click event for the entire grid.
6051 * @param {Roo.EventObject} e
6056 * Fires when the active item active state changes
6057 * @param {Roo.bootstrap.NavItem} this
6058 * @param {boolean} state the new state
6064 * Fires when scroll to element
6065 * @param {Roo.bootstrap.NavItem} this
6066 * @param {Object} options
6067 * @param {Roo.EventObject} e
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6084 preventDefault : false,
6092 button_outline : false,
6096 getAutoCreate : function(){
6104 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6106 if (this.disabled) {
6107 cfg.cls += ' disabled';
6111 if (this.button_weight.length) {
6112 cfg.tag = this.href ? 'a' : 'button';
6113 cfg.html = this.html || '';
6114 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6116 cfg.href = this.href;
6119 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6122 // menu .. should add dropdown-menu class - so no need for carat..
6124 if (this.badge !== '') {
6126 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6131 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6135 href : this.href || "#",
6136 html: this.html || ''
6139 if (this.tagtype == 'a') {
6140 cfg.cn[0].cls = 'nav-link';
6143 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6146 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6148 if(this.glyphicon) {
6149 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6154 cfg.cn[0].html += " <span class='caret'></span>";
6158 if (this.badge !== '') {
6160 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6168 onRender : function(ct, position)
6170 // Roo.log("Call onRender: " + this.xtype);
6171 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6175 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176 this.navLink = this.el.select('.nav-link',true).first();
6181 initEvents: function()
6183 if (typeof (this.menu) != 'undefined') {
6184 this.menu.parentType = this.xtype;
6185 this.menu.triggerEl = this.el;
6186 this.menu = this.addxtype(Roo.apply({}, this.menu));
6189 this.el.select('a',true).on('click', this.onClick, this);
6191 if(this.tagtype == 'span'){
6192 this.el.select('span',true).on('click', this.onClick, this);
6195 // at this point parent should be available..
6196 this.parent().register(this);
6199 onClick : function(e)
6201 if (e.getTarget('.dropdown-menu-item')) {
6202 // did you click on a menu itemm.... - then don't trigger onclick..
6207 this.preventDefault ||
6210 Roo.log("NavItem - prevent Default?");
6214 if (this.disabled) {
6218 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219 if (tg && tg.transition) {
6220 Roo.log("waiting for the transitionend");
6226 //Roo.log("fire event clicked");
6227 if(this.fireEvent('click', this, e) === false){
6231 if(this.tagtype == 'span'){
6235 //Roo.log(this.href);
6236 var ael = this.el.select('a',true).first();
6239 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242 return; // ignore... - it's a 'hash' to another page.
6244 Roo.log("NavItem - prevent Default?");
6246 this.scrollToElement(e);
6250 var p = this.parent();
6252 if (['tabs','pills'].indexOf(p.type)!==-1) {
6253 if (typeof(p.setActiveItem) !== 'undefined') {
6254 p.setActiveItem(this);
6258 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260 // remove the collapsed menu expand...
6261 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6265 isActive: function () {
6268 setActive : function(state, fire, is_was_active)
6270 if (this.active && !state && this.navId) {
6271 this.was_active = true;
6272 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6274 nv.clearWasActive(this);
6278 this.active = state;
6281 this.el.removeClass('active');
6282 this.navLink ? this.navLink.removeClass('active') : false;
6283 } else if (!this.el.hasClass('active')) {
6285 this.el.addClass('active');
6286 if (Roo.bootstrap.version == 4 && this.navLink ) {
6287 this.navLink.addClass('active');
6292 this.fireEvent('changed', this, state);
6295 // show a panel if it's registered and related..
6297 if (!this.navId || !this.tabId || !state || is_was_active) {
6301 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6305 var pan = tg.getPanelByName(this.tabId);
6309 // if we can not flip to new panel - go back to old nav highlight..
6310 if (false == tg.showPanel(pan)) {
6311 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6313 var onav = nv.getWasActive();
6315 onav.setActive(true, false, true);
6324 // this should not be here...
6325 setDisabled : function(state)
6327 this.disabled = state;
6329 this.el.removeClass('disabled');
6330 } else if (!this.el.hasClass('disabled')) {
6331 this.el.addClass('disabled');
6337 * Fetch the element to display the tooltip on.
6338 * @return {Roo.Element} defaults to this.el
6340 tooltipEl : function()
6342 return this.el.select('' + this.tagtype + '', true).first();
6345 scrollToElement : function(e)
6347 var c = document.body;
6350 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6352 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353 c = document.documentElement;
6356 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362 var o = target.calcOffsetsTo(c);
6369 this.fireEvent('scrollto', this, options, e);
6371 Roo.get(c).scrollTo('top', options.value, true);
6384 * <span> icon </span>
6385 * <span> text </span>
6386 * <span>badge </span>
6390 * @class Roo.bootstrap.NavSidebarItem
6391 * @extends Roo.bootstrap.NavItem
6392 * Bootstrap Navbar.NavSidebarItem class
6393 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394 * {Boolean} open is the menu open
6395 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397 * {String} buttonSize (sm|md|lg)the extra classes for the button
6398 * {Boolean} showArrow show arrow next to the text (default true)
6400 * Create a new Navbar Button
6401 * @param {Object} config The config object
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6409 * The raw click event for the entire grid.
6410 * @param {Roo.EventObject} e
6415 * Fires when the active item active state changes
6416 * @param {Roo.bootstrap.NavSidebarItem} this
6417 * @param {boolean} state the new state
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6427 badgeWeight : 'default',
6433 buttonWeight : 'default',
6439 getAutoCreate : function(){
6444 href : this.href || '#',
6450 if(this.buttonView){
6453 href : this.href || '#',
6454 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6467 cfg.cls += ' active';
6470 if (this.disabled) {
6471 cfg.cls += ' disabled';
6474 cfg.cls += ' open x-open';
6477 if (this.glyphicon || this.icon) {
6478 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6479 a.cn.push({ tag : 'i', cls : c }) ;
6482 if(!this.buttonView){
6485 html : this.html || ''
6492 if (this.badge !== '') {
6493 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6499 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6502 a.cls += ' dropdown-toggle treeview' ;
6508 initEvents : function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 if(this.badge !== ''){
6519 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6524 onClick : function(e)
6531 if(this.preventDefault){
6535 this.fireEvent('click', this, e);
6538 disable : function()
6540 this.setDisabled(true);
6545 this.setDisabled(false);
6548 setDisabled : function(state)
6550 if(this.disabled == state){
6554 this.disabled = state;
6557 this.el.addClass('disabled');
6561 this.el.removeClass('disabled');
6566 setActive : function(state)
6568 if(this.active == state){
6572 this.active = state;
6575 this.el.addClass('active');
6579 this.el.removeClass('active');
6584 isActive: function ()
6589 setBadge : function(str)
6595 this.badgeEl.dom.innerHTML = str;
6613 * @class Roo.bootstrap.breadcrumb.Nav
6614 * @extends Roo.bootstrap.Component
6615 * Bootstrap Breadcrumb Nav Class
6617 * @children Roo.bootstrap.breadcrumb.Item
6620 * Create a new breadcrumb.Nav
6621 * @param {Object} config The config object
6624 Roo.bootstrap.breadcrumb.Nav = function(config){
6625 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6630 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6632 getAutoCreate : function()
6649 initEvents: function()
6651 this.olEl = this.el.select('ol',true).first();
6653 getChildContainer : function()
6669 * @class Roo.bootstrap.breadcrumb.Nav
6670 * @extends Roo.bootstrap.Component
6671 * Bootstrap Breadcrumb Nav Class
6673 * @children Roo.bootstrap.breadcrumb.Component
6674 * @cfg {String} html the content of the link.
6675 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6676 * @cfg {Boolean} active is it active
6680 * Create a new breadcrumb.Nav
6681 * @param {Object} config The config object
6684 Roo.bootstrap.breadcrumb.Item = function(config){
6685 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6690 * The img click event for the img.
6691 * @param {Roo.EventObject} e
6698 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6703 getAutoCreate : function()
6709 if (this.href !== false) {
6716 cfg.html = this.html;
6722 initEvents: function()
6725 this.el.select('a', true).first().onClick(this.onClick, this)
6729 onClick : function(e)
6732 this.fireEvent('click',this, e);
6745 * @class Roo.bootstrap.Row
6746 * @extends Roo.bootstrap.Component
6747 * Bootstrap Row class (contains columns...)
6751 * @param {Object} config The config object
6754 Roo.bootstrap.Row = function(config){
6755 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6758 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6760 getAutoCreate : function(){
6779 * @class Roo.bootstrap.Pagination
6780 * @extends Roo.bootstrap.Component
6781 * Bootstrap Pagination class
6782 * @cfg {String} size xs | sm | md | lg
6783 * @cfg {Boolean} inverse false | true
6786 * Create a new Pagination
6787 * @param {Object} config The config object
6790 Roo.bootstrap.Pagination = function(config){
6791 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6794 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6800 getAutoCreate : function(){
6806 cfg.cls += ' inverse';
6812 cfg.cls += " " + this.cls;
6830 * @class Roo.bootstrap.PaginationItem
6831 * @extends Roo.bootstrap.Component
6832 * Bootstrap PaginationItem class
6833 * @cfg {String} html text
6834 * @cfg {String} href the link
6835 * @cfg {Boolean} preventDefault (true | false) default true
6836 * @cfg {Boolean} active (true | false) default false
6837 * @cfg {Boolean} disabled default false
6841 * Create a new PaginationItem
6842 * @param {Object} config The config object
6846 Roo.bootstrap.PaginationItem = function(config){
6847 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6852 * The raw click event for the entire grid.
6853 * @param {Roo.EventObject} e
6859 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6863 preventDefault: true,
6868 getAutoCreate : function(){
6874 href : this.href ? this.href : '#',
6875 html : this.html ? this.html : ''
6885 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6889 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6895 initEvents: function() {
6897 this.el.on('click', this.onClick, this);
6900 onClick : function(e)
6902 Roo.log('PaginationItem on click ');
6903 if(this.preventDefault){
6911 this.fireEvent('click', this, e);
6927 * @class Roo.bootstrap.Slider
6928 * @extends Roo.bootstrap.Component
6929 * Bootstrap Slider class
6932 * Create a new Slider
6933 * @param {Object} config The config object
6936 Roo.bootstrap.Slider = function(config){
6937 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6940 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6942 getAutoCreate : function(){
6946 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6950 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6962 * Ext JS Library 1.1.1
6963 * Copyright(c) 2006-2007, Ext JS, LLC.
6965 * Originally Released Under LGPL - original licence link has changed is not relivant.
6968 * <script type="text/javascript">
6973 * @class Roo.grid.ColumnModel
6974 * @extends Roo.util.Observable
6975 * This is the default implementation of a ColumnModel used by the Grid. It defines
6976 * the columns in the grid.
6979 var colModel = new Roo.grid.ColumnModel([
6980 {header: "Ticker", width: 60, sortable: true, locked: true},
6981 {header: "Company Name", width: 150, sortable: true},
6982 {header: "Market Cap.", width: 100, sortable: true},
6983 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6984 {header: "Employees", width: 100, sortable: true, resizable: false}
6989 * The config options listed for this class are options which may appear in each
6990 * individual column definition.
6991 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6993 * @param {Object} config An Array of column config objects. See this class's
6994 * config objects for details.
6996 Roo.grid.ColumnModel = function(config){
6998 * The config passed into the constructor
7000 this.config = config;
7003 // if no id, create one
7004 // if the column does not have a dataIndex mapping,
7005 // map it to the order it is in the config
7006 for(var i = 0, len = config.length; i < len; i++){
7008 if(typeof c.dataIndex == "undefined"){
7011 if(typeof c.renderer == "string"){
7012 c.renderer = Roo.util.Format[c.renderer];
7014 if(typeof c.id == "undefined"){
7017 if(c.editor && c.editor.xtype){
7018 c.editor = Roo.factory(c.editor, Roo.grid);
7020 if(c.editor && c.editor.isFormField){
7021 c.editor = new Roo.grid.GridEditor(c.editor);
7023 this.lookup[c.id] = c;
7027 * The width of columns which have no width specified (defaults to 100)
7030 this.defaultWidth = 100;
7033 * Default sortable of columns which have no sortable specified (defaults to false)
7036 this.defaultSortable = false;
7040 * @event widthchange
7041 * Fires when the width of a column changes.
7042 * @param {ColumnModel} this
7043 * @param {Number} columnIndex The column index
7044 * @param {Number} newWidth The new width
7046 "widthchange": true,
7048 * @event headerchange
7049 * Fires when the text of a header changes.
7050 * @param {ColumnModel} this
7051 * @param {Number} columnIndex The column index
7052 * @param {Number} newText The new header text
7054 "headerchange": true,
7056 * @event hiddenchange
7057 * Fires when a column is hidden or "unhidden".
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Boolean} hidden true if hidden, false otherwise
7062 "hiddenchange": true,
7064 * @event columnmoved
7065 * Fires when a column is moved.
7066 * @param {ColumnModel} this
7067 * @param {Number} oldIndex
7068 * @param {Number} newIndex
7070 "columnmoved" : true,
7072 * @event columlockchange
7073 * Fires when a column's locked state is changed
7074 * @param {ColumnModel} this
7075 * @param {Number} colIndex
7076 * @param {Boolean} locked true if locked
7078 "columnlockchange" : true
7080 Roo.grid.ColumnModel.superclass.constructor.call(this);
7082 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7084 * @cfg {String} header The header text to display in the Grid view.
7087 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7088 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7089 * specified, the column's index is used as an index into the Record's data Array.
7092 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7093 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7096 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7097 * Defaults to the value of the {@link #defaultSortable} property.
7098 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7101 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7104 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7107 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7110 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7113 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7114 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7115 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7116 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7119 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7122 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7125 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7128 * @cfg {String} cursor (Optional)
7131 * @cfg {String} tooltip (Optional)
7134 * @cfg {Number} xs (Optional)
7137 * @cfg {Number} sm (Optional)
7140 * @cfg {Number} md (Optional)
7143 * @cfg {Number} lg (Optional)
7146 * Returns the id of the column at the specified index.
7147 * @param {Number} index The column index
7148 * @return {String} the id
7150 getColumnId : function(index){
7151 return this.config[index].id;
7155 * Returns the column for a specified id.
7156 * @param {String} id The column id
7157 * @return {Object} the column
7159 getColumnById : function(id){
7160 return this.lookup[id];
7165 * Returns the column for a specified dataIndex.
7166 * @param {String} dataIndex The column dataIndex
7167 * @return {Object|Boolean} the column or false if not found
7169 getColumnByDataIndex: function(dataIndex){
7170 var index = this.findColumnIndex(dataIndex);
7171 return index > -1 ? this.config[index] : false;
7175 * Returns the index for a specified column id.
7176 * @param {String} id The column id
7177 * @return {Number} the index, or -1 if not found
7179 getIndexById : function(id){
7180 for(var i = 0, len = this.config.length; i < len; i++){
7181 if(this.config[i].id == id){
7189 * Returns the index for a specified column dataIndex.
7190 * @param {String} dataIndex The column dataIndex
7191 * @return {Number} the index, or -1 if not found
7194 findColumnIndex : function(dataIndex){
7195 for(var i = 0, len = this.config.length; i < len; i++){
7196 if(this.config[i].dataIndex == dataIndex){
7204 moveColumn : function(oldIndex, newIndex){
7205 var c = this.config[oldIndex];
7206 this.config.splice(oldIndex, 1);
7207 this.config.splice(newIndex, 0, c);
7208 this.dataMap = null;
7209 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7212 isLocked : function(colIndex){
7213 return this.config[colIndex].locked === true;
7216 setLocked : function(colIndex, value, suppressEvent){
7217 if(this.isLocked(colIndex) == value){
7220 this.config[colIndex].locked = value;
7222 this.fireEvent("columnlockchange", this, colIndex, value);
7226 getTotalLockedWidth : function(){
7228 for(var i = 0; i < this.config.length; i++){
7229 if(this.isLocked(i) && !this.isHidden(i)){
7230 this.totalWidth += this.getColumnWidth(i);
7236 getLockedCount : function(){
7237 for(var i = 0, len = this.config.length; i < len; i++){
7238 if(!this.isLocked(i)){
7243 return this.config.length;
7247 * Returns the number of columns.
7250 getColumnCount : function(visibleOnly){
7251 if(visibleOnly === true){
7253 for(var i = 0, len = this.config.length; i < len; i++){
7254 if(!this.isHidden(i)){
7260 return this.config.length;
7264 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7265 * @param {Function} fn
7266 * @param {Object} scope (optional)
7267 * @return {Array} result
7269 getColumnsBy : function(fn, scope){
7271 for(var i = 0, len = this.config.length; i < len; i++){
7272 var c = this.config[i];
7273 if(fn.call(scope||this, c, i) === true){
7281 * Returns true if the specified column is sortable.
7282 * @param {Number} col The column index
7285 isSortable : function(col){
7286 if(typeof this.config[col].sortable == "undefined"){
7287 return this.defaultSortable;
7289 return this.config[col].sortable;
7293 * Returns the rendering (formatting) function defined for the column.
7294 * @param {Number} col The column index.
7295 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7297 getRenderer : function(col){
7298 if(!this.config[col].renderer){
7299 return Roo.grid.ColumnModel.defaultRenderer;
7301 return this.config[col].renderer;
7305 * Sets the rendering (formatting) function for a column.
7306 * @param {Number} col The column index
7307 * @param {Function} fn The function to use to process the cell's raw data
7308 * to return HTML markup for the grid view. The render function is called with
7309 * the following parameters:<ul>
7310 * <li>Data value.</li>
7311 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7312 * <li>css A CSS style string to apply to the table cell.</li>
7313 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7314 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7315 * <li>Row index</li>
7316 * <li>Column index</li>
7317 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7319 setRenderer : function(col, fn){
7320 this.config[col].renderer = fn;
7324 * Returns the width for the specified column.
7325 * @param {Number} col The column index
7328 getColumnWidth : function(col){
7329 return this.config[col].width * 1 || this.defaultWidth;
7333 * Sets the width for a column.
7334 * @param {Number} col The column index
7335 * @param {Number} width The new width
7337 setColumnWidth : function(col, width, suppressEvent){
7338 this.config[col].width = width;
7339 this.totalWidth = null;
7341 this.fireEvent("widthchange", this, col, width);
7346 * Returns the total width of all columns.
7347 * @param {Boolean} includeHidden True to include hidden column widths
7350 getTotalWidth : function(includeHidden){
7351 if(!this.totalWidth){
7352 this.totalWidth = 0;
7353 for(var i = 0, len = this.config.length; i < len; i++){
7354 if(includeHidden || !this.isHidden(i)){
7355 this.totalWidth += this.getColumnWidth(i);
7359 return this.totalWidth;
7363 * Returns the header for the specified column.
7364 * @param {Number} col The column index
7367 getColumnHeader : function(col){
7368 return this.config[col].header;
7372 * Sets the header for a column.
7373 * @param {Number} col The column index
7374 * @param {String} header The new header
7376 setColumnHeader : function(col, header){
7377 this.config[col].header = header;
7378 this.fireEvent("headerchange", this, col, header);
7382 * Returns the tooltip for the specified column.
7383 * @param {Number} col The column index
7386 getColumnTooltip : function(col){
7387 return this.config[col].tooltip;
7390 * Sets the tooltip for a column.
7391 * @param {Number} col The column index
7392 * @param {String} tooltip The new tooltip
7394 setColumnTooltip : function(col, tooltip){
7395 this.config[col].tooltip = tooltip;
7399 * Returns the dataIndex for the specified column.
7400 * @param {Number} col The column index
7403 getDataIndex : function(col){
7404 return this.config[col].dataIndex;
7408 * Sets the dataIndex for a column.
7409 * @param {Number} col The column index
7410 * @param {Number} dataIndex The new dataIndex
7412 setDataIndex : function(col, dataIndex){
7413 this.config[col].dataIndex = dataIndex;
7419 * Returns true if the cell is editable.
7420 * @param {Number} colIndex The column index
7421 * @param {Number} rowIndex The row index - this is nto actually used..?
7424 isCellEditable : function(colIndex, rowIndex){
7425 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7429 * Returns the editor defined for the cell/column.
7430 * return false or null to disable editing.
7431 * @param {Number} colIndex The column index
7432 * @param {Number} rowIndex The row index
7435 getCellEditor : function(colIndex, rowIndex){
7436 return this.config[colIndex].editor;
7440 * Sets if a column is editable.
7441 * @param {Number} col The column index
7442 * @param {Boolean} editable True if the column is editable
7444 setEditable : function(col, editable){
7445 this.config[col].editable = editable;
7450 * Returns true if the column is hidden.
7451 * @param {Number} colIndex The column index
7454 isHidden : function(colIndex){
7455 return this.config[colIndex].hidden;
7460 * Returns true if the column width cannot be changed
7462 isFixed : function(colIndex){
7463 return this.config[colIndex].fixed;
7467 * Returns true if the column can be resized
7470 isResizable : function(colIndex){
7471 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7474 * Sets if a column is hidden.
7475 * @param {Number} colIndex The column index
7476 * @param {Boolean} hidden True if the column is hidden
7478 setHidden : function(colIndex, hidden){
7479 this.config[colIndex].hidden = hidden;
7480 this.totalWidth = null;
7481 this.fireEvent("hiddenchange", this, colIndex, hidden);
7485 * Sets the editor for a column.
7486 * @param {Number} col The column index
7487 * @param {Object} editor The editor object
7489 setEditor : function(col, editor){
7490 this.config[col].editor = editor;
7494 Roo.grid.ColumnModel.defaultRenderer = function(value)
7496 if(typeof value == "object") {
7499 if(typeof value == "string" && value.length < 1){
7503 return String.format("{0}", value);
7506 // Alias for backwards compatibility
7507 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7510 * Ext JS Library 1.1.1
7511 * Copyright(c) 2006-2007, Ext JS, LLC.
7513 * Originally Released Under LGPL - original licence link has changed is not relivant.
7516 * <script type="text/javascript">
7520 * @class Roo.LoadMask
7521 * A simple utility class for generically masking elements while loading data. If the element being masked has
7522 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7523 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7524 * element's UpdateManager load indicator and will be destroyed after the initial load.
7526 * Create a new LoadMask
7527 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7528 * @param {Object} config The config object
7530 Roo.LoadMask = function(el, config){
7531 this.el = Roo.get(el);
7532 Roo.apply(this, config);
7534 this.store.on('beforeload', this.onBeforeLoad, this);
7535 this.store.on('load', this.onLoad, this);
7536 this.store.on('loadexception', this.onLoadException, this);
7537 this.removeMask = false;
7539 var um = this.el.getUpdateManager();
7540 um.showLoadIndicator = false; // disable the default indicator
7541 um.on('beforeupdate', this.onBeforeLoad, this);
7542 um.on('update', this.onLoad, this);
7543 um.on('failure', this.onLoad, this);
7544 this.removeMask = true;
7548 Roo.LoadMask.prototype = {
7550 * @cfg {Boolean} removeMask
7551 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7552 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7556 * The text to display in a centered loading message box (defaults to 'Loading...')
7560 * @cfg {String} msgCls
7561 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7563 msgCls : 'x-mask-loading',
7566 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7572 * Disables the mask to prevent it from being displayed
7574 disable : function(){
7575 this.disabled = true;
7579 * Enables the mask so that it can be displayed
7581 enable : function(){
7582 this.disabled = false;
7585 onLoadException : function()
7589 if (typeof(arguments[3]) != 'undefined') {
7590 Roo.MessageBox.alert("Error loading",arguments[3]);
7594 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7595 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7602 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7607 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611 onBeforeLoad : function(){
7613 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7618 destroy : function(){
7620 this.store.un('beforeload', this.onBeforeLoad, this);
7621 this.store.un('load', this.onLoad, this);
7622 this.store.un('loadexception', this.onLoadException, this);
7624 var um = this.el.getUpdateManager();
7625 um.un('beforeupdate', this.onBeforeLoad, this);
7626 um.un('update', this.onLoad, this);
7627 um.un('failure', this.onLoad, this);
7638 * @class Roo.bootstrap.Table
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Table class
7641 * @cfg {String} cls table class
7642 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7643 * @cfg {String} bgcolor Specifies the background color for a table
7644 * @cfg {Number} border Specifies whether the table cells should have borders or not
7645 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7646 * @cfg {Number} cellspacing Specifies the space between cells
7647 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7648 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7649 * @cfg {String} sortable Specifies that the table should be sortable
7650 * @cfg {String} summary Specifies a summary of the content of a table
7651 * @cfg {Number} width Specifies the width of a table
7652 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7654 * @cfg {boolean} striped Should the rows be alternative striped
7655 * @cfg {boolean} bordered Add borders to the table
7656 * @cfg {boolean} hover Add hover highlighting
7657 * @cfg {boolean} condensed Format condensed
7658 * @cfg {boolean} responsive Format condensed
7659 * @cfg {Boolean} loadMask (true|false) default false
7660 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7661 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7662 * @cfg {Boolean} rowSelection (true|false) default false
7663 * @cfg {Boolean} cellSelection (true|false) default false
7664 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7665 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7666 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7667 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7671 * Create a new Table
7672 * @param {Object} config The config object
7675 Roo.bootstrap.Table = function(config){
7676 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7681 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7682 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7683 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7684 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7686 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7688 this.sm.grid = this;
7689 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7690 this.sm = this.selModel;
7691 this.sm.xmodule = this.xmodule || false;
7694 if (this.cm && typeof(this.cm.config) == 'undefined') {
7695 this.colModel = new Roo.grid.ColumnModel(this.cm);
7696 this.cm = this.colModel;
7697 this.cm.xmodule = this.xmodule || false;
7700 this.store= Roo.factory(this.store, Roo.data);
7701 this.ds = this.store;
7702 this.ds.xmodule = this.xmodule || false;
7705 if (this.footer && this.store) {
7706 this.footer.dataSource = this.ds;
7707 this.footer = Roo.factory(this.footer);
7714 * Fires when a cell is clicked
7715 * @param {Roo.bootstrap.Table} this
7716 * @param {Roo.Element} el
7717 * @param {Number} rowIndex
7718 * @param {Number} columnIndex
7719 * @param {Roo.EventObject} e
7723 * @event celldblclick
7724 * Fires when a cell is double 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
7731 "celldblclick" : true,
7734 * Fires when a row is clicked
7735 * @param {Roo.bootstrap.Table} this
7736 * @param {Roo.Element} el
7737 * @param {Number} rowIndex
7738 * @param {Roo.EventObject} e
7742 * @event rowdblclick
7743 * Fires when a row is double clicked
7744 * @param {Roo.bootstrap.Table} this
7745 * @param {Roo.Element} el
7746 * @param {Number} rowIndex
7747 * @param {Roo.EventObject} e
7749 "rowdblclick" : true,
7752 * Fires when a mouseover occur
7753 * @param {Roo.bootstrap.Table} this
7754 * @param {Roo.Element} el
7755 * @param {Number} rowIndex
7756 * @param {Number} columnIndex
7757 * @param {Roo.EventObject} e
7762 * Fires when a mouseout 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 row is rendered, so you can change add a style to it.
7773 * @param {Roo.bootstrap.Table} this
7774 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7778 * @event rowsrendered
7779 * Fires when all the rows have been rendered
7780 * @param {Roo.bootstrap.Table} this
7782 'rowsrendered' : true,
7784 * @event contextmenu
7785 * The raw contextmenu event for the entire grid.
7786 * @param {Roo.EventObject} e
7788 "contextmenu" : true,
7790 * @event rowcontextmenu
7791 * Fires when a row is right clicked
7792 * @param {Roo.bootstrap.Table} this
7793 * @param {Number} rowIndex
7794 * @param {Roo.EventObject} e
7796 "rowcontextmenu" : true,
7798 * @event cellcontextmenu
7799 * Fires when a cell is right clicked
7800 * @param {Roo.bootstrap.Table} this
7801 * @param {Number} rowIndex
7802 * @param {Number} cellIndex
7803 * @param {Roo.EventObject} e
7805 "cellcontextmenu" : true,
7807 * @event headercontextmenu
7808 * Fires when a header is right clicked
7809 * @param {Roo.bootstrap.Table} this
7810 * @param {Number} columnIndex
7811 * @param {Roo.EventObject} e
7813 "headercontextmenu" : true
7817 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7843 rowSelection : false,
7844 cellSelection : false,
7847 // Roo.Element - the tbody
7849 // Roo.Element - thead element
7852 container: false, // used by gridpanel...
7858 auto_hide_footer : false,
7860 getAutoCreate : function()
7862 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7869 if (this.scrollBody) {
7870 cfg.cls += ' table-body-fixed';
7873 cfg.cls += ' table-striped';
7877 cfg.cls += ' table-hover';
7879 if (this.bordered) {
7880 cfg.cls += ' table-bordered';
7882 if (this.condensed) {
7883 cfg.cls += ' table-condensed';
7885 if (this.responsive) {
7886 cfg.cls += ' table-responsive';
7890 cfg.cls+= ' ' +this.cls;
7893 // this lot should be simplifed...
7906 ].forEach(function(k) {
7914 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7917 if(this.store || this.cm){
7918 if(this.headerShow){
7919 cfg.cn.push(this.renderHeader());
7922 cfg.cn.push(this.renderBody());
7924 if(this.footerShow){
7925 cfg.cn.push(this.renderFooter());
7927 // where does this come from?
7928 //cfg.cls+= ' TableGrid';
7931 return { cn : [ cfg ] };
7934 initEvents : function()
7936 if(!this.store || !this.cm){
7939 if (this.selModel) {
7940 this.selModel.initEvents();
7944 //Roo.log('initEvents with ds!!!!');
7946 this.mainBody = this.el.select('tbody', true).first();
7947 this.mainHead = this.el.select('thead', true).first();
7948 this.mainFoot = this.el.select('tfoot', true).first();
7954 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7955 e.on('click', _this.sort, _this);
7958 this.mainBody.on("click", this.onClick, this);
7959 this.mainBody.on("dblclick", this.onDblClick, this);
7961 // why is this done????? = it breaks dialogs??
7962 //this.parent().el.setStyle('position', 'relative');
7966 this.footer.parentId = this.id;
7967 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7970 this.el.select('tfoot tr td').first().addClass('hide');
7975 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7978 this.store.on('load', this.onLoad, this);
7979 this.store.on('beforeload', this.onBeforeLoad, this);
7980 this.store.on('update', this.onUpdate, this);
7981 this.store.on('add', this.onAdd, this);
7982 this.store.on("clear", this.clear, this);
7984 this.el.on("contextmenu", this.onContextMenu, this);
7986 this.mainBody.on('scroll', this.onBodyScroll, this);
7988 this.cm.on("headerchange", this.onHeaderChange, this);
7990 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7994 onContextMenu : function(e, t)
7996 this.processEvent("contextmenu", e);
7999 processEvent : function(name, e)
8001 if (name != 'touchstart' ) {
8002 this.fireEvent(name, e);
8005 var t = e.getTarget();
8007 var cell = Roo.get(t);
8013 if(cell.findParent('tfoot', false, true)){
8017 if(cell.findParent('thead', false, true)){
8019 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8020 cell = Roo.get(t).findParent('th', false, true);
8022 Roo.log("failed to find th in thead?");
8023 Roo.log(e.getTarget());
8028 var cellIndex = cell.dom.cellIndex;
8030 var ename = name == 'touchstart' ? 'click' : name;
8031 this.fireEvent("header" + ename, this, cellIndex, e);
8036 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8037 cell = Roo.get(t).findParent('td', false, true);
8039 Roo.log("failed to find th in tbody?");
8040 Roo.log(e.getTarget());
8045 var row = cell.findParent('tr', false, true);
8046 var cellIndex = cell.dom.cellIndex;
8047 var rowIndex = row.dom.rowIndex - 1;
8051 this.fireEvent("row" + name, this, rowIndex, e);
8055 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8061 onMouseover : function(e, el)
8063 var cell = Roo.get(el);
8069 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8070 cell = cell.findParent('td', false, true);
8073 var row = cell.findParent('tr', false, true);
8074 var cellIndex = cell.dom.cellIndex;
8075 var rowIndex = row.dom.rowIndex - 1; // start from 0
8077 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8081 onMouseout : function(e, el)
8083 var cell = Roo.get(el);
8089 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8090 cell = cell.findParent('td', false, true);
8093 var row = cell.findParent('tr', false, true);
8094 var cellIndex = cell.dom.cellIndex;
8095 var rowIndex = row.dom.rowIndex - 1; // start from 0
8097 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8101 onClick : function(e, el)
8103 var cell = Roo.get(el);
8105 if(!cell || (!this.cellSelection && !this.rowSelection)){
8109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8110 cell = cell.findParent('td', false, true);
8113 if(!cell || typeof(cell) == 'undefined'){
8117 var row = cell.findParent('tr', false, true);
8119 if(!row || typeof(row) == 'undefined'){
8123 var cellIndex = cell.dom.cellIndex;
8124 var rowIndex = this.getRowIndex(row);
8126 // why??? - should these not be based on SelectionModel?
8127 if(this.cellSelection){
8128 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8131 if(this.rowSelection){
8132 this.fireEvent('rowclick', this, row, rowIndex, e);
8138 onDblClick : function(e,el)
8140 var cell = Roo.get(el);
8142 if(!cell || (!this.cellSelection && !this.rowSelection)){
8146 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8147 cell = cell.findParent('td', false, true);
8150 if(!cell || typeof(cell) == 'undefined'){
8154 var row = cell.findParent('tr', false, true);
8156 if(!row || typeof(row) == 'undefined'){
8160 var cellIndex = cell.dom.cellIndex;
8161 var rowIndex = this.getRowIndex(row);
8163 if(this.cellSelection){
8164 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8167 if(this.rowSelection){
8168 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8172 sort : function(e,el)
8174 var col = Roo.get(el);
8176 if(!col.hasClass('sortable')){
8180 var sort = col.attr('sort');
8183 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8187 this.store.sortInfo = {field : sort, direction : dir};
8190 Roo.log("calling footer first");
8191 this.footer.onClick('first');
8194 this.store.load({ params : { start : 0 } });
8198 renderHeader : function()
8206 this.totalWidth = 0;
8208 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8210 var config = cm.config[i];
8214 cls : 'x-hcol-' + i,
8216 html: cm.getColumnHeader(i)
8221 if(typeof(config.sortable) != 'undefined' && config.sortable){
8223 c.html = '<i class="glyphicon"></i>' + c.html;
8226 // could use BS4 hidden-..-down
8228 if(typeof(config.lgHeader) != 'undefined'){
8229 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8232 if(typeof(config.mdHeader) != 'undefined'){
8233 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8236 if(typeof(config.smHeader) != 'undefined'){
8237 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8240 if(typeof(config.xsHeader) != 'undefined'){
8241 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8248 if(typeof(config.tooltip) != 'undefined'){
8249 c.tooltip = config.tooltip;
8252 if(typeof(config.colspan) != 'undefined'){
8253 c.colspan = config.colspan;
8256 if(typeof(config.hidden) != 'undefined' && config.hidden){
8257 c.style += ' display:none;';
8260 if(typeof(config.dataIndex) != 'undefined'){
8261 c.sort = config.dataIndex;
8266 if(typeof(config.align) != 'undefined' && config.align.length){
8267 c.style += ' text-align:' + config.align + ';';
8270 if(typeof(config.width) != 'undefined'){
8271 c.style += ' width:' + config.width + 'px;';
8272 this.totalWidth += config.width;
8274 this.totalWidth += 100; // assume minimum of 100 per column?
8277 if(typeof(config.cls) != 'undefined'){
8278 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8281 ['xs','sm','md','lg'].map(function(size){
8283 if(typeof(config[size]) == 'undefined'){
8287 if (!config[size]) { // 0 = hidden
8288 // BS 4 '0' is treated as hide that column and below.
8289 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8293 c.cls += ' col-' + size + '-' + config[size] + (
8294 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8306 renderBody : function()
8316 colspan : this.cm.getColumnCount()
8326 renderFooter : function()
8336 colspan : this.cm.getColumnCount()
8350 // Roo.log('ds onload');
8355 var ds = this.store;
8357 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8358 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8359 if (_this.store.sortInfo) {
8361 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8362 e.select('i', true).addClass(['glyphicon-arrow-up']);
8365 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8366 e.select('i', true).addClass(['glyphicon-arrow-down']);
8371 var tbody = this.mainBody;
8373 if(ds.getCount() > 0){
8374 ds.data.each(function(d,rowIndex){
8375 var row = this.renderRow(cm, ds, rowIndex);
8377 tbody.createChild(row);
8381 if(row.cellObjects.length){
8382 Roo.each(row.cellObjects, function(r){
8383 _this.renderCellObject(r);
8390 var tfoot = this.el.select('tfoot', true).first();
8392 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8394 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8396 var total = this.ds.getTotalCount();
8398 if(this.footer.pageSize < total){
8399 this.mainFoot.show();
8403 Roo.each(this.el.select('tbody td', true).elements, function(e){
8404 e.on('mouseover', _this.onMouseover, _this);
8407 Roo.each(this.el.select('tbody td', true).elements, function(e){
8408 e.on('mouseout', _this.onMouseout, _this);
8410 this.fireEvent('rowsrendered', this);
8416 onUpdate : function(ds,record)
8418 this.refreshRow(record);
8422 onRemove : function(ds, record, index, isUpdate){
8423 if(isUpdate !== true){
8424 this.fireEvent("beforerowremoved", this, index, record);
8426 var bt = this.mainBody.dom;
8428 var rows = this.el.select('tbody > tr', true).elements;
8430 if(typeof(rows[index]) != 'undefined'){
8431 bt.removeChild(rows[index].dom);
8434 // if(bt.rows[index]){
8435 // bt.removeChild(bt.rows[index]);
8438 if(isUpdate !== true){
8439 //this.stripeRows(index);
8440 //this.syncRowHeights(index, index);
8442 this.fireEvent("rowremoved", this, index, record);
8446 onAdd : function(ds, records, rowIndex)
8448 //Roo.log('on Add called');
8449 // - note this does not handle multiple adding very well..
8450 var bt = this.mainBody.dom;
8451 for (var i =0 ; i < records.length;i++) {
8452 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8453 //Roo.log(records[i]);
8454 //Roo.log(this.store.getAt(rowIndex+i));
8455 this.insertRow(this.store, rowIndex + i, false);
8462 refreshRow : function(record){
8463 var ds = this.store, index;
8464 if(typeof record == 'number'){
8466 record = ds.getAt(index);
8468 index = ds.indexOf(record);
8470 return; // should not happen - but seems to
8473 this.insertRow(ds, index, true);
8475 this.onRemove(ds, record, index+1, true);
8477 //this.syncRowHeights(index, index);
8479 this.fireEvent("rowupdated", this, index, record);
8482 insertRow : function(dm, rowIndex, isUpdate){
8485 this.fireEvent("beforerowsinserted", this, rowIndex);
8487 //var s = this.getScrollState();
8488 var row = this.renderRow(this.cm, this.store, rowIndex);
8489 // insert before rowIndex..
8490 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8494 if(row.cellObjects.length){
8495 Roo.each(row.cellObjects, function(r){
8496 _this.renderCellObject(r);
8501 this.fireEvent("rowsinserted", this, rowIndex);
8502 //this.syncRowHeights(firstRow, lastRow);
8503 //this.stripeRows(firstRow);
8510 getRowDom : function(rowIndex)
8512 var rows = this.el.select('tbody > tr', true).elements;
8514 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8517 // returns the object tree for a tr..
8520 renderRow : function(cm, ds, rowIndex)
8522 var d = ds.getAt(rowIndex);
8526 cls : 'x-row-' + rowIndex,
8530 var cellObjects = [];
8532 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8533 var config = cm.config[i];
8535 var renderer = cm.getRenderer(i);
8539 if(typeof(renderer) !== 'undefined'){
8540 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8542 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8543 // and are rendered into the cells after the row is rendered - using the id for the element.
8545 if(typeof(value) === 'object'){
8555 rowIndex : rowIndex,
8560 this.fireEvent('rowclass', this, rowcfg);
8564 cls : rowcfg.rowClass + ' x-col-' + i,
8566 html: (typeof(value) === 'object') ? '' : value
8573 if(typeof(config.colspan) != 'undefined'){
8574 td.colspan = config.colspan;
8577 if(typeof(config.hidden) != 'undefined' && config.hidden){
8578 td.style += ' display:none;';
8581 if(typeof(config.align) != 'undefined' && config.align.length){
8582 td.style += ' text-align:' + config.align + ';';
8584 if(typeof(config.valign) != 'undefined' && config.valign.length){
8585 td.style += ' vertical-align:' + config.valign + ';';
8588 if(typeof(config.width) != 'undefined'){
8589 td.style += ' width:' + config.width + 'px;';
8592 if(typeof(config.cursor) != 'undefined'){
8593 td.style += ' cursor:' + config.cursor + ';';
8596 if(typeof(config.cls) != 'undefined'){
8597 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8600 ['xs','sm','md','lg'].map(function(size){
8602 if(typeof(config[size]) == 'undefined'){
8608 if (!config[size]) { // 0 = hidden
8609 // BS 4 '0' is treated as hide that column and below.
8610 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8614 td.cls += ' col-' + size + '-' + config[size] + (
8615 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8625 row.cellObjects = cellObjects;
8633 onBeforeLoad : function()
8642 this.el.select('tbody', true).first().dom.innerHTML = '';
8645 * Show or hide a row.
8646 * @param {Number} rowIndex to show or hide
8647 * @param {Boolean} state hide
8649 setRowVisibility : function(rowIndex, state)
8651 var bt = this.mainBody.dom;
8653 var rows = this.el.select('tbody > tr', true).elements;
8655 if(typeof(rows[rowIndex]) == 'undefined'){
8658 rows[rowIndex].dom.style.display = state ? '' : 'none';
8662 getSelectionModel : function(){
8664 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8666 return this.selModel;
8669 * Render the Roo.bootstrap object from renderder
8671 renderCellObject : function(r)
8675 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8677 var t = r.cfg.render(r.container);
8680 Roo.each(r.cfg.cn, function(c){
8682 container: t.getChildContainer(),
8685 _this.renderCellObject(child);
8690 getRowIndex : function(row)
8694 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8705 * Returns the grid's underlying element = used by panel.Grid
8706 * @return {Element} The element
8708 getGridEl : function(){
8712 * Forces a resize - used by panel.Grid
8713 * @return {Element} The element
8715 autoSize : function()
8717 //var ctr = Roo.get(this.container.dom.parentElement);
8718 var ctr = Roo.get(this.el.dom);
8720 var thd = this.getGridEl().select('thead',true).first();
8721 var tbd = this.getGridEl().select('tbody', true).first();
8722 var tfd = this.getGridEl().select('tfoot', true).first();
8724 var cw = ctr.getWidth();
8725 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8729 tbd.setWidth(ctr.getWidth());
8730 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8731 // this needs fixing for various usage - currently only hydra job advers I think..
8733 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8735 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8738 cw = Math.max(cw, this.totalWidth);
8739 this.getGridEl().select('tbody tr',true).setWidth(cw);
8741 // resize 'expandable coloumn?
8743 return; // we doe not have a view in this design..
8746 onBodyScroll: function()
8748 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8750 this.mainHead.setStyle({
8751 'position' : 'relative',
8752 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8758 var scrollHeight = this.mainBody.dom.scrollHeight;
8760 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8762 var height = this.mainBody.getHeight();
8764 if(scrollHeight - height == scrollTop) {
8766 var total = this.ds.getTotalCount();
8768 if(this.footer.cursor + this.footer.pageSize < total){
8770 this.footer.ds.load({
8772 start : this.footer.cursor + this.footer.pageSize,
8773 limit : this.footer.pageSize
8783 onHeaderChange : function()
8785 var header = this.renderHeader();
8786 var table = this.el.select('table', true).first();
8788 this.mainHead.remove();
8789 this.mainHead = table.createChild(header, this.mainBody, false);
8792 onHiddenChange : function(colModel, colIndex, hidden)
8794 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8795 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8797 this.CSS.updateRule(thSelector, "display", "");
8798 this.CSS.updateRule(tdSelector, "display", "");
8801 this.CSS.updateRule(thSelector, "display", "none");
8802 this.CSS.updateRule(tdSelector, "display", "none");
8805 this.onHeaderChange();
8809 setColumnWidth: function(col_index, width)
8811 // width = "md-2 xs-2..."
8812 if(!this.colModel.config[col_index]) {
8816 var w = width.split(" ");
8818 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8820 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8823 for(var j = 0; j < w.length; j++) {
8829 var size_cls = w[j].split("-");
8831 if(!Number.isInteger(size_cls[1] * 1)) {
8835 if(!this.colModel.config[col_index][size_cls[0]]) {
8839 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8843 h_row[0].classList.replace(
8844 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8845 "col-"+size_cls[0]+"-"+size_cls[1]
8848 for(var i = 0; i < rows.length; i++) {
8850 var size_cls = w[j].split("-");
8852 if(!Number.isInteger(size_cls[1] * 1)) {
8856 if(!this.colModel.config[col_index][size_cls[0]]) {
8860 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8864 rows[i].classList.replace(
8865 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8866 "col-"+size_cls[0]+"-"+size_cls[1]
8870 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8885 * @class Roo.bootstrap.TableCell
8886 * @extends Roo.bootstrap.Component
8887 * Bootstrap TableCell class
8888 * @cfg {String} html cell contain text
8889 * @cfg {String} cls cell class
8890 * @cfg {String} tag cell tag (td|th) default td
8891 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8892 * @cfg {String} align Aligns the content in a cell
8893 * @cfg {String} axis Categorizes cells
8894 * @cfg {String} bgcolor Specifies the background color of a cell
8895 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8896 * @cfg {Number} colspan Specifies the number of columns a cell should span
8897 * @cfg {String} headers Specifies one or more header cells a cell is related to
8898 * @cfg {Number} height Sets the height of a cell
8899 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8900 * @cfg {Number} rowspan Sets the number of rows a cell should span
8901 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8902 * @cfg {String} valign Vertical aligns the content in a cell
8903 * @cfg {Number} width Specifies the width of a cell
8906 * Create a new TableCell
8907 * @param {Object} config The config object
8910 Roo.bootstrap.TableCell = function(config){
8911 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8914 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8934 getAutoCreate : function(){
8935 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8955 cfg.align=this.align
8961 cfg.bgcolor=this.bgcolor
8964 cfg.charoff=this.charoff
8967 cfg.colspan=this.colspan
8970 cfg.headers=this.headers
8973 cfg.height=this.height
8976 cfg.nowrap=this.nowrap
8979 cfg.rowspan=this.rowspan
8982 cfg.scope=this.scope
8985 cfg.valign=this.valign
8988 cfg.width=this.width
9007 * @class Roo.bootstrap.TableRow
9008 * @extends Roo.bootstrap.Component
9009 * Bootstrap TableRow class
9010 * @cfg {String} cls row class
9011 * @cfg {String} align Aligns the content in a table row
9012 * @cfg {String} bgcolor Specifies a background color for a table row
9013 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9014 * @cfg {String} valign Vertical aligns the content in a table row
9017 * Create a new TableRow
9018 * @param {Object} config The config object
9021 Roo.bootstrap.TableRow = function(config){
9022 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9025 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9033 getAutoCreate : function(){
9034 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9044 cfg.align = this.align;
9047 cfg.bgcolor = this.bgcolor;
9050 cfg.charoff = this.charoff;
9053 cfg.valign = this.valign;
9071 * @class Roo.bootstrap.TableBody
9072 * @extends Roo.bootstrap.Component
9073 * Bootstrap TableBody class
9074 * @cfg {String} cls element class
9075 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9076 * @cfg {String} align Aligns the content inside the element
9077 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9078 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9081 * Create a new TableBody
9082 * @param {Object} config The config object
9085 Roo.bootstrap.TableBody = function(config){
9086 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9089 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9097 getAutoCreate : function(){
9098 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112 cfg.align = this.align;
9115 cfg.charoff = this.charoff;
9118 cfg.valign = this.valign;
9125 // initEvents : function()
9132 // this.store = Roo.factory(this.store, Roo.data);
9133 // this.store.on('load', this.onLoad, this);
9135 // this.store.load();
9139 // onLoad: function ()
9141 // this.fireEvent('load', this);
9151 * Ext JS Library 1.1.1
9152 * Copyright(c) 2006-2007, Ext JS, LLC.
9154 * Originally Released Under LGPL - original licence link has changed is not relivant.
9157 * <script type="text/javascript">
9160 // as we use this in bootstrap.
9161 Roo.namespace('Roo.form');
9163 * @class Roo.form.Action
9164 * Internal Class used to handle form actions
9166 * @param {Roo.form.BasicForm} el The form element or its id
9167 * @param {Object} config Configuration options
9172 // define the action interface
9173 Roo.form.Action = function(form, options){
9175 this.options = options || {};
9178 * Client Validation Failed
9181 Roo.form.Action.CLIENT_INVALID = 'client';
9183 * Server Validation Failed
9186 Roo.form.Action.SERVER_INVALID = 'server';
9188 * Connect to Server Failed
9191 Roo.form.Action.CONNECT_FAILURE = 'connect';
9193 * Reading Data from Server Failed
9196 Roo.form.Action.LOAD_FAILURE = 'load';
9198 Roo.form.Action.prototype = {
9200 failureType : undefined,
9201 response : undefined,
9205 run : function(options){
9210 success : function(response){
9215 handleResponse : function(response){
9219 // default connection failure
9220 failure : function(response){
9222 this.response = response;
9223 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9224 this.form.afterAction(this, false);
9227 processResponse : function(response){
9228 this.response = response;
9229 if(!response.responseText){
9232 this.result = this.handleResponse(response);
9236 // utility functions used internally
9237 getUrl : function(appendParams){
9238 var url = this.options.url || this.form.url || this.form.el.dom.action;
9240 var p = this.getParams();
9242 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9248 getMethod : function(){
9249 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9252 getParams : function(){
9253 var bp = this.form.baseParams;
9254 var p = this.options.params;
9256 if(typeof p == "object"){
9257 p = Roo.urlEncode(Roo.applyIf(p, bp));
9258 }else if(typeof p == 'string' && bp){
9259 p += '&' + Roo.urlEncode(bp);
9262 p = Roo.urlEncode(bp);
9267 createCallback : function(){
9269 success: this.success,
9270 failure: this.failure,
9272 timeout: (this.form.timeout*1000),
9273 upload: this.form.fileUpload ? this.success : undefined
9278 Roo.form.Action.Submit = function(form, options){
9279 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9282 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9285 haveProgress : false,
9286 uploadComplete : false,
9288 // uploadProgress indicator.
9289 uploadProgress : function()
9291 if (!this.form.progressUrl) {
9295 if (!this.haveProgress) {
9296 Roo.MessageBox.progress("Uploading", "Uploading");
9298 if (this.uploadComplete) {
9299 Roo.MessageBox.hide();
9303 this.haveProgress = true;
9305 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9307 var c = new Roo.data.Connection();
9309 url : this.form.progressUrl,
9314 success : function(req){
9315 //console.log(data);
9319 rdata = Roo.decode(req.responseText)
9321 Roo.log("Invalid data from server..");
9325 if (!rdata || !rdata.success) {
9327 Roo.MessageBox.alert(Roo.encode(rdata));
9330 var data = rdata.data;
9332 if (this.uploadComplete) {
9333 Roo.MessageBox.hide();
9338 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9339 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9342 this.uploadProgress.defer(2000,this);
9345 failure: function(data) {
9346 Roo.log('progress url failed ');
9357 // run get Values on the form, so it syncs any secondary forms.
9358 this.form.getValues();
9360 var o = this.options;
9361 var method = this.getMethod();
9362 var isPost = method == 'POST';
9363 if(o.clientValidation === false || this.form.isValid()){
9365 if (this.form.progressUrl) {
9366 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9367 (new Date() * 1) + '' + Math.random());
9372 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9373 form:this.form.el.dom,
9374 url:this.getUrl(!isPost),
9376 params:isPost ? this.getParams() : null,
9377 isUpload: this.form.fileUpload,
9378 formData : this.form.formData
9381 this.uploadProgress();
9383 }else if (o.clientValidation !== false){ // client validation failed
9384 this.failureType = Roo.form.Action.CLIENT_INVALID;
9385 this.form.afterAction(this, false);
9389 success : function(response)
9391 this.uploadComplete= true;
9392 if (this.haveProgress) {
9393 Roo.MessageBox.hide();
9397 var result = this.processResponse(response);
9398 if(result === true || result.success){
9399 this.form.afterAction(this, true);
9403 this.form.markInvalid(result.errors);
9404 this.failureType = Roo.form.Action.SERVER_INVALID;
9406 this.form.afterAction(this, false);
9408 failure : function(response)
9410 this.uploadComplete= true;
9411 if (this.haveProgress) {
9412 Roo.MessageBox.hide();
9415 this.response = response;
9416 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9417 this.form.afterAction(this, false);
9420 handleResponse : function(response){
9421 if(this.form.errorReader){
9422 var rs = this.form.errorReader.read(response);
9425 for(var i = 0, len = rs.records.length; i < len; i++) {
9426 var r = rs.records[i];
9430 if(errors.length < 1){
9434 success : rs.success,
9440 ret = Roo.decode(response.responseText);
9444 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9454 Roo.form.Action.Load = function(form, options){
9455 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9456 this.reader = this.form.reader;
9459 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9464 Roo.Ajax.request(Roo.apply(
9465 this.createCallback(), {
9466 method:this.getMethod(),
9467 url:this.getUrl(false),
9468 params:this.getParams()
9472 success : function(response){
9474 var result = this.processResponse(response);
9475 if(result === true || !result.success || !result.data){
9476 this.failureType = Roo.form.Action.LOAD_FAILURE;
9477 this.form.afterAction(this, false);
9480 this.form.clearInvalid();
9481 this.form.setValues(result.data);
9482 this.form.afterAction(this, true);
9485 handleResponse : function(response){
9486 if(this.form.reader){
9487 var rs = this.form.reader.read(response);
9488 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9490 success : rs.success,
9494 return Roo.decode(response.responseText);
9498 Roo.form.Action.ACTION_TYPES = {
9499 'load' : Roo.form.Action.Load,
9500 'submit' : Roo.form.Action.Submit
9509 * @class Roo.bootstrap.Form
9510 * @extends Roo.bootstrap.Component
9511 * Bootstrap Form class
9512 * @cfg {String} method GET | POST (default POST)
9513 * @cfg {String} labelAlign top | left (default top)
9514 * @cfg {String} align left | right - for navbars
9515 * @cfg {Boolean} loadMask load mask when submit (default true)
9520 * @param {Object} config The config object
9524 Roo.bootstrap.Form = function(config){
9526 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9528 Roo.bootstrap.Form.popover.apply();
9532 * @event clientvalidation
9533 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9534 * @param {Form} this
9535 * @param {Boolean} valid true if the form has passed client-side validation
9537 clientvalidation: true,
9539 * @event beforeaction
9540 * Fires before any action is performed. Return false to cancel the action.
9541 * @param {Form} this
9542 * @param {Action} action The action to be performed
9546 * @event actionfailed
9547 * Fires when an action fails.
9548 * @param {Form} this
9549 * @param {Action} action The action that failed
9551 actionfailed : true,
9553 * @event actioncomplete
9554 * Fires when an action is completed.
9555 * @param {Form} this
9556 * @param {Action} action The action that completed
9558 actioncomplete : true
9562 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9565 * @cfg {String} method
9566 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9571 * The URL to use for form actions if one isn't supplied in the action options.
9574 * @cfg {Boolean} fileUpload
9575 * Set to true if this form is a file upload.
9579 * @cfg {Object} baseParams
9580 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9584 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9588 * @cfg {Sting} align (left|right) for navbar forms
9593 activeAction : null,
9596 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9597 * element by passing it or its id or mask the form itself by passing in true.
9600 waitMsgTarget : false,
9605 * @cfg {Boolean} errorMask (true|false) default false
9610 * @cfg {Number} maskOffset Default 100
9615 * @cfg {Boolean} maskBody
9619 getAutoCreate : function(){
9623 method : this.method || 'POST',
9624 id : this.id || Roo.id(),
9627 if (this.parent().xtype.match(/^Nav/)) {
9628 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9632 if (this.labelAlign == 'left' ) {
9633 cfg.cls += ' form-horizontal';
9639 initEvents : function()
9641 this.el.on('submit', this.onSubmit, this);
9642 // this was added as random key presses on the form where triggering form submit.
9643 this.el.on('keypress', function(e) {
9644 if (e.getCharCode() != 13) {
9647 // we might need to allow it for textareas.. and some other items.
9648 // check e.getTarget().
9650 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9654 Roo.log("keypress blocked");
9662 onSubmit : function(e){
9667 * Returns true if client-side validation on the form is successful.
9670 isValid : function(){
9671 var items = this.getItems();
9675 items.each(function(f){
9681 Roo.log('invalid field: ' + f.name);
9685 if(!target && f.el.isVisible(true)){
9691 if(this.errorMask && !valid){
9692 Roo.bootstrap.Form.popover.mask(this, target);
9699 * Returns true if any fields in this form have changed since their original load.
9702 isDirty : function(){
9704 var items = this.getItems();
9705 items.each(function(f){
9715 * Performs a predefined action (submit or load) or custom actions you define on this form.
9716 * @param {String} actionName The name of the action type
9717 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9718 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9719 * accept other config options):
9721 Property Type Description
9722 ---------------- --------------- ----------------------------------------------------------------------------------
9723 url String The url for the action (defaults to the form's url)
9724 method String The form method to use (defaults to the form's method, or POST if not defined)
9725 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9726 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9727 validate the form on the client (defaults to false)
9729 * @return {BasicForm} this
9731 doAction : function(action, options){
9732 if(typeof action == 'string'){
9733 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9735 if(this.fireEvent('beforeaction', this, action) !== false){
9736 this.beforeAction(action);
9737 action.run.defer(100, action);
9743 beforeAction : function(action){
9744 var o = action.options;
9749 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9751 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9754 // not really supported yet.. ??
9756 //if(this.waitMsgTarget === true){
9757 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9758 //}else if(this.waitMsgTarget){
9759 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9760 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9762 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9768 afterAction : function(action, success){
9769 this.activeAction = null;
9770 var o = action.options;
9775 Roo.get(document.body).unmask();
9781 //if(this.waitMsgTarget === true){
9782 // this.el.unmask();
9783 //}else if(this.waitMsgTarget){
9784 // this.waitMsgTarget.unmask();
9786 // Roo.MessageBox.updateProgress(1);
9787 // Roo.MessageBox.hide();
9794 Roo.callback(o.success, o.scope, [this, action]);
9795 this.fireEvent('actioncomplete', this, action);
9799 // failure condition..
9800 // we have a scenario where updates need confirming.
9801 // eg. if a locking scenario exists..
9802 // we look for { errors : { needs_confirm : true }} in the response.
9804 (typeof(action.result) != 'undefined') &&
9805 (typeof(action.result.errors) != 'undefined') &&
9806 (typeof(action.result.errors.needs_confirm) != 'undefined')
9809 Roo.log("not supported yet");
9812 Roo.MessageBox.confirm(
9813 "Change requires confirmation",
9814 action.result.errorMsg,
9819 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9829 Roo.callback(o.failure, o.scope, [this, action]);
9830 // show an error message if no failed handler is set..
9831 if (!this.hasListener('actionfailed')) {
9832 Roo.log("need to add dialog support");
9834 Roo.MessageBox.alert("Error",
9835 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9836 action.result.errorMsg :
9837 "Saving Failed, please check your entries or try again"
9842 this.fireEvent('actionfailed', this, action);
9847 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9848 * @param {String} id The value to search for
9851 findField : function(id){
9852 var items = this.getItems();
9853 var field = items.get(id);
9855 items.each(function(f){
9856 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9863 return field || null;
9866 * Mark fields in this form invalid in bulk.
9867 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9868 * @return {BasicForm} this
9870 markInvalid : function(errors){
9871 if(errors instanceof Array){
9872 for(var i = 0, len = errors.length; i < len; i++){
9873 var fieldError = errors[i];
9874 var f = this.findField(fieldError.id);
9876 f.markInvalid(fieldError.msg);
9882 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9883 field.markInvalid(errors[id]);
9887 //Roo.each(this.childForms || [], function (f) {
9888 // f.markInvalid(errors);
9895 * Set values for fields in this form in bulk.
9896 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9897 * @return {BasicForm} this
9899 setValues : function(values){
9900 if(values instanceof Array){ // array of objects
9901 for(var i = 0, len = values.length; i < len; i++){
9903 var f = this.findField(v.id);
9905 f.setValue(v.value);
9906 if(this.trackResetOnLoad){
9907 f.originalValue = f.getValue();
9911 }else{ // object hash
9914 if(typeof values[id] != 'function' && (field = this.findField(id))){
9916 if (field.setFromData &&
9918 field.displayField &&
9919 // combos' with local stores can
9920 // be queried via setValue()
9921 // to set their value..
9922 (field.store && !field.store.isLocal)
9926 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9927 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9928 field.setFromData(sd);
9930 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9932 field.setFromData(values);
9935 field.setValue(values[id]);
9939 if(this.trackResetOnLoad){
9940 field.originalValue = field.getValue();
9946 //Roo.each(this.childForms || [], function (f) {
9947 // f.setValues(values);
9954 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9955 * they are returned as an array.
9956 * @param {Boolean} asString
9959 getValues : function(asString){
9960 //if (this.childForms) {
9961 // copy values from the child forms
9962 // Roo.each(this.childForms, function (f) {
9963 // this.setValues(f.getValues());
9969 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9970 if(asString === true){
9973 return Roo.urlDecode(fs);
9977 * Returns the fields in this form as an object with key/value pairs.
9978 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9981 getFieldValues : function(with_hidden)
9983 var items = this.getItems();
9985 items.each(function(f){
9991 var v = f.getValue();
9993 if (f.inputType =='radio') {
9994 if (typeof(ret[f.getName()]) == 'undefined') {
9995 ret[f.getName()] = ''; // empty..
9998 if (!f.el.dom.checked) {
10002 v = f.el.dom.value;
10006 if(f.xtype == 'MoneyField'){
10007 ret[f.currencyName] = f.getCurrency();
10010 // not sure if this supported any more..
10011 if ((typeof(v) == 'object') && f.getRawValue) {
10012 v = f.getRawValue() ; // dates..
10014 // combo boxes where name != hiddenName...
10015 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10016 ret[f.name] = f.getRawValue();
10018 ret[f.getName()] = v;
10025 * Clears all invalid messages in this form.
10026 * @return {BasicForm} this
10028 clearInvalid : function(){
10029 var items = this.getItems();
10031 items.each(function(f){
10039 * Resets this form.
10040 * @return {BasicForm} this
10042 reset : function(){
10043 var items = this.getItems();
10044 items.each(function(f){
10048 Roo.each(this.childForms || [], function (f) {
10056 getItems : function()
10058 var r=new Roo.util.MixedCollection(false, function(o){
10059 return o.id || (o.id = Roo.id());
10061 var iter = function(el) {
10068 Roo.each(el.items,function(e) {
10077 hideFields : function(items)
10079 Roo.each(items, function(i){
10081 var f = this.findField(i);
10092 showFields : function(items)
10094 Roo.each(items, function(i){
10096 var f = this.findField(i);
10109 Roo.apply(Roo.bootstrap.Form, {
10125 intervalID : false,
10131 if(this.isApplied){
10136 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10137 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10138 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10139 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10142 this.maskEl.top.enableDisplayMode("block");
10143 this.maskEl.left.enableDisplayMode("block");
10144 this.maskEl.bottom.enableDisplayMode("block");
10145 this.maskEl.right.enableDisplayMode("block");
10147 this.toolTip = new Roo.bootstrap.Tooltip({
10148 cls : 'roo-form-error-popover',
10150 'left' : ['r-l', [-2,0], 'right'],
10151 'right' : ['l-r', [2,0], 'left'],
10152 'bottom' : ['tl-bl', [0,2], 'top'],
10153 'top' : [ 'bl-tl', [0,-2], 'bottom']
10157 this.toolTip.render(Roo.get(document.body));
10159 this.toolTip.el.enableDisplayMode("block");
10161 Roo.get(document.body).on('click', function(){
10165 Roo.get(document.body).on('touchstart', function(){
10169 this.isApplied = true
10172 mask : function(form, target)
10176 this.target = target;
10178 if(!this.form.errorMask || !target.el){
10182 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10184 Roo.log(scrollable);
10186 var ot = this.target.el.calcOffsetsTo(scrollable);
10188 var scrollTo = ot[1] - this.form.maskOffset;
10190 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10192 scrollable.scrollTo('top', scrollTo);
10194 var box = this.target.el.getBox();
10196 var zIndex = Roo.bootstrap.Modal.zIndex++;
10199 this.maskEl.top.setStyle('position', 'absolute');
10200 this.maskEl.top.setStyle('z-index', zIndex);
10201 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10202 this.maskEl.top.setLeft(0);
10203 this.maskEl.top.setTop(0);
10204 this.maskEl.top.show();
10206 this.maskEl.left.setStyle('position', 'absolute');
10207 this.maskEl.left.setStyle('z-index', zIndex);
10208 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10209 this.maskEl.left.setLeft(0);
10210 this.maskEl.left.setTop(box.y - this.padding);
10211 this.maskEl.left.show();
10213 this.maskEl.bottom.setStyle('position', 'absolute');
10214 this.maskEl.bottom.setStyle('z-index', zIndex);
10215 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10216 this.maskEl.bottom.setLeft(0);
10217 this.maskEl.bottom.setTop(box.bottom + this.padding);
10218 this.maskEl.bottom.show();
10220 this.maskEl.right.setStyle('position', 'absolute');
10221 this.maskEl.right.setStyle('z-index', zIndex);
10222 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10223 this.maskEl.right.setLeft(box.right + this.padding);
10224 this.maskEl.right.setTop(box.y - this.padding);
10225 this.maskEl.right.show();
10227 this.toolTip.bindEl = this.target.el;
10229 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10231 var tip = this.target.blankText;
10233 if(this.target.getValue() !== '' ) {
10235 if (this.target.invalidText.length) {
10236 tip = this.target.invalidText;
10237 } else if (this.target.regexText.length){
10238 tip = this.target.regexText;
10242 this.toolTip.show(tip);
10244 this.intervalID = window.setInterval(function() {
10245 Roo.bootstrap.Form.popover.unmask();
10248 window.onwheel = function(){ return false;};
10250 (function(){ this.isMasked = true; }).defer(500, this);
10254 unmask : function()
10256 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10260 this.maskEl.top.setStyle('position', 'absolute');
10261 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10262 this.maskEl.top.hide();
10264 this.maskEl.left.setStyle('position', 'absolute');
10265 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10266 this.maskEl.left.hide();
10268 this.maskEl.bottom.setStyle('position', 'absolute');
10269 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10270 this.maskEl.bottom.hide();
10272 this.maskEl.right.setStyle('position', 'absolute');
10273 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10274 this.maskEl.right.hide();
10276 this.toolTip.hide();
10278 this.toolTip.el.hide();
10280 window.onwheel = function(){ return true;};
10282 if(this.intervalID){
10283 window.clearInterval(this.intervalID);
10284 this.intervalID = false;
10287 this.isMasked = false;
10297 * Ext JS Library 1.1.1
10298 * Copyright(c) 2006-2007, Ext JS, LLC.
10300 * Originally Released Under LGPL - original licence link has changed is not relivant.
10303 * <script type="text/javascript">
10306 * @class Roo.form.VTypes
10307 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10310 Roo.form.VTypes = function(){
10311 // closure these in so they are only created once.
10312 var alpha = /^[a-zA-Z_]+$/;
10313 var alphanum = /^[a-zA-Z0-9_]+$/;
10314 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10315 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10317 // All these messages and functions are configurable
10320 * The function used to validate email addresses
10321 * @param {String} value The email address
10323 'email' : function(v){
10324 return email.test(v);
10327 * The error text to display when the email validation function returns false
10330 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10332 * The keystroke filter mask to be applied on email input
10335 'emailMask' : /[a-z0-9_\.\-@]/i,
10338 * The function used to validate URLs
10339 * @param {String} value The URL
10341 'url' : function(v){
10342 return url.test(v);
10345 * The error text to display when the url validation function returns false
10348 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10351 * The function used to validate alpha values
10352 * @param {String} value The value
10354 'alpha' : function(v){
10355 return alpha.test(v);
10358 * The error text to display when the alpha validation function returns false
10361 'alphaText' : 'This field should only contain letters and _',
10363 * The keystroke filter mask to be applied on alpha input
10366 'alphaMask' : /[a-z_]/i,
10369 * The function used to validate alphanumeric values
10370 * @param {String} value The value
10372 'alphanum' : function(v){
10373 return alphanum.test(v);
10376 * The error text to display when the alphanumeric validation function returns false
10379 'alphanumText' : 'This field should only contain letters, numbers and _',
10381 * The keystroke filter mask to be applied on alphanumeric input
10384 'alphanumMask' : /[a-z0-9_]/i
10394 * @class Roo.bootstrap.Input
10395 * @extends Roo.bootstrap.Component
10396 * Bootstrap Input class
10397 * @cfg {Boolean} disabled is it disabled
10398 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10399 * @cfg {String} name name of the input
10400 * @cfg {string} fieldLabel - the label associated
10401 * @cfg {string} placeholder - placeholder to put in text.
10402 * @cfg {string} before - input group add on before
10403 * @cfg {string} after - input group add on after
10404 * @cfg {string} size - (lg|sm) or leave empty..
10405 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10406 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10407 * @cfg {Number} md colspan out of 12 for computer-sized screens
10408 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10409 * @cfg {string} value default value of the input
10410 * @cfg {Number} labelWidth set the width of label
10411 * @cfg {Number} labellg set the width of label (1-12)
10412 * @cfg {Number} labelmd set the width of label (1-12)
10413 * @cfg {Number} labelsm set the width of label (1-12)
10414 * @cfg {Number} labelxs set the width of label (1-12)
10415 * @cfg {String} labelAlign (top|left)
10416 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10417 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10418 * @cfg {String} indicatorpos (left|right) default left
10419 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10420 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10421 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10423 * @cfg {String} align (left|center|right) Default left
10424 * @cfg {Boolean} forceFeedback (true|false) Default false
10427 * Create a new Input
10428 * @param {Object} config The config object
10431 Roo.bootstrap.Input = function(config){
10433 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10438 * Fires when this field receives input focus.
10439 * @param {Roo.form.Field} this
10444 * Fires when this field loses input focus.
10445 * @param {Roo.form.Field} this
10449 * @event specialkey
10450 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10451 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10452 * @param {Roo.form.Field} this
10453 * @param {Roo.EventObject} e The event object
10458 * Fires just before the field blurs if the field value has changed.
10459 * @param {Roo.form.Field} this
10460 * @param {Mixed} newValue The new value
10461 * @param {Mixed} oldValue The original value
10466 * Fires after the field has been marked as invalid.
10467 * @param {Roo.form.Field} this
10468 * @param {String} msg The validation message
10473 * Fires after the field has been validated with no errors.
10474 * @param {Roo.form.Field} this
10479 * Fires after the key up
10480 * @param {Roo.form.Field} this
10481 * @param {Roo.EventObject} e The event Object
10487 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10489 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10490 automatic validation (defaults to "keyup").
10492 validationEvent : "keyup",
10494 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10496 validateOnBlur : true,
10498 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10500 validationDelay : 250,
10502 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10504 focusClass : "x-form-focus", // not needed???
10508 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10510 invalidClass : "has-warning",
10513 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10515 validClass : "has-success",
10518 * @cfg {Boolean} hasFeedback (true|false) default true
10520 hasFeedback : true,
10523 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10525 invalidFeedbackClass : "glyphicon-warning-sign",
10528 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10530 validFeedbackClass : "glyphicon-ok",
10533 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10535 selectOnFocus : false,
10538 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10542 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10547 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10549 disableKeyFilter : false,
10552 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10556 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10560 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10562 blankText : "Please complete this mandatory field",
10565 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10569 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10571 maxLength : Number.MAX_VALUE,
10573 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10575 minLengthText : "The minimum length for this field is {0}",
10577 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10579 maxLengthText : "The maximum length for this field is {0}",
10583 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10584 * If available, this function will be called only after the basic validators all return true, and will be passed the
10585 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10589 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10590 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10591 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10595 * @cfg {String} regexText -- Depricated - use Invalid Text
10600 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10606 autocomplete: false,
10610 inputType : 'text',
10613 placeholder: false,
10618 preventMark: false,
10619 isFormField : true,
10622 labelAlign : false,
10625 formatedValue : false,
10626 forceFeedback : false,
10628 indicatorpos : 'left',
10638 parentLabelAlign : function()
10641 while (parent.parent()) {
10642 parent = parent.parent();
10643 if (typeof(parent.labelAlign) !='undefined') {
10644 return parent.labelAlign;
10651 getAutoCreate : function()
10653 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10659 if(this.inputType != 'hidden'){
10660 cfg.cls = 'form-group' //input-group
10666 type : this.inputType,
10667 value : this.value,
10668 cls : 'form-control',
10669 placeholder : this.placeholder || '',
10670 autocomplete : this.autocomplete || 'new-password'
10672 if (this.inputType == 'file') {
10673 input.style = 'overflow:hidden'; // why not in CSS?
10676 if(this.capture.length){
10677 input.capture = this.capture;
10680 if(this.accept.length){
10681 input.accept = this.accept + "/*";
10685 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10688 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10689 input.maxLength = this.maxLength;
10692 if (this.disabled) {
10693 input.disabled=true;
10696 if (this.readOnly) {
10697 input.readonly=true;
10701 input.name = this.name;
10705 input.cls += ' input-' + this.size;
10709 ['xs','sm','md','lg'].map(function(size){
10710 if (settings[size]) {
10711 cfg.cls += ' col-' + size + '-' + settings[size];
10715 var inputblock = input;
10719 cls: 'glyphicon form-control-feedback'
10722 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10725 cls : 'has-feedback',
10733 if (this.before || this.after) {
10736 cls : 'input-group',
10740 if (this.before && typeof(this.before) == 'string') {
10742 inputblock.cn.push({
10744 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10748 if (this.before && typeof(this.before) == 'object') {
10749 this.before = Roo.factory(this.before);
10751 inputblock.cn.push({
10753 cls : 'roo-input-before input-group-prepend input-group-' +
10754 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10758 inputblock.cn.push(input);
10760 if (this.after && typeof(this.after) == 'string') {
10761 inputblock.cn.push({
10763 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10767 if (this.after && typeof(this.after) == 'object') {
10768 this.after = Roo.factory(this.after);
10770 inputblock.cn.push({
10772 cls : 'roo-input-after input-group-append input-group-' +
10773 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10777 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10778 inputblock.cls += ' has-feedback';
10779 inputblock.cn.push(feedback);
10784 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10785 tooltip : 'This field is required'
10787 if (this.allowBlank ) {
10788 indicator.style = this.allowBlank ? ' display:none' : '';
10790 if (align ==='left' && this.fieldLabel.length) {
10792 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10799 cls : 'control-label col-form-label',
10800 html : this.fieldLabel
10811 var labelCfg = cfg.cn[1];
10812 var contentCfg = cfg.cn[2];
10814 if(this.indicatorpos == 'right'){
10819 cls : 'control-label col-form-label',
10823 html : this.fieldLabel
10837 labelCfg = cfg.cn[0];
10838 contentCfg = cfg.cn[1];
10842 if(this.labelWidth > 12){
10843 labelCfg.style = "width: " + this.labelWidth + 'px';
10846 if(this.labelWidth < 13 && this.labelmd == 0){
10847 this.labelmd = this.labelWidth;
10850 if(this.labellg > 0){
10851 labelCfg.cls += ' col-lg-' + this.labellg;
10852 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10855 if(this.labelmd > 0){
10856 labelCfg.cls += ' col-md-' + this.labelmd;
10857 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10860 if(this.labelsm > 0){
10861 labelCfg.cls += ' col-sm-' + this.labelsm;
10862 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10865 if(this.labelxs > 0){
10866 labelCfg.cls += ' col-xs-' + this.labelxs;
10867 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10871 } else if ( this.fieldLabel.length) {
10878 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10879 tooltip : 'This field is required',
10880 style : this.allowBlank ? ' display:none' : ''
10884 //cls : 'input-group-addon',
10885 html : this.fieldLabel
10893 if(this.indicatorpos == 'right'){
10898 //cls : 'input-group-addon',
10899 html : this.fieldLabel
10904 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10905 tooltip : 'This field is required',
10906 style : this.allowBlank ? ' display:none' : ''
10926 if (this.parentType === 'Navbar' && this.parent().bar) {
10927 cfg.cls += ' navbar-form';
10930 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10931 // on BS4 we do this only if not form
10932 cfg.cls += ' navbar-form';
10940 * return the real input element.
10942 inputEl: function ()
10944 return this.el.select('input.form-control',true).first();
10947 tooltipEl : function()
10949 return this.inputEl();
10952 indicatorEl : function()
10954 if (Roo.bootstrap.version == 4) {
10955 return false; // not enabled in v4 yet.
10958 var indicator = this.el.select('i.roo-required-indicator',true).first();
10968 setDisabled : function(v)
10970 var i = this.inputEl().dom;
10972 i.removeAttribute('disabled');
10976 i.setAttribute('disabled','true');
10978 initEvents : function()
10981 this.inputEl().on("keydown" , this.fireKey, this);
10982 this.inputEl().on("focus", this.onFocus, this);
10983 this.inputEl().on("blur", this.onBlur, this);
10985 this.inputEl().relayEvent('keyup', this);
10987 this.indicator = this.indicatorEl();
10989 if(this.indicator){
10990 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10993 // reference to original value for reset
10994 this.originalValue = this.getValue();
10995 //Roo.form.TextField.superclass.initEvents.call(this);
10996 if(this.validationEvent == 'keyup'){
10997 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10998 this.inputEl().on('keyup', this.filterValidation, this);
11000 else if(this.validationEvent !== false){
11001 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11004 if(this.selectOnFocus){
11005 this.on("focus", this.preFocus, this);
11008 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11009 this.inputEl().on("keypress", this.filterKeys, this);
11011 this.inputEl().relayEvent('keypress', this);
11014 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11015 this.el.on("click", this.autoSize, this);
11018 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11019 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11022 if (typeof(this.before) == 'object') {
11023 this.before.render(this.el.select('.roo-input-before',true).first());
11025 if (typeof(this.after) == 'object') {
11026 this.after.render(this.el.select('.roo-input-after',true).first());
11029 this.inputEl().on('change', this.onChange, this);
11032 filterValidation : function(e){
11033 if(!e.isNavKeyPress()){
11034 this.validationTask.delay(this.validationDelay);
11038 * Validates the field value
11039 * @return {Boolean} True if the value is valid, else false
11041 validate : function(){
11042 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11043 if(this.disabled || this.validateValue(this.getRawValue())){
11048 this.markInvalid();
11054 * Validates a value according to the field's validation rules and marks the field as invalid
11055 * if the validation fails
11056 * @param {Mixed} value The value to validate
11057 * @return {Boolean} True if the value is valid, else false
11059 validateValue : function(value)
11061 if(this.getVisibilityEl().hasClass('hidden')){
11065 if(value.length < 1) { // if it's blank
11066 if(this.allowBlank){
11072 if(value.length < this.minLength){
11075 if(value.length > this.maxLength){
11079 var vt = Roo.form.VTypes;
11080 if(!vt[this.vtype](value, this)){
11084 if(typeof this.validator == "function"){
11085 var msg = this.validator(value);
11089 if (typeof(msg) == 'string') {
11090 this.invalidText = msg;
11094 if(this.regex && !this.regex.test(value)){
11102 fireKey : function(e){
11103 //Roo.log('field ' + e.getKey());
11104 if(e.isNavKeyPress()){
11105 this.fireEvent("specialkey", this, e);
11108 focus : function (selectText){
11110 this.inputEl().focus();
11111 if(selectText === true){
11112 this.inputEl().dom.select();
11118 onFocus : function(){
11119 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11120 // this.el.addClass(this.focusClass);
11122 if(!this.hasFocus){
11123 this.hasFocus = true;
11124 this.startValue = this.getValue();
11125 this.fireEvent("focus", this);
11129 beforeBlur : Roo.emptyFn,
11133 onBlur : function(){
11135 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136 //this.el.removeClass(this.focusClass);
11138 this.hasFocus = false;
11139 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11142 var v = this.getValue();
11143 if(String(v) !== String(this.startValue)){
11144 this.fireEvent('change', this, v, this.startValue);
11146 this.fireEvent("blur", this);
11149 onChange : function(e)
11151 var v = this.getValue();
11152 if(String(v) !== String(this.startValue)){
11153 this.fireEvent('change', this, v, this.startValue);
11159 * Resets the current field value to the originally loaded value and clears any validation messages
11161 reset : function(){
11162 this.setValue(this.originalValue);
11166 * Returns the name of the field
11167 * @return {Mixed} name The name field
11169 getName: function(){
11173 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11174 * @return {Mixed} value The field value
11176 getValue : function(){
11178 var v = this.inputEl().getValue();
11183 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11184 * @return {Mixed} value The field value
11186 getRawValue : function(){
11187 var v = this.inputEl().getValue();
11193 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11194 * @param {Mixed} value The value to set
11196 setRawValue : function(v){
11197 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11200 selectText : function(start, end){
11201 var v = this.getRawValue();
11203 start = start === undefined ? 0 : start;
11204 end = end === undefined ? v.length : end;
11205 var d = this.inputEl().dom;
11206 if(d.setSelectionRange){
11207 d.setSelectionRange(start, end);
11208 }else if(d.createTextRange){
11209 var range = d.createTextRange();
11210 range.moveStart("character", start);
11211 range.moveEnd("character", v.length-end);
11218 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11219 * @param {Mixed} value The value to set
11221 setValue : function(v){
11224 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11230 processValue : function(value){
11231 if(this.stripCharsRe){
11232 var newValue = value.replace(this.stripCharsRe, '');
11233 if(newValue !== value){
11234 this.setRawValue(newValue);
11241 preFocus : function(){
11243 if(this.selectOnFocus){
11244 this.inputEl().dom.select();
11247 filterKeys : function(e){
11248 var k = e.getKey();
11249 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11252 var c = e.getCharCode(), cc = String.fromCharCode(c);
11253 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11256 if(!this.maskRe.test(cc)){
11261 * Clear any invalid styles/messages for this field
11263 clearInvalid : function(){
11265 if(!this.el || this.preventMark){ // not rendered
11270 this.el.removeClass([this.invalidClass, 'is-invalid']);
11272 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11274 var feedback = this.el.select('.form-control-feedback', true).first();
11277 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11282 if(this.indicator){
11283 this.indicator.removeClass('visible');
11284 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11287 this.fireEvent('valid', this);
11291 * Mark this field as valid
11293 markValid : function()
11295 if(!this.el || this.preventMark){ // not rendered...
11299 this.el.removeClass([this.invalidClass, this.validClass]);
11300 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11302 var feedback = this.el.select('.form-control-feedback', true).first();
11305 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11308 if(this.indicator){
11309 this.indicator.removeClass('visible');
11310 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11318 if(this.allowBlank && !this.getRawValue().length){
11321 if (Roo.bootstrap.version == 3) {
11322 this.el.addClass(this.validClass);
11324 this.inputEl().addClass('is-valid');
11327 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11329 var feedback = this.el.select('.form-control-feedback', true).first();
11332 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11333 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11338 this.fireEvent('valid', this);
11342 * Mark this field as invalid
11343 * @param {String} msg The validation message
11345 markInvalid : function(msg)
11347 if(!this.el || this.preventMark){ // not rendered
11351 this.el.removeClass([this.invalidClass, this.validClass]);
11352 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11354 var feedback = this.el.select('.form-control-feedback', true).first();
11357 this.el.select('.form-control-feedback', true).first().removeClass(
11358 [this.invalidFeedbackClass, this.validFeedbackClass]);
11365 if(this.allowBlank && !this.getRawValue().length){
11369 if(this.indicator){
11370 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11371 this.indicator.addClass('visible');
11373 if (Roo.bootstrap.version == 3) {
11374 this.el.addClass(this.invalidClass);
11376 this.inputEl().addClass('is-invalid');
11381 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11383 var feedback = this.el.select('.form-control-feedback', true).first();
11386 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11388 if(this.getValue().length || this.forceFeedback){
11389 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11396 this.fireEvent('invalid', this, msg);
11399 SafariOnKeyDown : function(event)
11401 // this is a workaround for a password hang bug on chrome/ webkit.
11402 if (this.inputEl().dom.type != 'password') {
11406 var isSelectAll = false;
11408 if(this.inputEl().dom.selectionEnd > 0){
11409 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11411 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11412 event.preventDefault();
11417 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11419 event.preventDefault();
11420 // this is very hacky as keydown always get's upper case.
11422 var cc = String.fromCharCode(event.getCharCode());
11423 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11427 adjustWidth : function(tag, w){
11428 tag = tag.toLowerCase();
11429 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11430 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11431 if(tag == 'input'){
11434 if(tag == 'textarea'){
11437 }else if(Roo.isOpera){
11438 if(tag == 'input'){
11441 if(tag == 'textarea'){
11449 setFieldLabel : function(v)
11451 if(!this.rendered){
11455 if(this.indicatorEl()){
11456 var ar = this.el.select('label > span',true);
11458 if (ar.elements.length) {
11459 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11460 this.fieldLabel = v;
11464 var br = this.el.select('label',true);
11466 if(br.elements.length) {
11467 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468 this.fieldLabel = v;
11472 Roo.log('Cannot Found any of label > span || label in input');
11476 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11477 this.fieldLabel = v;
11492 * @class Roo.bootstrap.TextArea
11493 * @extends Roo.bootstrap.Input
11494 * Bootstrap TextArea class
11495 * @cfg {Number} cols Specifies the visible width of a text area
11496 * @cfg {Number} rows Specifies the visible number of lines in a text area
11497 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11498 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11499 * @cfg {string} html text
11502 * Create a new TextArea
11503 * @param {Object} config The config object
11506 Roo.bootstrap.TextArea = function(config){
11507 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11511 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11521 getAutoCreate : function(){
11523 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11529 if(this.inputType != 'hidden'){
11530 cfg.cls = 'form-group' //input-group
11538 value : this.value || '',
11539 html: this.html || '',
11540 cls : 'form-control',
11541 placeholder : this.placeholder || ''
11545 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11546 input.maxLength = this.maxLength;
11550 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11554 input.cols = this.cols;
11557 if (this.readOnly) {
11558 input.readonly = true;
11562 input.name = this.name;
11566 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11570 ['xs','sm','md','lg'].map(function(size){
11571 if (settings[size]) {
11572 cfg.cls += ' col-' + size + '-' + settings[size];
11576 var inputblock = input;
11578 if(this.hasFeedback && !this.allowBlank){
11582 cls: 'glyphicon form-control-feedback'
11586 cls : 'has-feedback',
11595 if (this.before || this.after) {
11598 cls : 'input-group',
11602 inputblock.cn.push({
11604 cls : 'input-group-addon',
11609 inputblock.cn.push(input);
11611 if(this.hasFeedback && !this.allowBlank){
11612 inputblock.cls += ' has-feedback';
11613 inputblock.cn.push(feedback);
11617 inputblock.cn.push({
11619 cls : 'input-group-addon',
11626 if (align ==='left' && this.fieldLabel.length) {
11631 cls : 'control-label',
11632 html : this.fieldLabel
11643 if(this.labelWidth > 12){
11644 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11647 if(this.labelWidth < 13 && this.labelmd == 0){
11648 this.labelmd = this.labelWidth;
11651 if(this.labellg > 0){
11652 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11653 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11656 if(this.labelmd > 0){
11657 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11658 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11661 if(this.labelsm > 0){
11662 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11663 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11666 if(this.labelxs > 0){
11667 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11668 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11671 } else if ( this.fieldLabel.length) {
11676 //cls : 'input-group-addon',
11677 html : this.fieldLabel
11695 if (this.disabled) {
11696 input.disabled=true;
11703 * return the real textarea element.
11705 inputEl: function ()
11707 return this.el.select('textarea.form-control',true).first();
11711 * Clear any invalid styles/messages for this field
11713 clearInvalid : function()
11716 if(!this.el || this.preventMark){ // not rendered
11720 var label = this.el.select('label', true).first();
11721 var icon = this.el.select('i.fa-star', true).first();
11726 this.el.removeClass( this.validClass);
11727 this.inputEl().removeClass('is-invalid');
11729 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11731 var feedback = this.el.select('.form-control-feedback', true).first();
11734 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11739 this.fireEvent('valid', this);
11743 * Mark this field as valid
11745 markValid : function()
11747 if(!this.el || this.preventMark){ // not rendered
11751 this.el.removeClass([this.invalidClass, this.validClass]);
11752 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11754 var feedback = this.el.select('.form-control-feedback', true).first();
11757 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11760 if(this.disabled || this.allowBlank){
11764 var label = this.el.select('label', true).first();
11765 var icon = this.el.select('i.fa-star', true).first();
11770 if (Roo.bootstrap.version == 3) {
11771 this.el.addClass(this.validClass);
11773 this.inputEl().addClass('is-valid');
11777 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11779 var feedback = this.el.select('.form-control-feedback', true).first();
11782 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11783 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11788 this.fireEvent('valid', this);
11792 * Mark this field as invalid
11793 * @param {String} msg The validation message
11795 markInvalid : function(msg)
11797 if(!this.el || this.preventMark){ // not rendered
11801 this.el.removeClass([this.invalidClass, this.validClass]);
11802 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11804 var feedback = this.el.select('.form-control-feedback', true).first();
11807 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11810 if(this.disabled || this.allowBlank){
11814 var label = this.el.select('label', true).first();
11815 var icon = this.el.select('i.fa-star', true).first();
11817 if(!this.getValue().length && label && !icon){
11818 this.el.createChild({
11820 cls : 'text-danger fa fa-lg fa-star',
11821 tooltip : 'This field is required',
11822 style : 'margin-right:5px;'
11826 if (Roo.bootstrap.version == 3) {
11827 this.el.addClass(this.invalidClass);
11829 this.inputEl().addClass('is-invalid');
11832 // fixme ... this may be depricated need to test..
11833 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11835 var feedback = this.el.select('.form-control-feedback', true).first();
11838 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11840 if(this.getValue().length || this.forceFeedback){
11841 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11848 this.fireEvent('invalid', this, msg);
11856 * trigger field - base class for combo..
11861 * @class Roo.bootstrap.TriggerField
11862 * @extends Roo.bootstrap.Input
11863 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11864 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11865 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11866 * for which you can provide a custom implementation. For example:
11868 var trigger = new Roo.bootstrap.TriggerField();
11869 trigger.onTriggerClick = myTriggerFn;
11870 trigger.applyTo('my-field');
11873 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11874 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11875 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11876 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11877 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11880 * Create a new TriggerField.
11881 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11882 * to the base TextField)
11884 Roo.bootstrap.TriggerField = function(config){
11885 this.mimicing = false;
11886 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11889 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11891 * @cfg {String} triggerClass A CSS class to apply to the trigger
11894 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11899 * @cfg {Boolean} removable (true|false) special filter default false
11903 /** @cfg {Boolean} grow @hide */
11904 /** @cfg {Number} growMin @hide */
11905 /** @cfg {Number} growMax @hide */
11911 autoSize: Roo.emptyFn,
11915 deferHeight : true,
11918 actionMode : 'wrap',
11923 getAutoCreate : function(){
11925 var align = this.labelAlign || this.parentLabelAlign();
11930 cls: 'form-group' //input-group
11937 type : this.inputType,
11938 cls : 'form-control',
11939 autocomplete: 'new-password',
11940 placeholder : this.placeholder || ''
11944 input.name = this.name;
11947 input.cls += ' input-' + this.size;
11950 if (this.disabled) {
11951 input.disabled=true;
11954 var inputblock = input;
11956 if(this.hasFeedback && !this.allowBlank){
11960 cls: 'glyphicon form-control-feedback'
11963 if(this.removable && !this.editable ){
11965 cls : 'has-feedback',
11971 cls : 'roo-combo-removable-btn close'
11978 cls : 'has-feedback',
11987 if(this.removable && !this.editable ){
11989 cls : 'roo-removable',
11995 cls : 'roo-combo-removable-btn close'
12002 if (this.before || this.after) {
12005 cls : 'input-group',
12009 inputblock.cn.push({
12011 cls : 'input-group-addon input-group-prepend input-group-text',
12016 inputblock.cn.push(input);
12018 if(this.hasFeedback && !this.allowBlank){
12019 inputblock.cls += ' has-feedback';
12020 inputblock.cn.push(feedback);
12024 inputblock.cn.push({
12026 cls : 'input-group-addon input-group-append input-group-text',
12035 var ibwrap = inputblock;
12040 cls: 'roo-select2-choices',
12044 cls: 'roo-select2-search-field',
12056 cls: 'roo-select2-container input-group',
12061 cls: 'form-hidden-field'
12067 if(!this.multiple && this.showToggleBtn){
12073 if (this.caret != false) {
12076 cls: 'fa fa-' + this.caret
12083 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12085 Roo.bootstrap.version == 3 ? caret : '',
12088 cls: 'combobox-clear',
12102 combobox.cls += ' roo-select2-container-multi';
12106 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12107 tooltip : 'This field is required'
12109 if (Roo.bootstrap.version == 4) {
12112 style : 'display:none'
12117 if (align ==='left' && this.fieldLabel.length) {
12119 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12126 cls : 'control-label',
12127 html : this.fieldLabel
12139 var labelCfg = cfg.cn[1];
12140 var contentCfg = cfg.cn[2];
12142 if(this.indicatorpos == 'right'){
12147 cls : 'control-label',
12151 html : this.fieldLabel
12165 labelCfg = cfg.cn[0];
12166 contentCfg = cfg.cn[1];
12169 if(this.labelWidth > 12){
12170 labelCfg.style = "width: " + this.labelWidth + 'px';
12173 if(this.labelWidth < 13 && this.labelmd == 0){
12174 this.labelmd = this.labelWidth;
12177 if(this.labellg > 0){
12178 labelCfg.cls += ' col-lg-' + this.labellg;
12179 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12182 if(this.labelmd > 0){
12183 labelCfg.cls += ' col-md-' + this.labelmd;
12184 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12187 if(this.labelsm > 0){
12188 labelCfg.cls += ' col-sm-' + this.labelsm;
12189 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12192 if(this.labelxs > 0){
12193 labelCfg.cls += ' col-xs-' + this.labelxs;
12194 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12197 } else if ( this.fieldLabel.length) {
12198 // Roo.log(" label");
12203 //cls : 'input-group-addon',
12204 html : this.fieldLabel
12212 if(this.indicatorpos == 'right'){
12220 html : this.fieldLabel
12234 // Roo.log(" no label && no align");
12241 ['xs','sm','md','lg'].map(function(size){
12242 if (settings[size]) {
12243 cfg.cls += ' col-' + size + '-' + settings[size];
12254 onResize : function(w, h){
12255 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12256 // if(typeof w == 'number'){
12257 // var x = w - this.trigger.getWidth();
12258 // this.inputEl().setWidth(this.adjustWidth('input', x));
12259 // this.trigger.setStyle('left', x+'px');
12264 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12267 getResizeEl : function(){
12268 return this.inputEl();
12272 getPositionEl : function(){
12273 return this.inputEl();
12277 alignErrorIcon : function(){
12278 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12282 initEvents : function(){
12286 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12287 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12288 if(!this.multiple && this.showToggleBtn){
12289 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12290 if(this.hideTrigger){
12291 this.trigger.setDisplayed(false);
12293 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12297 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12300 if(this.removable && !this.editable && !this.tickable){
12301 var close = this.closeTriggerEl();
12304 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12305 close.on('click', this.removeBtnClick, this, close);
12309 //this.trigger.addClassOnOver('x-form-trigger-over');
12310 //this.trigger.addClassOnClick('x-form-trigger-click');
12313 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12317 closeTriggerEl : function()
12319 var close = this.el.select('.roo-combo-removable-btn', true).first();
12320 return close ? close : false;
12323 removeBtnClick : function(e, h, el)
12325 e.preventDefault();
12327 if(this.fireEvent("remove", this) !== false){
12329 this.fireEvent("afterremove", this)
12333 createList : function()
12335 this.list = Roo.get(document.body).createChild({
12336 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12337 cls: 'typeahead typeahead-long dropdown-menu',
12338 style: 'display:none'
12341 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12346 initTrigger : function(){
12351 onDestroy : function(){
12353 this.trigger.removeAllListeners();
12354 // this.trigger.remove();
12357 // this.wrap.remove();
12359 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12363 onFocus : function(){
12364 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12366 if(!this.mimicing){
12367 this.wrap.addClass('x-trigger-wrap-focus');
12368 this.mimicing = true;
12369 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12370 if(this.monitorTab){
12371 this.el.on("keydown", this.checkTab, this);
12378 checkTab : function(e){
12379 if(e.getKey() == e.TAB){
12380 this.triggerBlur();
12385 onBlur : function(){
12390 mimicBlur : function(e, t){
12392 if(!this.wrap.contains(t) && this.validateBlur()){
12393 this.triggerBlur();
12399 triggerBlur : function(){
12400 this.mimicing = false;
12401 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12402 if(this.monitorTab){
12403 this.el.un("keydown", this.checkTab, this);
12405 //this.wrap.removeClass('x-trigger-wrap-focus');
12406 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12410 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12411 validateBlur : function(e, t){
12416 onDisable : function(){
12417 this.inputEl().dom.disabled = true;
12418 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12420 // this.wrap.addClass('x-item-disabled');
12425 onEnable : function(){
12426 this.inputEl().dom.disabled = false;
12427 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12429 // this.el.removeClass('x-item-disabled');
12434 onShow : function(){
12435 var ae = this.getActionEl();
12438 ae.dom.style.display = '';
12439 ae.dom.style.visibility = 'visible';
12445 onHide : function(){
12446 var ae = this.getActionEl();
12447 ae.dom.style.display = 'none';
12451 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12452 * by an implementing function.
12454 * @param {EventObject} e
12456 onTriggerClick : Roo.emptyFn
12464 * @class Roo.bootstrap.CardUploader
12465 * @extends Roo.bootstrap.Button
12466 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12467 * @cfg {Number} errorTimeout default 3000
12468 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12469 * @cfg {Array} html The button text.
12473 * Create a new CardUploader
12474 * @param {Object} config The config object
12477 Roo.bootstrap.CardUploader = function(config){
12481 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12484 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12491 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12494 errorTimeout : 3000,
12498 fileCollection : false,
12501 getAutoCreate : function()
12505 cls :'form-group' ,
12510 //cls : 'input-group-addon',
12511 html : this.fieldLabel
12518 value : this.value,
12519 cls : 'd-none form-control'
12524 multiple : 'multiple',
12526 cls : 'd-none roo-card-upload-selector'
12530 cls : 'roo-card-uploader-button-container w-100 mb-2'
12533 cls : 'card-columns roo-card-uploader-container'
12543 getChildContainer : function() /// what children are added to.
12545 return this.containerEl;
12548 getButtonContainer : function() /// what children are added to.
12550 return this.el.select(".roo-card-uploader-button-container").first();
12553 initEvents : function()
12556 Roo.bootstrap.Input.prototype.initEvents.call(this);
12560 xns: Roo.bootstrap,
12563 container_method : 'getButtonContainer' ,
12564 html : this.html, // fix changable?
12567 'click' : function(btn, e) {
12576 this.urlAPI = (window.createObjectURL && window) ||
12577 (window.URL && URL.revokeObjectURL && URL) ||
12578 (window.webkitURL && webkitURL);
12583 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12585 this.selectorEl.on('change', this.onFileSelected, this);
12588 this.images.forEach(function(img) {
12591 this.images = false;
12593 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12599 onClick : function(e)
12601 e.preventDefault();
12603 this.selectorEl.dom.click();
12607 onFileSelected : function(e)
12609 e.preventDefault();
12611 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12615 Roo.each(this.selectorEl.dom.files, function(file){
12616 this.addFile(file);
12625 addFile : function(file)
12628 if(typeof(file) === 'string'){
12629 throw "Add file by name?"; // should not happen
12633 if(!file || !this.urlAPI){
12643 var url = _this.urlAPI.createObjectURL( file);
12646 id : Roo.bootstrap.CardUploader.ID--,
12647 is_uploaded : false,
12650 mimetype : file.type,
12657 addCard : function (data)
12659 // hidden input element?
12660 // if the file is not an image...
12661 //then we need to use something other that and header_image
12666 xns : Roo.bootstrap,
12667 xtype : 'CardFooter',
12670 xns : Roo.bootstrap,
12676 xns : Roo.bootstrap,
12678 html : String.format("<small>{0}</small>", data.title),
12679 cls : 'col-11 text-left',
12684 click : function() {
12685 this.downloadCard(data.id)
12691 xns : Roo.bootstrap,
12699 click : function() {
12700 t.removeCard(data.id)
12712 var cn = this.addxtype(
12715 xns : Roo.bootstrap,
12718 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12719 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12720 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12725 initEvents : function() {
12726 Roo.bootstrap.Card.prototype.initEvents.call(this);
12727 this.imgEl = this.el.select('.card-img-top').first();
12729 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12730 this.imgEl.set({ 'pointer' : 'cursor' });
12739 // dont' really need ot update items.
12740 // this.items.push(cn);
12741 this.fileCollection.add(cn);
12742 this.updateInput();
12745 removeCard : function(id)
12748 var card = this.fileCollection.get(id);
12749 card.data.is_deleted = 1;
12750 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12751 this.fileCollection.remove(card);
12752 //this.items = this.items.filter(function(e) { return e != card });
12753 // dont' really need ot update items.
12754 card.el.dom.parentNode.removeChild(card.el.dom);
12759 this.fileCollection.each(function(card) {
12760 card.el.dom.parentNode.removeChild(card.el.dom);
12762 this.fileCollection.clear();
12763 this.updateInput();
12766 updateInput : function()
12769 this.fileCollection.each(function(e) {
12773 this.inputEl().dom.value = JSON.stringify(data);
12780 Roo.bootstrap.CardUploader.ID = -1;/*
12782 * Ext JS Library 1.1.1
12783 * Copyright(c) 2006-2007, Ext JS, LLC.
12785 * Originally Released Under LGPL - original licence link has changed is not relivant.
12788 * <script type="text/javascript">
12793 * @class Roo.data.SortTypes
12795 * Defines the default sorting (casting?) comparison functions used when sorting data.
12797 Roo.data.SortTypes = {
12799 * Default sort that does nothing
12800 * @param {Mixed} s The value being converted
12801 * @return {Mixed} The comparison value
12803 none : function(s){
12808 * The regular expression used to strip tags
12812 stripTagsRE : /<\/?[^>]+>/gi,
12815 * Strips all HTML tags to sort on text only
12816 * @param {Mixed} s The value being converted
12817 * @return {String} The comparison value
12819 asText : function(s){
12820 return String(s).replace(this.stripTagsRE, "");
12824 * Strips all HTML tags to sort on text only - Case insensitive
12825 * @param {Mixed} s The value being converted
12826 * @return {String} The comparison value
12828 asUCText : function(s){
12829 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12833 * Case insensitive string
12834 * @param {Mixed} s The value being converted
12835 * @return {String} The comparison value
12837 asUCString : function(s) {
12838 return String(s).toUpperCase();
12843 * @param {Mixed} s The value being converted
12844 * @return {Number} The comparison value
12846 asDate : function(s) {
12850 if(s instanceof Date){
12851 return s.getTime();
12853 return Date.parse(String(s));
12858 * @param {Mixed} s The value being converted
12859 * @return {Float} The comparison value
12861 asFloat : function(s) {
12862 var val = parseFloat(String(s).replace(/,/g, ""));
12871 * @param {Mixed} s The value being converted
12872 * @return {Number} The comparison value
12874 asInt : function(s) {
12875 var val = parseInt(String(s).replace(/,/g, ""));
12883 * Ext JS Library 1.1.1
12884 * Copyright(c) 2006-2007, Ext JS, LLC.
12886 * Originally Released Under LGPL - original licence link has changed is not relivant.
12889 * <script type="text/javascript">
12893 * @class Roo.data.Record
12894 * Instances of this class encapsulate both record <em>definition</em> information, and record
12895 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12896 * to access Records cached in an {@link Roo.data.Store} object.<br>
12898 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12899 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12902 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12904 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12905 * {@link #create}. The parameters are the same.
12906 * @param {Array} data An associative Array of data values keyed by the field name.
12907 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12908 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12909 * not specified an integer id is generated.
12911 Roo.data.Record = function(data, id){
12912 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12917 * Generate a constructor for a specific record layout.
12918 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12919 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12920 * Each field definition object may contain the following properties: <ul>
12921 * <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,
12922 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12923 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12924 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12925 * is being used, then this is a string containing the javascript expression to reference the data relative to
12926 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12927 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12928 * this may be omitted.</p></li>
12929 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12930 * <ul><li>auto (Default, implies no conversion)</li>
12935 * <li>date</li></ul></p></li>
12936 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12937 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12938 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12939 * by the Reader into an object that will be stored in the Record. It is passed the
12940 * following parameters:<ul>
12941 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12943 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12945 * <br>usage:<br><pre><code>
12946 var TopicRecord = Roo.data.Record.create(
12947 {name: 'title', mapping: 'topic_title'},
12948 {name: 'author', mapping: 'username'},
12949 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12950 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12951 {name: 'lastPoster', mapping: 'user2'},
12952 {name: 'excerpt', mapping: 'post_text'}
12955 var myNewRecord = new TopicRecord({
12956 title: 'Do my job please',
12959 lastPost: new Date(),
12960 lastPoster: 'Animal',
12961 excerpt: 'No way dude!'
12963 myStore.add(myNewRecord);
12968 Roo.data.Record.create = function(o){
12969 var f = function(){
12970 f.superclass.constructor.apply(this, arguments);
12972 Roo.extend(f, Roo.data.Record);
12973 var p = f.prototype;
12974 p.fields = new Roo.util.MixedCollection(false, function(field){
12977 for(var i = 0, len = o.length; i < len; i++){
12978 p.fields.add(new Roo.data.Field(o[i]));
12980 f.getField = function(name){
12981 return p.fields.get(name);
12986 Roo.data.Record.AUTO_ID = 1000;
12987 Roo.data.Record.EDIT = 'edit';
12988 Roo.data.Record.REJECT = 'reject';
12989 Roo.data.Record.COMMIT = 'commit';
12991 Roo.data.Record.prototype = {
12993 * Readonly flag - true if this record has been modified.
13002 join : function(store){
13003 this.store = store;
13007 * Set the named field to the specified value.
13008 * @param {String} name The name of the field to set.
13009 * @param {Object} value The value to set the field to.
13011 set : function(name, value){
13012 if(this.data[name] == value){
13016 if(!this.modified){
13017 this.modified = {};
13019 if(typeof this.modified[name] == 'undefined'){
13020 this.modified[name] = this.data[name];
13022 this.data[name] = value;
13023 if(!this.editing && this.store){
13024 this.store.afterEdit(this);
13029 * Get the value of the named field.
13030 * @param {String} name The name of the field to get the value of.
13031 * @return {Object} The value of the field.
13033 get : function(name){
13034 return this.data[name];
13038 beginEdit : function(){
13039 this.editing = true;
13040 this.modified = {};
13044 cancelEdit : function(){
13045 this.editing = false;
13046 delete this.modified;
13050 endEdit : function(){
13051 this.editing = false;
13052 if(this.dirty && this.store){
13053 this.store.afterEdit(this);
13058 * Usually called by the {@link Roo.data.Store} which owns the Record.
13059 * Rejects all changes made to the Record since either creation, or the last commit operation.
13060 * Modified fields are reverted to their original values.
13062 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13063 * of reject operations.
13065 reject : function(){
13066 var m = this.modified;
13068 if(typeof m[n] != "function"){
13069 this.data[n] = m[n];
13072 this.dirty = false;
13073 delete this.modified;
13074 this.editing = false;
13076 this.store.afterReject(this);
13081 * Usually called by the {@link Roo.data.Store} which owns the Record.
13082 * Commits all changes made to the Record since either creation, or the last commit operation.
13084 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13085 * of commit operations.
13087 commit : function(){
13088 this.dirty = false;
13089 delete this.modified;
13090 this.editing = false;
13092 this.store.afterCommit(this);
13097 hasError : function(){
13098 return this.error != null;
13102 clearError : function(){
13107 * Creates a copy of this record.
13108 * @param {String} id (optional) A new record id if you don't want to use this record's id
13111 copy : function(newId) {
13112 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13116 * Ext JS Library 1.1.1
13117 * Copyright(c) 2006-2007, Ext JS, LLC.
13119 * Originally Released Under LGPL - original licence link has changed is not relivant.
13122 * <script type="text/javascript">
13128 * @class Roo.data.Store
13129 * @extends Roo.util.Observable
13130 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13131 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13133 * 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
13134 * has no knowledge of the format of the data returned by the Proxy.<br>
13136 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13137 * instances from the data object. These records are cached and made available through accessor functions.
13139 * Creates a new Store.
13140 * @param {Object} config A config object containing the objects needed for the Store to access data,
13141 * and read the data into Records.
13143 Roo.data.Store = function(config){
13144 this.data = new Roo.util.MixedCollection(false);
13145 this.data.getKey = function(o){
13148 this.baseParams = {};
13150 this.paramNames = {
13155 "multisort" : "_multisort"
13158 if(config && config.data){
13159 this.inlineData = config.data;
13160 delete config.data;
13163 Roo.apply(this, config);
13165 if(this.reader){ // reader passed
13166 this.reader = Roo.factory(this.reader, Roo.data);
13167 this.reader.xmodule = this.xmodule || false;
13168 if(!this.recordType){
13169 this.recordType = this.reader.recordType;
13171 if(this.reader.onMetaChange){
13172 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13176 if(this.recordType){
13177 this.fields = this.recordType.prototype.fields;
13179 this.modified = [];
13183 * @event datachanged
13184 * Fires when the data cache has changed, and a widget which is using this Store
13185 * as a Record cache should refresh its view.
13186 * @param {Store} this
13188 datachanged : true,
13190 * @event metachange
13191 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13192 * @param {Store} this
13193 * @param {Object} meta The JSON metadata
13198 * Fires when Records have been added to the Store
13199 * @param {Store} this
13200 * @param {Roo.data.Record[]} records The array of Records added
13201 * @param {Number} index The index at which the record(s) were added
13206 * Fires when a Record has been removed from the Store
13207 * @param {Store} this
13208 * @param {Roo.data.Record} record The Record that was removed
13209 * @param {Number} index The index at which the record was removed
13214 * Fires when a Record has been updated
13215 * @param {Store} this
13216 * @param {Roo.data.Record} record The Record that was updated
13217 * @param {String} operation The update operation being performed. Value may be one of:
13219 Roo.data.Record.EDIT
13220 Roo.data.Record.REJECT
13221 Roo.data.Record.COMMIT
13227 * Fires when the data cache has been cleared.
13228 * @param {Store} this
13232 * @event beforeload
13233 * Fires before a request is made for a new data object. If the beforeload handler returns false
13234 * the load action will be canceled.
13235 * @param {Store} this
13236 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13240 * @event beforeloadadd
13241 * Fires after a new set of Records has been loaded.
13242 * @param {Store} this
13243 * @param {Roo.data.Record[]} records The Records that were loaded
13244 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13246 beforeloadadd : true,
13249 * Fires after a new set of Records has been loaded, before they are added to the store.
13250 * @param {Store} this
13251 * @param {Roo.data.Record[]} records The Records that were loaded
13252 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253 * @params {Object} return from reader
13257 * @event loadexception
13258 * Fires if an exception occurs in the Proxy during loading.
13259 * Called with the signature of the Proxy's "loadexception" event.
13260 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13263 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13264 * @param {Object} load options
13265 * @param {Object} jsonData from your request (normally this contains the Exception)
13267 loadexception : true
13271 this.proxy = Roo.factory(this.proxy, Roo.data);
13272 this.proxy.xmodule = this.xmodule || false;
13273 this.relayEvents(this.proxy, ["loadexception"]);
13275 this.sortToggle = {};
13276 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13278 Roo.data.Store.superclass.constructor.call(this);
13280 if(this.inlineData){
13281 this.loadData(this.inlineData);
13282 delete this.inlineData;
13286 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13288 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13289 * without a remote query - used by combo/forms at present.
13293 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13296 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13299 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13300 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13303 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13304 * on any HTTP request
13307 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13310 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13314 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13315 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13317 remoteSort : false,
13320 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13321 * loaded or when a record is removed. (defaults to false).
13323 pruneModifiedRecords : false,
13326 lastOptions : null,
13329 * Add Records to the Store and fires the add event.
13330 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13332 add : function(records){
13333 records = [].concat(records);
13334 for(var i = 0, len = records.length; i < len; i++){
13335 records[i].join(this);
13337 var index = this.data.length;
13338 this.data.addAll(records);
13339 this.fireEvent("add", this, records, index);
13343 * Remove a Record from the Store and fires the remove event.
13344 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13346 remove : function(record){
13347 var index = this.data.indexOf(record);
13348 this.data.removeAt(index);
13350 if(this.pruneModifiedRecords){
13351 this.modified.remove(record);
13353 this.fireEvent("remove", this, record, index);
13357 * Remove all Records from the Store and fires the clear event.
13359 removeAll : function(){
13361 if(this.pruneModifiedRecords){
13362 this.modified = [];
13364 this.fireEvent("clear", this);
13368 * Inserts Records to the Store at the given index and fires the add event.
13369 * @param {Number} index The start index at which to insert the passed Records.
13370 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13372 insert : function(index, records){
13373 records = [].concat(records);
13374 for(var i = 0, len = records.length; i < len; i++){
13375 this.data.insert(index, records[i]);
13376 records[i].join(this);
13378 this.fireEvent("add", this, records, index);
13382 * Get the index within the cache of the passed Record.
13383 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13384 * @return {Number} The index of the passed Record. Returns -1 if not found.
13386 indexOf : function(record){
13387 return this.data.indexOf(record);
13391 * Get the index within the cache of the Record with the passed id.
13392 * @param {String} id The id of the Record to find.
13393 * @return {Number} The index of the Record. Returns -1 if not found.
13395 indexOfId : function(id){
13396 return this.data.indexOfKey(id);
13400 * Get the Record with the specified id.
13401 * @param {String} id The id of the Record to find.
13402 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13404 getById : function(id){
13405 return this.data.key(id);
13409 * Get the Record at the specified index.
13410 * @param {Number} index The index of the Record to find.
13411 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13413 getAt : function(index){
13414 return this.data.itemAt(index);
13418 * Returns a range of Records between specified indices.
13419 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13420 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13421 * @return {Roo.data.Record[]} An array of Records
13423 getRange : function(start, end){
13424 return this.data.getRange(start, end);
13428 storeOptions : function(o){
13429 o = Roo.apply({}, o);
13432 this.lastOptions = o;
13436 * Loads the Record cache from the configured Proxy using the configured Reader.
13438 * If using remote paging, then the first load call must specify the <em>start</em>
13439 * and <em>limit</em> properties in the options.params property to establish the initial
13440 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13442 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13443 * and this call will return before the new data has been loaded. Perform any post-processing
13444 * in a callback function, or in a "load" event handler.</strong>
13446 * @param {Object} options An object containing properties which control loading options:<ul>
13447 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13448 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13449 * passed the following arguments:<ul>
13450 * <li>r : Roo.data.Record[]</li>
13451 * <li>options: Options object from the load call</li>
13452 * <li>success: Boolean success indicator</li></ul></li>
13453 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13454 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13457 load : function(options){
13458 options = options || {};
13459 if(this.fireEvent("beforeload", this, options) !== false){
13460 this.storeOptions(options);
13461 var p = Roo.apply(options.params || {}, this.baseParams);
13462 // if meta was not loaded from remote source.. try requesting it.
13463 if (!this.reader.metaFromRemote) {
13464 p._requestMeta = 1;
13466 if(this.sortInfo && this.remoteSort){
13467 var pn = this.paramNames;
13468 p[pn["sort"]] = this.sortInfo.field;
13469 p[pn["dir"]] = this.sortInfo.direction;
13471 if (this.multiSort) {
13472 var pn = this.paramNames;
13473 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13476 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13481 * Reloads the Record cache from the configured Proxy using the configured Reader and
13482 * the options from the last load operation performed.
13483 * @param {Object} options (optional) An object containing properties which may override the options
13484 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13485 * the most recently used options are reused).
13487 reload : function(options){
13488 this.load(Roo.applyIf(options||{}, this.lastOptions));
13492 // Called as a callback by the Reader during a load operation.
13493 loadRecords : function(o, options, success){
13494 if(!o || success === false){
13495 if(success !== false){
13496 this.fireEvent("load", this, [], options, o);
13498 if(options.callback){
13499 options.callback.call(options.scope || this, [], options, false);
13503 // if data returned failure - throw an exception.
13504 if (o.success === false) {
13505 // show a message if no listener is registered.
13506 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13507 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13509 // loadmask wil be hooked into this..
13510 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13513 var r = o.records, t = o.totalRecords || r.length;
13515 this.fireEvent("beforeloadadd", this, r, options, o);
13517 if(!options || options.add !== true){
13518 if(this.pruneModifiedRecords){
13519 this.modified = [];
13521 for(var i = 0, len = r.length; i < len; i++){
13525 this.data = this.snapshot;
13526 delete this.snapshot;
13529 this.data.addAll(r);
13530 this.totalLength = t;
13532 this.fireEvent("datachanged", this);
13534 this.totalLength = Math.max(t, this.data.length+r.length);
13538 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13540 var e = new Roo.data.Record({});
13542 e.set(this.parent.displayField, this.parent.emptyTitle);
13543 e.set(this.parent.valueField, '');
13548 this.fireEvent("load", this, r, options, o);
13549 if(options.callback){
13550 options.callback.call(options.scope || this, r, options, true);
13556 * Loads data from a passed data block. A Reader which understands the format of the data
13557 * must have been configured in the constructor.
13558 * @param {Object} data The data block from which to read the Records. The format of the data expected
13559 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13560 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13562 loadData : function(o, append){
13563 var r = this.reader.readRecords(o);
13564 this.loadRecords(r, {add: append}, true);
13568 * using 'cn' the nested child reader read the child array into it's child stores.
13569 * @param {Object} rec The record with a 'children array
13571 loadDataFromChildren : function(rec)
13573 this.loadData(this.reader.toLoadData(rec));
13578 * Gets the number of cached records.
13580 * <em>If using paging, this may not be the total size of the dataset. If the data object
13581 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13582 * the data set size</em>
13584 getCount : function(){
13585 return this.data.length || 0;
13589 * Gets the total number of records in the dataset as returned by the server.
13591 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13592 * the dataset size</em>
13594 getTotalCount : function(){
13595 return this.totalLength || 0;
13599 * Returns the sort state of the Store as an object with two properties:
13601 field {String} The name of the field by which the Records are sorted
13602 direction {String} The sort order, "ASC" or "DESC"
13605 getSortState : function(){
13606 return this.sortInfo;
13610 applySort : function(){
13611 if(this.sortInfo && !this.remoteSort){
13612 var s = this.sortInfo, f = s.field;
13613 var st = this.fields.get(f).sortType;
13614 var fn = function(r1, r2){
13615 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13616 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13618 this.data.sort(s.direction, fn);
13619 if(this.snapshot && this.snapshot != this.data){
13620 this.snapshot.sort(s.direction, fn);
13626 * Sets the default sort column and order to be used by the next load operation.
13627 * @param {String} fieldName The name of the field to sort by.
13628 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13630 setDefaultSort : function(field, dir){
13631 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13635 * Sort the Records.
13636 * If remote sorting is used, the sort is performed on the server, and the cache is
13637 * reloaded. If local sorting is used, the cache is sorted internally.
13638 * @param {String} fieldName The name of the field to sort by.
13639 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13641 sort : function(fieldName, dir){
13642 var f = this.fields.get(fieldName);
13644 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13646 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13647 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13652 this.sortToggle[f.name] = dir;
13653 this.sortInfo = {field: f.name, direction: dir};
13654 if(!this.remoteSort){
13656 this.fireEvent("datachanged", this);
13658 this.load(this.lastOptions);
13663 * Calls the specified function for each of the Records in the cache.
13664 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13665 * Returning <em>false</em> aborts and exits the iteration.
13666 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13668 each : function(fn, scope){
13669 this.data.each(fn, scope);
13673 * Gets all records modified since the last commit. Modified records are persisted across load operations
13674 * (e.g., during paging).
13675 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13677 getModifiedRecords : function(){
13678 return this.modified;
13682 createFilterFn : function(property, value, anyMatch){
13683 if(!value.exec){ // not a regex
13684 value = String(value);
13685 if(value.length == 0){
13688 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13690 return function(r){
13691 return value.test(r.data[property]);
13696 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13697 * @param {String} property A field on your records
13698 * @param {Number} start The record index to start at (defaults to 0)
13699 * @param {Number} end The last record index to include (defaults to length - 1)
13700 * @return {Number} The sum
13702 sum : function(property, start, end){
13703 var rs = this.data.items, v = 0;
13704 start = start || 0;
13705 end = (end || end === 0) ? end : rs.length-1;
13707 for(var i = start; i <= end; i++){
13708 v += (rs[i].data[property] || 0);
13714 * Filter the records by a specified property.
13715 * @param {String} field A field on your records
13716 * @param {String/RegExp} value Either a string that the field
13717 * should start with or a RegExp to test against the field
13718 * @param {Boolean} anyMatch True to match any part not just the beginning
13720 filter : function(property, value, anyMatch){
13721 var fn = this.createFilterFn(property, value, anyMatch);
13722 return fn ? this.filterBy(fn) : this.clearFilter();
13726 * Filter by a function. The specified function will be called with each
13727 * record in this data source. If the function returns true the record is included,
13728 * otherwise it is filtered.
13729 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13730 * @param {Object} scope (optional) The scope of the function (defaults to this)
13732 filterBy : function(fn, scope){
13733 this.snapshot = this.snapshot || this.data;
13734 this.data = this.queryBy(fn, scope||this);
13735 this.fireEvent("datachanged", this);
13739 * Query the records by a specified property.
13740 * @param {String} field A field on your records
13741 * @param {String/RegExp} value Either a string that the field
13742 * should start with or a RegExp to test against the field
13743 * @param {Boolean} anyMatch True to match any part not just the beginning
13744 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13746 query : function(property, value, anyMatch){
13747 var fn = this.createFilterFn(property, value, anyMatch);
13748 return fn ? this.queryBy(fn) : this.data.clone();
13752 * Query by a function. The specified function will be called with each
13753 * record in this data source. If the function returns true the record is included
13755 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13756 * @param {Object} scope (optional) The scope of the function (defaults to this)
13757 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13759 queryBy : function(fn, scope){
13760 var data = this.snapshot || this.data;
13761 return data.filterBy(fn, scope||this);
13765 * Collects unique values for a particular dataIndex from this store.
13766 * @param {String} dataIndex The property to collect
13767 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13768 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13769 * @return {Array} An array of the unique values
13771 collect : function(dataIndex, allowNull, bypassFilter){
13772 var d = (bypassFilter === true && this.snapshot) ?
13773 this.snapshot.items : this.data.items;
13774 var v, sv, r = [], l = {};
13775 for(var i = 0, len = d.length; i < len; i++){
13776 v = d[i].data[dataIndex];
13778 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787 * Revert to a view of the Record cache with no filtering applied.
13788 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13790 clearFilter : function(suppressEvent){
13791 if(this.snapshot && this.snapshot != this.data){
13792 this.data = this.snapshot;
13793 delete this.snapshot;
13794 if(suppressEvent !== true){
13795 this.fireEvent("datachanged", this);
13801 afterEdit : function(record){
13802 if(this.modified.indexOf(record) == -1){
13803 this.modified.push(record);
13805 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13809 afterReject : function(record){
13810 this.modified.remove(record);
13811 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13815 afterCommit : function(record){
13816 this.modified.remove(record);
13817 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13821 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13822 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13824 commitChanges : function(){
13825 var m = this.modified.slice(0);
13826 this.modified = [];
13827 for(var i = 0, len = m.length; i < len; i++){
13833 * Cancel outstanding changes on all changed records.
13835 rejectChanges : function(){
13836 var m = this.modified.slice(0);
13837 this.modified = [];
13838 for(var i = 0, len = m.length; i < len; i++){
13843 onMetaChange : function(meta, rtype, o){
13844 this.recordType = rtype;
13845 this.fields = rtype.prototype.fields;
13846 delete this.snapshot;
13847 this.sortInfo = meta.sortInfo || this.sortInfo;
13848 this.modified = [];
13849 this.fireEvent('metachange', this, this.reader.meta);
13852 moveIndex : function(data, type)
13854 var index = this.indexOf(data);
13856 var newIndex = index + type;
13860 this.insert(newIndex, data);
13865 * Ext JS Library 1.1.1
13866 * Copyright(c) 2006-2007, Ext JS, LLC.
13868 * Originally Released Under LGPL - original licence link has changed is not relivant.
13871 * <script type="text/javascript">
13875 * @class Roo.data.SimpleStore
13876 * @extends Roo.data.Store
13877 * Small helper class to make creating Stores from Array data easier.
13878 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13879 * @cfg {Array} fields An array of field definition objects, or field name strings.
13880 * @cfg {Object} an existing reader (eg. copied from another store)
13881 * @cfg {Array} data The multi-dimensional array of data
13883 * @param {Object} config
13885 Roo.data.SimpleStore = function(config)
13887 Roo.data.SimpleStore.superclass.constructor.call(this, {
13889 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13892 Roo.data.Record.create(config.fields)
13894 proxy : new Roo.data.MemoryProxy(config.data)
13898 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13900 * Ext JS Library 1.1.1
13901 * Copyright(c) 2006-2007, Ext JS, LLC.
13903 * Originally Released Under LGPL - original licence link has changed is not relivant.
13906 * <script type="text/javascript">
13911 * @extends Roo.data.Store
13912 * @class Roo.data.JsonStore
13913 * Small helper class to make creating Stores for JSON data easier. <br/>
13915 var store = new Roo.data.JsonStore({
13916 url: 'get-images.php',
13918 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13921 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13922 * JsonReader and HttpProxy (unless inline data is provided).</b>
13923 * @cfg {Array} fields An array of field definition objects, or field name strings.
13925 * @param {Object} config
13927 Roo.data.JsonStore = function(c){
13928 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13929 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13930 reader: new Roo.data.JsonReader(c, c.fields)
13933 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13935 * Ext JS Library 1.1.1
13936 * Copyright(c) 2006-2007, Ext JS, LLC.
13938 * Originally Released Under LGPL - original licence link has changed is not relivant.
13941 * <script type="text/javascript">
13945 Roo.data.Field = function(config){
13946 if(typeof config == "string"){
13947 config = {name: config};
13949 Roo.apply(this, config);
13952 this.type = "auto";
13955 var st = Roo.data.SortTypes;
13956 // named sortTypes are supported, here we look them up
13957 if(typeof this.sortType == "string"){
13958 this.sortType = st[this.sortType];
13961 // set default sortType for strings and dates
13962 if(!this.sortType){
13965 this.sortType = st.asUCString;
13968 this.sortType = st.asDate;
13971 this.sortType = st.none;
13976 var stripRe = /[\$,%]/g;
13978 // prebuilt conversion function for this field, instead of
13979 // switching every time we're reading a value
13981 var cv, dateFormat = this.dateFormat;
13986 cv = function(v){ return v; };
13989 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13993 return v !== undefined && v !== null && v !== '' ?
13994 parseInt(String(v).replace(stripRe, ""), 10) : '';
13999 return v !== undefined && v !== null && v !== '' ?
14000 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14005 cv = function(v){ return v === true || v === "true" || v == 1; };
14012 if(v instanceof Date){
14016 if(dateFormat == "timestamp"){
14017 return new Date(v*1000);
14019 return Date.parseDate(v, dateFormat);
14021 var parsed = Date.parse(v);
14022 return parsed ? new Date(parsed) : null;
14031 Roo.data.Field.prototype = {
14039 * Ext JS Library 1.1.1
14040 * Copyright(c) 2006-2007, Ext JS, LLC.
14042 * Originally Released Under LGPL - original licence link has changed is not relivant.
14045 * <script type="text/javascript">
14048 // Base class for reading structured data from a data source. This class is intended to be
14049 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14052 * @class Roo.data.DataReader
14053 * Base class for reading structured data from a data source. This class is intended to be
14054 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14057 Roo.data.DataReader = function(meta, recordType){
14061 this.recordType = recordType instanceof Array ?
14062 Roo.data.Record.create(recordType) : recordType;
14065 Roo.data.DataReader.prototype = {
14068 readerType : 'Data',
14070 * Create an empty record
14071 * @param {Object} data (optional) - overlay some values
14072 * @return {Roo.data.Record} record created.
14074 newRow : function(d) {
14076 this.recordType.prototype.fields.each(function(c) {
14078 case 'int' : da[c.name] = 0; break;
14079 case 'date' : da[c.name] = new Date(); break;
14080 case 'float' : da[c.name] = 0.0; break;
14081 case 'boolean' : da[c.name] = false; break;
14082 default : da[c.name] = ""; break;
14086 return new this.recordType(Roo.apply(da, d));
14092 * Ext JS Library 1.1.1
14093 * Copyright(c) 2006-2007, Ext JS, LLC.
14095 * Originally Released Under LGPL - original licence link has changed is not relivant.
14098 * <script type="text/javascript">
14102 * @class Roo.data.DataProxy
14103 * @extends Roo.data.Observable
14104 * This class is an abstract base class for implementations which provide retrieval of
14105 * unformatted data objects.<br>
14107 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14108 * (of the appropriate type which knows how to parse the data object) to provide a block of
14109 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14111 * Custom implementations must implement the load method as described in
14112 * {@link Roo.data.HttpProxy#load}.
14114 Roo.data.DataProxy = function(){
14117 * @event beforeload
14118 * Fires before a network request is made to retrieve a data object.
14119 * @param {Object} This DataProxy object.
14120 * @param {Object} params The params parameter to the load function.
14125 * Fires before the load method's callback is called.
14126 * @param {Object} This DataProxy object.
14127 * @param {Object} o The data object.
14128 * @param {Object} arg The callback argument object passed to the load function.
14132 * @event loadexception
14133 * Fires if an Exception occurs during data retrieval.
14134 * @param {Object} This DataProxy object.
14135 * @param {Object} o The data object.
14136 * @param {Object} arg The callback argument object passed to the load function.
14137 * @param {Object} e The Exception.
14139 loadexception : true
14141 Roo.data.DataProxy.superclass.constructor.call(this);
14144 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14147 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14151 * Ext JS Library 1.1.1
14152 * Copyright(c) 2006-2007, Ext JS, LLC.
14154 * Originally Released Under LGPL - original licence link has changed is not relivant.
14157 * <script type="text/javascript">
14160 * @class Roo.data.MemoryProxy
14161 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14162 * to the Reader when its load method is called.
14164 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14166 Roo.data.MemoryProxy = function(data){
14170 Roo.data.MemoryProxy.superclass.constructor.call(this);
14174 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14177 * Load data from the requested source (in this case an in-memory
14178 * data object passed to the constructor), read the data object into
14179 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14180 * process that block using the passed callback.
14181 * @param {Object} params This parameter is not used by the MemoryProxy class.
14182 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14183 * object into a block of Roo.data.Records.
14184 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14185 * The function must be passed <ul>
14186 * <li>The Record block object</li>
14187 * <li>The "arg" argument from the load function</li>
14188 * <li>A boolean success indicator</li>
14190 * @param {Object} scope The scope in which to call the callback
14191 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14193 load : function(params, reader, callback, scope, arg){
14194 params = params || {};
14197 result = reader.readRecords(params.data ? params.data :this.data);
14199 this.fireEvent("loadexception", this, arg, null, e);
14200 callback.call(scope, null, arg, false);
14203 callback.call(scope, result, arg, true);
14207 update : function(params, records){
14212 * Ext JS Library 1.1.1
14213 * Copyright(c) 2006-2007, Ext JS, LLC.
14215 * Originally Released Under LGPL - original licence link has changed is not relivant.
14218 * <script type="text/javascript">
14221 * @class Roo.data.HttpProxy
14222 * @extends Roo.data.DataProxy
14223 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14224 * configured to reference a certain URL.<br><br>
14226 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14227 * from which the running page was served.<br><br>
14229 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14231 * Be aware that to enable the browser to parse an XML document, the server must set
14232 * the Content-Type header in the HTTP response to "text/xml".
14234 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14235 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14236 * will be used to make the request.
14238 Roo.data.HttpProxy = function(conn){
14239 Roo.data.HttpProxy.superclass.constructor.call(this);
14240 // is conn a conn config or a real conn?
14242 this.useAjax = !conn || !conn.events;
14246 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14247 // thse are take from connection...
14250 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14253 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14254 * extra parameters to each request made by this object. (defaults to undefined)
14257 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14258 * to each request made by this object. (defaults to undefined)
14261 * @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)
14264 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14267 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14273 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14277 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14278 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14279 * a finer-grained basis than the DataProxy events.
14281 getConnection : function(){
14282 return this.useAjax ? Roo.Ajax : this.conn;
14286 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14287 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14288 * process that block using the passed callback.
14289 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14290 * for the request to the remote server.
14291 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14292 * object into a block of Roo.data.Records.
14293 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14294 * The function must be passed <ul>
14295 * <li>The Record block object</li>
14296 * <li>The "arg" argument from the load function</li>
14297 * <li>A boolean success indicator</li>
14299 * @param {Object} scope The scope in which to call the callback
14300 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14302 load : function(params, reader, callback, scope, arg){
14303 if(this.fireEvent("beforeload", this, params) !== false){
14305 params : params || {},
14307 callback : callback,
14312 callback : this.loadResponse,
14316 Roo.applyIf(o, this.conn);
14317 if(this.activeRequest){
14318 Roo.Ajax.abort(this.activeRequest);
14320 this.activeRequest = Roo.Ajax.request(o);
14322 this.conn.request(o);
14325 callback.call(scope||this, null, arg, false);
14330 loadResponse : function(o, success, response){
14331 delete this.activeRequest;
14333 this.fireEvent("loadexception", this, o, response);
14334 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14339 result = o.reader.read(response);
14341 this.fireEvent("loadexception", this, o, response, e);
14342 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14346 this.fireEvent("load", this, o, o.request.arg);
14347 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14351 update : function(dataSet){
14356 updateResponse : function(dataSet){
14361 * Ext JS Library 1.1.1
14362 * Copyright(c) 2006-2007, Ext JS, LLC.
14364 * Originally Released Under LGPL - original licence link has changed is not relivant.
14367 * <script type="text/javascript">
14371 * @class Roo.data.ScriptTagProxy
14372 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14373 * other than the originating domain of the running page.<br><br>
14375 * <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
14376 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14378 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14379 * source code that is used as the source inside a <script> tag.<br><br>
14381 * In order for the browser to process the returned data, the server must wrap the data object
14382 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14383 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14384 * depending on whether the callback name was passed:
14387 boolean scriptTag = false;
14388 String cb = request.getParameter("callback");
14391 response.setContentType("text/javascript");
14393 response.setContentType("application/x-json");
14395 Writer out = response.getWriter();
14397 out.write(cb + "(");
14399 out.print(dataBlock.toJsonString());
14406 * @param {Object} config A configuration object.
14408 Roo.data.ScriptTagProxy = function(config){
14409 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14410 Roo.apply(this, config);
14411 this.head = document.getElementsByTagName("head")[0];
14414 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14416 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14418 * @cfg {String} url The URL from which to request the data object.
14421 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14425 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14426 * the server the name of the callback function set up by the load call to process the returned data object.
14427 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14428 * javascript output which calls this named function passing the data object as its only parameter.
14430 callbackParam : "callback",
14432 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14433 * name to the request.
14438 * Load data from the configured URL, read the data object into
14439 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14440 * process that block using the passed callback.
14441 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14442 * for the request to the remote server.
14443 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14444 * object into a block of Roo.data.Records.
14445 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14446 * The function must be passed <ul>
14447 * <li>The Record block object</li>
14448 * <li>The "arg" argument from the load function</li>
14449 * <li>A boolean success indicator</li>
14451 * @param {Object} scope The scope in which to call the callback
14452 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14454 load : function(params, reader, callback, scope, arg){
14455 if(this.fireEvent("beforeload", this, params) !== false){
14457 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14459 var url = this.url;
14460 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14462 url += "&_dc=" + (new Date().getTime());
14464 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14467 cb : "stcCallback"+transId,
14468 scriptId : "stcScript"+transId,
14472 callback : callback,
14478 window[trans.cb] = function(o){
14479 conn.handleResponse(o, trans);
14482 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14484 if(this.autoAbort !== false){
14488 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14490 var script = document.createElement("script");
14491 script.setAttribute("src", url);
14492 script.setAttribute("type", "text/javascript");
14493 script.setAttribute("id", trans.scriptId);
14494 this.head.appendChild(script);
14496 this.trans = trans;
14498 callback.call(scope||this, null, arg, false);
14503 isLoading : function(){
14504 return this.trans ? true : false;
14508 * Abort the current server request.
14510 abort : function(){
14511 if(this.isLoading()){
14512 this.destroyTrans(this.trans);
14517 destroyTrans : function(trans, isLoaded){
14518 this.head.removeChild(document.getElementById(trans.scriptId));
14519 clearTimeout(trans.timeoutId);
14521 window[trans.cb] = undefined;
14523 delete window[trans.cb];
14526 // if hasn't been loaded, wait for load to remove it to prevent script error
14527 window[trans.cb] = function(){
14528 window[trans.cb] = undefined;
14530 delete window[trans.cb];
14537 handleResponse : function(o, trans){
14538 this.trans = false;
14539 this.destroyTrans(trans, true);
14542 result = trans.reader.readRecords(o);
14544 this.fireEvent("loadexception", this, o, trans.arg, e);
14545 trans.callback.call(trans.scope||window, null, trans.arg, false);
14548 this.fireEvent("load", this, o, trans.arg);
14549 trans.callback.call(trans.scope||window, result, trans.arg, true);
14553 handleFailure : function(trans){
14554 this.trans = false;
14555 this.destroyTrans(trans, false);
14556 this.fireEvent("loadexception", this, null, trans.arg);
14557 trans.callback.call(trans.scope||window, null, trans.arg, false);
14561 * Ext JS Library 1.1.1
14562 * Copyright(c) 2006-2007, Ext JS, LLC.
14564 * Originally Released Under LGPL - original licence link has changed is not relivant.
14567 * <script type="text/javascript">
14571 * @class Roo.data.JsonReader
14572 * @extends Roo.data.DataReader
14573 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14574 * based on mappings in a provided Roo.data.Record constructor.
14576 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14577 * in the reply previously.
14582 var RecordDef = Roo.data.Record.create([
14583 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14584 {name: 'occupation'} // This field will use "occupation" as the mapping.
14586 var myReader = new Roo.data.JsonReader({
14587 totalProperty: "results", // The property which contains the total dataset size (optional)
14588 root: "rows", // The property which contains an Array of row objects
14589 id: "id" // The property within each row object that provides an ID for the record (optional)
14593 * This would consume a JSON file like this:
14595 { 'results': 2, 'rows': [
14596 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14597 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14600 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14601 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14602 * paged from the remote server.
14603 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14604 * @cfg {String} root name of the property which contains the Array of row objects.
14605 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14606 * @cfg {Array} fields Array of field definition objects
14608 * Create a new JsonReader
14609 * @param {Object} meta Metadata configuration options
14610 * @param {Object} recordType Either an Array of field definition objects,
14611 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14613 Roo.data.JsonReader = function(meta, recordType){
14616 // set some defaults:
14617 Roo.applyIf(meta, {
14618 totalProperty: 'total',
14619 successProperty : 'success',
14624 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14626 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14628 readerType : 'Json',
14631 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14632 * Used by Store query builder to append _requestMeta to params.
14635 metaFromRemote : false,
14637 * This method is only used by a DataProxy which has retrieved data from a remote server.
14638 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14639 * @return {Object} data A data block which is used by an Roo.data.Store object as
14640 * a cache of Roo.data.Records.
14642 read : function(response){
14643 var json = response.responseText;
14645 var o = /* eval:var:o */ eval("("+json+")");
14647 throw {message: "JsonReader.read: Json object not found"};
14653 this.metaFromRemote = true;
14654 this.meta = o.metaData;
14655 this.recordType = Roo.data.Record.create(o.metaData.fields);
14656 this.onMetaChange(this.meta, this.recordType, o);
14658 return this.readRecords(o);
14661 // private function a store will implement
14662 onMetaChange : function(meta, recordType, o){
14669 simpleAccess: function(obj, subsc) {
14676 getJsonAccessor: function(){
14678 return function(expr) {
14680 return(re.test(expr))
14681 ? new Function("obj", "return obj." + expr)
14686 return Roo.emptyFn;
14691 * Create a data block containing Roo.data.Records from an XML document.
14692 * @param {Object} o An object which contains an Array of row objects in the property specified
14693 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14694 * which contains the total size of the dataset.
14695 * @return {Object} data A data block which is used by an Roo.data.Store object as
14696 * a cache of Roo.data.Records.
14698 readRecords : function(o){
14700 * After any data loads, the raw JSON data is available for further custom processing.
14704 var s = this.meta, Record = this.recordType,
14705 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14707 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14709 if(s.totalProperty) {
14710 this.getTotal = this.getJsonAccessor(s.totalProperty);
14712 if(s.successProperty) {
14713 this.getSuccess = this.getJsonAccessor(s.successProperty);
14715 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14717 var g = this.getJsonAccessor(s.id);
14718 this.getId = function(rec) {
14720 return (r === undefined || r === "") ? null : r;
14723 this.getId = function(){return null;};
14726 for(var jj = 0; jj < fl; jj++){
14728 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14729 this.ef[jj] = this.getJsonAccessor(map);
14733 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14734 if(s.totalProperty){
14735 var vt = parseInt(this.getTotal(o), 10);
14740 if(s.successProperty){
14741 var vs = this.getSuccess(o);
14742 if(vs === false || vs === 'false'){
14747 for(var i = 0; i < c; i++){
14750 var id = this.getId(n);
14751 for(var j = 0; j < fl; j++){
14753 var v = this.ef[j](n);
14755 Roo.log('missing convert for ' + f.name);
14759 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14761 var record = new Record(values, id);
14763 records[i] = record;
14769 totalRecords : totalRecords
14772 // used when loading children.. @see loadDataFromChildren
14773 toLoadData: function(rec)
14775 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14776 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14777 return { data : data, total : data.length };
14782 * Ext JS Library 1.1.1
14783 * Copyright(c) 2006-2007, Ext JS, LLC.
14785 * Originally Released Under LGPL - original licence link has changed is not relivant.
14788 * <script type="text/javascript">
14792 * @class Roo.data.ArrayReader
14793 * @extends Roo.data.DataReader
14794 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14795 * Each element of that Array represents a row of data fields. The
14796 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14797 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14801 var RecordDef = Roo.data.Record.create([
14802 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14803 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14805 var myReader = new Roo.data.ArrayReader({
14806 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14810 * This would consume an Array like this:
14812 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14816 * Create a new JsonReader
14817 * @param {Object} meta Metadata configuration options.
14818 * @param {Object|Array} recordType Either an Array of field definition objects
14820 * @cfg {Array} fields Array of field definition objects
14821 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14822 * as specified to {@link Roo.data.Record#create},
14823 * or an {@link Roo.data.Record} object
14826 * created using {@link Roo.data.Record#create}.
14828 Roo.data.ArrayReader = function(meta, recordType)
14830 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14833 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14836 * Create a data block containing Roo.data.Records from an XML document.
14837 * @param {Object} o An Array of row objects which represents the dataset.
14838 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14839 * a cache of Roo.data.Records.
14841 readRecords : function(o)
14843 var sid = this.meta ? this.meta.id : null;
14844 var recordType = this.recordType, fields = recordType.prototype.fields;
14847 for(var i = 0; i < root.length; i++){
14850 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14851 for(var j = 0, jlen = fields.length; j < jlen; j++){
14852 var f = fields.items[j];
14853 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14854 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14856 values[f.name] = v;
14858 var record = new recordType(values, id);
14860 records[records.length] = record;
14864 totalRecords : records.length
14867 // used when loading children.. @see loadDataFromChildren
14868 toLoadData: function(rec)
14870 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14871 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14882 * @class Roo.bootstrap.ComboBox
14883 * @extends Roo.bootstrap.TriggerField
14884 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14885 * @cfg {Boolean} append (true|false) default false
14886 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14887 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14888 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14889 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14890 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14891 * @cfg {Boolean} animate default true
14892 * @cfg {Boolean} emptyResultText only for touch device
14893 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14894 * @cfg {String} emptyTitle default ''
14895 * @cfg {Number} width fixed with? experimental
14897 * Create a new ComboBox.
14898 * @param {Object} config Configuration options
14900 Roo.bootstrap.ComboBox = function(config){
14901 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14905 * Fires when the dropdown list is expanded
14906 * @param {Roo.bootstrap.ComboBox} combo This combo box
14911 * Fires when the dropdown list is collapsed
14912 * @param {Roo.bootstrap.ComboBox} combo This combo box
14916 * @event beforeselect
14917 * Fires before a list item is selected. Return false to cancel the selection.
14918 * @param {Roo.bootstrap.ComboBox} combo This combo box
14919 * @param {Roo.data.Record} record The data record returned from the underlying store
14920 * @param {Number} index The index of the selected item in the dropdown list
14922 'beforeselect' : true,
14925 * Fires when a list item is selected
14926 * @param {Roo.bootstrap.ComboBox} combo This combo box
14927 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14928 * @param {Number} index The index of the selected item in the dropdown list
14932 * @event beforequery
14933 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14934 * The event object passed has these properties:
14935 * @param {Roo.bootstrap.ComboBox} combo This combo box
14936 * @param {String} query The query
14937 * @param {Boolean} forceAll true to force "all" query
14938 * @param {Boolean} cancel true to cancel the query
14939 * @param {Object} e The query event object
14941 'beforequery': true,
14944 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14945 * @param {Roo.bootstrap.ComboBox} combo This combo box
14950 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14951 * @param {Roo.bootstrap.ComboBox} combo This combo box
14952 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14957 * Fires when the remove value from the combobox array
14958 * @param {Roo.bootstrap.ComboBox} combo This combo box
14962 * @event afterremove
14963 * Fires when the remove value from the combobox array
14964 * @param {Roo.bootstrap.ComboBox} combo This combo box
14966 'afterremove' : true,
14968 * @event specialfilter
14969 * Fires when specialfilter
14970 * @param {Roo.bootstrap.ComboBox} combo This combo box
14972 'specialfilter' : true,
14975 * Fires when tick the element
14976 * @param {Roo.bootstrap.ComboBox} combo This combo box
14980 * @event touchviewdisplay
14981 * Fires when touch view require special display (default is using displayField)
14982 * @param {Roo.bootstrap.ComboBox} combo This combo box
14983 * @param {Object} cfg set html .
14985 'touchviewdisplay' : true
14990 this.tickItems = [];
14992 this.selectedIndex = -1;
14993 if(this.mode == 'local'){
14994 if(config.queryDelay === undefined){
14995 this.queryDelay = 10;
14997 if(config.minChars === undefined){
15003 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15006 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15007 * rendering into an Roo.Editor, defaults to false)
15010 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15011 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15014 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15017 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15018 * the dropdown list (defaults to undefined, with no header element)
15022 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15026 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15028 listWidth: undefined,
15030 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15031 * mode = 'remote' or 'text' if mode = 'local')
15033 displayField: undefined,
15036 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15037 * mode = 'remote' or 'value' if mode = 'local').
15038 * Note: use of a valueField requires the user make a selection
15039 * in order for a value to be mapped.
15041 valueField: undefined,
15043 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15048 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15049 * field's data value (defaults to the underlying DOM element's name)
15051 hiddenName: undefined,
15053 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15057 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15059 selectedClass: 'active',
15062 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15066 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15067 * anchor positions (defaults to 'tl-bl')
15069 listAlign: 'tl-bl?',
15071 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15075 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15076 * query specified by the allQuery config option (defaults to 'query')
15078 triggerAction: 'query',
15080 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15081 * (defaults to 4, does not apply if editable = false)
15085 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15086 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15090 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15091 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15095 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15096 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15100 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15101 * when editable = true (defaults to false)
15103 selectOnFocus:false,
15105 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15107 queryParam: 'query',
15109 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15110 * when mode = 'remote' (defaults to 'Loading...')
15112 loadingText: 'Loading...',
15114 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15118 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15122 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15123 * traditional select (defaults to true)
15127 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15131 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15135 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15136 * listWidth has a higher value)
15140 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15141 * allow the user to set arbitrary text into the field (defaults to false)
15143 forceSelection:false,
15145 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15146 * if typeAhead = true (defaults to 250)
15148 typeAheadDelay : 250,
15150 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15151 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15153 valueNotFoundText : undefined,
15155 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15157 blockFocus : false,
15160 * @cfg {Boolean} disableClear Disable showing of clear button.
15162 disableClear : false,
15164 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15166 alwaysQuery : false,
15169 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15174 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15176 invalidClass : "has-warning",
15179 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15181 validClass : "has-success",
15184 * @cfg {Boolean} specialFilter (true|false) special filter default false
15186 specialFilter : false,
15189 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15191 mobileTouchView : true,
15194 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15196 useNativeIOS : false,
15199 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15201 mobile_restrict_height : false,
15203 ios_options : false,
15215 btnPosition : 'right',
15216 triggerList : true,
15217 showToggleBtn : true,
15219 emptyResultText: 'Empty',
15220 triggerText : 'Select',
15224 // element that contains real text value.. (when hidden is used..)
15226 getAutoCreate : function()
15231 * Render classic select for iso
15234 if(Roo.isIOS && this.useNativeIOS){
15235 cfg = this.getAutoCreateNativeIOS();
15243 if(Roo.isTouch && this.mobileTouchView){
15244 cfg = this.getAutoCreateTouchView();
15251 if(!this.tickable){
15252 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15257 * ComboBox with tickable selections
15260 var align = this.labelAlign || this.parentLabelAlign();
15263 cls : 'form-group roo-combobox-tickable' //input-group
15266 var btn_text_select = '';
15267 var btn_text_done = '';
15268 var btn_text_cancel = '';
15270 if (this.btn_text_show) {
15271 btn_text_select = 'Select';
15272 btn_text_done = 'Done';
15273 btn_text_cancel = 'Cancel';
15278 cls : 'tickable-buttons',
15283 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15284 //html : this.triggerText
15285 html: btn_text_select
15291 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15293 html: btn_text_done
15299 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15301 html: btn_text_cancel
15307 buttons.cn.unshift({
15309 cls: 'roo-select2-search-field-input'
15315 Roo.each(buttons.cn, function(c){
15317 c.cls += ' btn-' + _this.size;
15320 if (_this.disabled) {
15327 style : 'display: contents',
15332 cls: 'form-hidden-field'
15336 cls: 'roo-select2-choices',
15340 cls: 'roo-select2-search-field',
15351 cls: 'roo-select2-container input-group roo-select2-container-multi',
15357 // cls: 'typeahead typeahead-long dropdown-menu',
15358 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15363 if(this.hasFeedback && !this.allowBlank){
15367 cls: 'glyphicon form-control-feedback'
15370 combobox.cn.push(feedback);
15377 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15378 tooltip : 'This field is required'
15380 if (Roo.bootstrap.version == 4) {
15383 style : 'display:none'
15386 if (align ==='left' && this.fieldLabel.length) {
15388 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15395 cls : 'control-label col-form-label',
15396 html : this.fieldLabel
15408 var labelCfg = cfg.cn[1];
15409 var contentCfg = cfg.cn[2];
15412 if(this.indicatorpos == 'right'){
15418 cls : 'control-label col-form-label',
15422 html : this.fieldLabel
15438 labelCfg = cfg.cn[0];
15439 contentCfg = cfg.cn[1];
15443 if(this.labelWidth > 12){
15444 labelCfg.style = "width: " + this.labelWidth + 'px';
15446 if(this.width * 1 > 0){
15447 contentCfg.style = "width: " + this.width + 'px';
15449 if(this.labelWidth < 13 && this.labelmd == 0){
15450 this.labelmd = this.labelWidth;
15453 if(this.labellg > 0){
15454 labelCfg.cls += ' col-lg-' + this.labellg;
15455 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15458 if(this.labelmd > 0){
15459 labelCfg.cls += ' col-md-' + this.labelmd;
15460 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15463 if(this.labelsm > 0){
15464 labelCfg.cls += ' col-sm-' + this.labelsm;
15465 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15468 if(this.labelxs > 0){
15469 labelCfg.cls += ' col-xs-' + this.labelxs;
15470 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15474 } else if ( this.fieldLabel.length) {
15475 // Roo.log(" label");
15480 //cls : 'input-group-addon',
15481 html : this.fieldLabel
15486 if(this.indicatorpos == 'right'){
15490 //cls : 'input-group-addon',
15491 html : this.fieldLabel
15501 // Roo.log(" no label && no align");
15508 ['xs','sm','md','lg'].map(function(size){
15509 if (settings[size]) {
15510 cfg.cls += ' col-' + size + '-' + settings[size];
15518 _initEventsCalled : false,
15521 initEvents: function()
15523 if (this._initEventsCalled) { // as we call render... prevent looping...
15526 this._initEventsCalled = true;
15529 throw "can not find store for combo";
15532 this.indicator = this.indicatorEl();
15534 this.store = Roo.factory(this.store, Roo.data);
15535 this.store.parent = this;
15537 // if we are building from html. then this element is so complex, that we can not really
15538 // use the rendered HTML.
15539 // so we have to trash and replace the previous code.
15540 if (Roo.XComponent.build_from_html) {
15541 // remove this element....
15542 var e = this.el.dom, k=0;
15543 while (e ) { e = e.previousSibling; ++k;}
15548 this.rendered = false;
15550 this.render(this.parent().getChildContainer(true), k);
15553 if(Roo.isIOS && this.useNativeIOS){
15554 this.initIOSView();
15562 if(Roo.isTouch && this.mobileTouchView){
15563 this.initTouchView();
15568 this.initTickableEvents();
15572 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15574 if(this.hiddenName){
15576 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15578 this.hiddenField.dom.value =
15579 this.hiddenValue !== undefined ? this.hiddenValue :
15580 this.value !== undefined ? this.value : '';
15582 // prevent input submission
15583 this.el.dom.removeAttribute('name');
15584 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15589 // this.el.dom.setAttribute('autocomplete', 'off');
15592 var cls = 'x-combo-list';
15594 //this.list = new Roo.Layer({
15595 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15601 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15602 _this.list.setWidth(lw);
15605 this.list.on('mouseover', this.onViewOver, this);
15606 this.list.on('mousemove', this.onViewMove, this);
15607 this.list.on('scroll', this.onViewScroll, this);
15610 this.list.swallowEvent('mousewheel');
15611 this.assetHeight = 0;
15614 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15615 this.assetHeight += this.header.getHeight();
15618 this.innerList = this.list.createChild({cls:cls+'-inner'});
15619 this.innerList.on('mouseover', this.onViewOver, this);
15620 this.innerList.on('mousemove', this.onViewMove, this);
15621 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15623 if(this.allowBlank && !this.pageSize && !this.disableClear){
15624 this.footer = this.list.createChild({cls:cls+'-ft'});
15625 this.pageTb = new Roo.Toolbar(this.footer);
15629 this.footer = this.list.createChild({cls:cls+'-ft'});
15630 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15631 {pageSize: this.pageSize});
15635 if (this.pageTb && this.allowBlank && !this.disableClear) {
15637 this.pageTb.add(new Roo.Toolbar.Fill(), {
15638 cls: 'x-btn-icon x-btn-clear',
15640 handler: function()
15643 _this.clearValue();
15644 _this.onSelect(false, -1);
15649 this.assetHeight += this.footer.getHeight();
15654 this.tpl = Roo.bootstrap.version == 4 ?
15655 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15656 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15659 this.view = new Roo.View(this.list, this.tpl, {
15660 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15662 //this.view.wrapEl.setDisplayed(false);
15663 this.view.on('click', this.onViewClick, this);
15666 this.store.on('beforeload', this.onBeforeLoad, this);
15667 this.store.on('load', this.onLoad, this);
15668 this.store.on('loadexception', this.onLoadException, this);
15670 if(this.resizable){
15671 this.resizer = new Roo.Resizable(this.list, {
15672 pinned:true, handles:'se'
15674 this.resizer.on('resize', function(r, w, h){
15675 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15676 this.listWidth = w;
15677 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15678 this.restrictHeight();
15680 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15683 if(!this.editable){
15684 this.editable = true;
15685 this.setEditable(false);
15690 if (typeof(this.events.add.listeners) != 'undefined') {
15692 this.addicon = this.wrap.createChild(
15693 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15695 this.addicon.on('click', function(e) {
15696 this.fireEvent('add', this);
15699 if (typeof(this.events.edit.listeners) != 'undefined') {
15701 this.editicon = this.wrap.createChild(
15702 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15703 if (this.addicon) {
15704 this.editicon.setStyle('margin-left', '40px');
15706 this.editicon.on('click', function(e) {
15708 // we fire even if inothing is selected..
15709 this.fireEvent('edit', this, this.lastData );
15715 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15716 "up" : function(e){
15717 this.inKeyMode = true;
15721 "down" : function(e){
15722 if(!this.isExpanded()){
15723 this.onTriggerClick();
15725 this.inKeyMode = true;
15730 "enter" : function(e){
15731 // this.onViewClick();
15735 if(this.fireEvent("specialkey", this, e)){
15736 this.onViewClick(false);
15742 "esc" : function(e){
15746 "tab" : function(e){
15749 if(this.fireEvent("specialkey", this, e)){
15750 this.onViewClick(false);
15758 doRelay : function(foo, bar, hname){
15759 if(hname == 'down' || this.scope.isExpanded()){
15760 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769 this.queryDelay = Math.max(this.queryDelay || 10,
15770 this.mode == 'local' ? 10 : 250);
15773 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15775 if(this.typeAhead){
15776 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15778 if(this.editable !== false){
15779 this.inputEl().on("keyup", this.onKeyUp, this);
15781 if(this.forceSelection){
15782 this.inputEl().on('blur', this.doForce, this);
15786 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15787 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15791 initTickableEvents: function()
15795 if(this.hiddenName){
15797 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15799 this.hiddenField.dom.value =
15800 this.hiddenValue !== undefined ? this.hiddenValue :
15801 this.value !== undefined ? this.value : '';
15803 // prevent input submission
15804 this.el.dom.removeAttribute('name');
15805 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15810 // this.list = this.el.select('ul.dropdown-menu',true).first();
15812 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15813 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15814 if(this.triggerList){
15815 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15818 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15819 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15821 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15822 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15824 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15825 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15827 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15828 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15829 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15832 this.cancelBtn.hide();
15837 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15838 _this.list.setWidth(lw);
15841 this.list.on('mouseover', this.onViewOver, this);
15842 this.list.on('mousemove', this.onViewMove, this);
15844 this.list.on('scroll', this.onViewScroll, this);
15847 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15848 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15851 this.view = new Roo.View(this.list, this.tpl, {
15856 selectedClass: this.selectedClass
15859 //this.view.wrapEl.setDisplayed(false);
15860 this.view.on('click', this.onViewClick, this);
15864 this.store.on('beforeload', this.onBeforeLoad, this);
15865 this.store.on('load', this.onLoad, this);
15866 this.store.on('loadexception', this.onLoadException, this);
15869 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15870 "up" : function(e){
15871 this.inKeyMode = true;
15875 "down" : function(e){
15876 this.inKeyMode = true;
15880 "enter" : function(e){
15881 if(this.fireEvent("specialkey", this, e)){
15882 this.onViewClick(false);
15888 "esc" : function(e){
15889 this.onTickableFooterButtonClick(e, false, false);
15892 "tab" : function(e){
15893 this.fireEvent("specialkey", this, e);
15895 this.onTickableFooterButtonClick(e, false, false);
15902 doRelay : function(e, fn, key){
15903 if(this.scope.isExpanded()){
15904 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913 this.queryDelay = Math.max(this.queryDelay || 10,
15914 this.mode == 'local' ? 10 : 250);
15917 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15919 if(this.typeAhead){
15920 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15923 if(this.editable !== false){
15924 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15927 this.indicator = this.indicatorEl();
15929 if(this.indicator){
15930 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15931 this.indicator.hide();
15936 onDestroy : function(){
15938 this.view.setStore(null);
15939 this.view.el.removeAllListeners();
15940 this.view.el.remove();
15941 this.view.purgeListeners();
15944 this.list.dom.innerHTML = '';
15948 this.store.un('beforeload', this.onBeforeLoad, this);
15949 this.store.un('load', this.onLoad, this);
15950 this.store.un('loadexception', this.onLoadException, this);
15952 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15956 fireKey : function(e){
15957 if(e.isNavKeyPress() && !this.list.isVisible()){
15958 this.fireEvent("specialkey", this, e);
15963 onResize: function(w, h)
15967 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15969 // if(typeof w != 'number'){
15970 // // we do not handle it!?!?
15973 // var tw = this.trigger.getWidth();
15974 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15975 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15977 // this.inputEl().setWidth( this.adjustWidth('input', x));
15979 // //this.trigger.setStyle('left', x+'px');
15981 // if(this.list && this.listWidth === undefined){
15982 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15983 // this.list.setWidth(lw);
15984 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15992 * Allow or prevent the user from directly editing the field text. If false is passed,
15993 * the user will only be able to select from the items defined in the dropdown list. This method
15994 * is the runtime equivalent of setting the 'editable' config option at config time.
15995 * @param {Boolean} value True to allow the user to directly edit the field text
15997 setEditable : function(value){
15998 if(value == this.editable){
16001 this.editable = value;
16003 this.inputEl().dom.setAttribute('readOnly', true);
16004 this.inputEl().on('mousedown', this.onTriggerClick, this);
16005 this.inputEl().addClass('x-combo-noedit');
16007 this.inputEl().dom.setAttribute('readOnly', false);
16008 this.inputEl().un('mousedown', this.onTriggerClick, this);
16009 this.inputEl().removeClass('x-combo-noedit');
16015 onBeforeLoad : function(combo,opts){
16016 if(!this.hasFocus){
16020 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16022 this.restrictHeight();
16023 this.selectedIndex = -1;
16027 onLoad : function(){
16029 this.hasQuery = false;
16031 if(!this.hasFocus){
16035 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16036 this.loading.hide();
16039 if(this.store.getCount() > 0){
16042 this.restrictHeight();
16043 if(this.lastQuery == this.allQuery){
16044 if(this.editable && !this.tickable){
16045 this.inputEl().dom.select();
16049 !this.selectByValue(this.value, true) &&
16052 !this.store.lastOptions ||
16053 typeof(this.store.lastOptions.add) == 'undefined' ||
16054 this.store.lastOptions.add != true
16057 this.select(0, true);
16060 if(this.autoFocus){
16063 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16064 this.taTask.delay(this.typeAheadDelay);
16068 this.onEmptyResults();
16074 onLoadException : function()
16076 this.hasQuery = false;
16078 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16079 this.loading.hide();
16082 if(this.tickable && this.editable){
16087 // only causes errors at present
16088 //Roo.log(this.store.reader.jsonData);
16089 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16091 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16097 onTypeAhead : function(){
16098 if(this.store.getCount() > 0){
16099 var r = this.store.getAt(0);
16100 var newValue = r.data[this.displayField];
16101 var len = newValue.length;
16102 var selStart = this.getRawValue().length;
16104 if(selStart != len){
16105 this.setRawValue(newValue);
16106 this.selectText(selStart, newValue.length);
16112 onSelect : function(record, index){
16114 if(this.fireEvent('beforeselect', this, record, index) !== false){
16116 this.setFromData(index > -1 ? record.data : false);
16119 this.fireEvent('select', this, record, index);
16124 * Returns the currently selected field value or empty string if no value is set.
16125 * @return {String} value The selected value
16127 getValue : function()
16129 if(Roo.isIOS && this.useNativeIOS){
16130 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16134 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16137 if(this.valueField){
16138 return typeof this.value != 'undefined' ? this.value : '';
16140 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16144 getRawValue : function()
16146 if(Roo.isIOS && this.useNativeIOS){
16147 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16150 var v = this.inputEl().getValue();
16156 * Clears any text/value currently set in the field
16158 clearValue : function(){
16160 if(this.hiddenField){
16161 this.hiddenField.dom.value = '';
16164 this.setRawValue('');
16165 this.lastSelectionText = '';
16166 this.lastData = false;
16168 var close = this.closeTriggerEl();
16179 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16180 * will be displayed in the field. If the value does not match the data value of an existing item,
16181 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16182 * Otherwise the field will be blank (although the value will still be set).
16183 * @param {String} value The value to match
16185 setValue : function(v)
16187 if(Roo.isIOS && this.useNativeIOS){
16188 this.setIOSValue(v);
16198 if(this.valueField){
16199 var r = this.findRecord(this.valueField, v);
16201 text = r.data[this.displayField];
16202 }else if(this.valueNotFoundText !== undefined){
16203 text = this.valueNotFoundText;
16206 this.lastSelectionText = text;
16207 if(this.hiddenField){
16208 this.hiddenField.dom.value = v;
16210 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16213 var close = this.closeTriggerEl();
16216 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16222 * @property {Object} the last set data for the element
16227 * Sets the value of the field based on a object which is related to the record format for the store.
16228 * @param {Object} value the value to set as. or false on reset?
16230 setFromData : function(o){
16237 var dv = ''; // display value
16238 var vv = ''; // value value..
16240 if (this.displayField) {
16241 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16243 // this is an error condition!!!
16244 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16247 if(this.valueField){
16248 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16251 var close = this.closeTriggerEl();
16254 if(dv.length || vv * 1 > 0){
16256 this.blockFocus=true;
16262 if(this.hiddenField){
16263 this.hiddenField.dom.value = vv;
16265 this.lastSelectionText = dv;
16266 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16270 // no hidden field.. - we store the value in 'value', but still display
16271 // display field!!!!
16272 this.lastSelectionText = dv;
16273 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280 reset : function(){
16281 // overridden so that last data is reset..
16288 this.setValue(this.originalValue);
16289 //this.clearInvalid();
16290 this.lastData = false;
16292 this.view.clearSelections();
16298 findRecord : function(prop, value){
16300 if(this.store.getCount() > 0){
16301 this.store.each(function(r){
16302 if(r.data[prop] == value){
16312 getName: function()
16314 // returns hidden if it's set..
16315 if (!this.rendered) {return ''};
16316 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16320 onViewMove : function(e, t){
16321 this.inKeyMode = false;
16325 onViewOver : function(e, t){
16326 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16329 var item = this.view.findItemFromChild(t);
16332 var index = this.view.indexOf(item);
16333 this.select(index, false);
16338 onViewClick : function(view, doFocus, el, e)
16340 var index = this.view.getSelectedIndexes()[0];
16342 var r = this.store.getAt(index);
16346 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16353 Roo.each(this.tickItems, function(v,k){
16355 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16357 _this.tickItems.splice(k, 1);
16359 if(typeof(e) == 'undefined' && view == false){
16360 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16372 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16373 this.tickItems.push(r.data);
16376 if(typeof(e) == 'undefined' && view == false){
16377 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16384 this.onSelect(r, index);
16386 if(doFocus !== false && !this.blockFocus){
16387 this.inputEl().focus();
16392 restrictHeight : function(){
16393 //this.innerList.dom.style.height = '';
16394 //var inner = this.innerList.dom;
16395 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16396 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16397 //this.list.beginUpdate();
16398 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16399 this.list.alignTo(this.inputEl(), this.listAlign);
16400 this.list.alignTo(this.inputEl(), this.listAlign);
16401 //this.list.endUpdate();
16405 onEmptyResults : function(){
16407 if(this.tickable && this.editable){
16408 this.hasFocus = false;
16409 this.restrictHeight();
16417 * Returns true if the dropdown list is expanded, else false.
16419 isExpanded : function(){
16420 return this.list.isVisible();
16424 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16425 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16426 * @param {String} value The data value of the item to select
16427 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16428 * selected item if it is not currently in view (defaults to true)
16429 * @return {Boolean} True if the value matched an item in the list, else false
16431 selectByValue : function(v, scrollIntoView){
16432 if(v !== undefined && v !== null){
16433 var r = this.findRecord(this.valueField || this.displayField, v);
16435 this.select(this.store.indexOf(r), scrollIntoView);
16443 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16444 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16445 * @param {Number} index The zero-based index of the list item to select
16446 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16447 * selected item if it is not currently in view (defaults to true)
16449 select : function(index, scrollIntoView){
16450 this.selectedIndex = index;
16451 this.view.select(index);
16452 if(scrollIntoView !== false){
16453 var el = this.view.getNode(index);
16455 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16458 this.list.scrollChildIntoView(el, false);
16464 selectNext : function(){
16465 var ct = this.store.getCount();
16467 if(this.selectedIndex == -1){
16469 }else if(this.selectedIndex < ct-1){
16470 this.select(this.selectedIndex+1);
16476 selectPrev : function(){
16477 var ct = this.store.getCount();
16479 if(this.selectedIndex == -1){
16481 }else if(this.selectedIndex != 0){
16482 this.select(this.selectedIndex-1);
16488 onKeyUp : function(e){
16489 if(this.editable !== false && !e.isSpecialKey()){
16490 this.lastKey = e.getKey();
16491 this.dqTask.delay(this.queryDelay);
16496 validateBlur : function(){
16497 return !this.list || !this.list.isVisible();
16501 initQuery : function(){
16503 var v = this.getRawValue();
16505 if(this.tickable && this.editable){
16506 v = this.tickableInputEl().getValue();
16513 doForce : function(){
16514 if(this.inputEl().dom.value.length > 0){
16515 this.inputEl().dom.value =
16516 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16522 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16523 * query allowing the query action to be canceled if needed.
16524 * @param {String} query The SQL query to execute
16525 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16526 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16527 * saved in the current store (defaults to false)
16529 doQuery : function(q, forceAll){
16531 if(q === undefined || q === null){
16536 forceAll: forceAll,
16540 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16545 forceAll = qe.forceAll;
16546 if(forceAll === true || (q.length >= this.minChars)){
16548 this.hasQuery = true;
16550 if(this.lastQuery != q || this.alwaysQuery){
16551 this.lastQuery = q;
16552 if(this.mode == 'local'){
16553 this.selectedIndex = -1;
16555 this.store.clearFilter();
16558 if(this.specialFilter){
16559 this.fireEvent('specialfilter', this);
16564 this.store.filter(this.displayField, q);
16567 this.store.fireEvent("datachanged", this.store);
16574 this.store.baseParams[this.queryParam] = q;
16576 var options = {params : this.getParams(q)};
16579 options.add = true;
16580 options.params.start = this.page * this.pageSize;
16583 this.store.load(options);
16586 * this code will make the page width larger, at the beginning, the list not align correctly,
16587 * we should expand the list on onLoad
16588 * so command out it
16593 this.selectedIndex = -1;
16598 this.loadNext = false;
16602 getParams : function(q){
16604 //p[this.queryParam] = q;
16608 p.limit = this.pageSize;
16614 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16616 collapse : function(){
16617 if(!this.isExpanded()){
16623 this.hasFocus = false;
16627 this.cancelBtn.hide();
16628 this.trigger.show();
16631 this.tickableInputEl().dom.value = '';
16632 this.tickableInputEl().blur();
16637 Roo.get(document).un('mousedown', this.collapseIf, this);
16638 Roo.get(document).un('mousewheel', this.collapseIf, this);
16639 if (!this.editable) {
16640 Roo.get(document).un('keydown', this.listKeyPress, this);
16642 this.fireEvent('collapse', this);
16648 collapseIf : function(e){
16649 var in_combo = e.within(this.el);
16650 var in_list = e.within(this.list);
16651 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16653 if (in_combo || in_list || is_list) {
16654 //e.stopPropagation();
16659 this.onTickableFooterButtonClick(e, false, false);
16667 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16669 expand : function(){
16671 if(this.isExpanded() || !this.hasFocus){
16675 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16676 this.list.setWidth(lw);
16682 this.restrictHeight();
16686 this.tickItems = Roo.apply([], this.item);
16689 this.cancelBtn.show();
16690 this.trigger.hide();
16693 this.tickableInputEl().focus();
16698 Roo.get(document).on('mousedown', this.collapseIf, this);
16699 Roo.get(document).on('mousewheel', this.collapseIf, this);
16700 if (!this.editable) {
16701 Roo.get(document).on('keydown', this.listKeyPress, this);
16704 this.fireEvent('expand', this);
16708 // Implements the default empty TriggerField.onTriggerClick function
16709 onTriggerClick : function(e)
16711 Roo.log('trigger click');
16713 if(this.disabled || !this.triggerList){
16718 this.loadNext = false;
16720 if(this.isExpanded()){
16722 if (!this.blockFocus) {
16723 this.inputEl().focus();
16727 this.hasFocus = true;
16728 if(this.triggerAction == 'all') {
16729 this.doQuery(this.allQuery, true);
16731 this.doQuery(this.getRawValue());
16733 if (!this.blockFocus) {
16734 this.inputEl().focus();
16739 onTickableTriggerClick : function(e)
16746 this.loadNext = false;
16747 this.hasFocus = true;
16749 if(this.triggerAction == 'all') {
16750 this.doQuery(this.allQuery, true);
16752 this.doQuery(this.getRawValue());
16756 onSearchFieldClick : function(e)
16758 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16759 this.onTickableFooterButtonClick(e, false, false);
16763 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16768 this.loadNext = false;
16769 this.hasFocus = true;
16771 if(this.triggerAction == 'all') {
16772 this.doQuery(this.allQuery, true);
16774 this.doQuery(this.getRawValue());
16778 listKeyPress : function(e)
16780 //Roo.log('listkeypress');
16781 // scroll to first matching element based on key pres..
16782 if (e.isSpecialKey()) {
16785 var k = String.fromCharCode(e.getKey()).toUpperCase();
16788 var csel = this.view.getSelectedNodes();
16789 var cselitem = false;
16791 var ix = this.view.indexOf(csel[0]);
16792 cselitem = this.store.getAt(ix);
16793 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16799 this.store.each(function(v) {
16801 // start at existing selection.
16802 if (cselitem.id == v.id) {
16808 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16809 match = this.store.indexOf(v);
16815 if (match === false) {
16816 return true; // no more action?
16819 this.view.select(match);
16820 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16821 sn.scrollIntoView(sn.dom.parentNode, false);
16824 onViewScroll : function(e, t){
16826 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){
16830 this.hasQuery = true;
16832 this.loading = this.list.select('.loading', true).first();
16834 if(this.loading === null){
16835 this.list.createChild({
16837 cls: 'loading roo-select2-more-results roo-select2-active',
16838 html: 'Loading more results...'
16841 this.loading = this.list.select('.loading', true).first();
16843 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16845 this.loading.hide();
16848 this.loading.show();
16853 this.loadNext = true;
16855 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16860 addItem : function(o)
16862 var dv = ''; // display value
16864 if (this.displayField) {
16865 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16867 // this is an error condition!!!
16868 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16875 var choice = this.choices.createChild({
16877 cls: 'roo-select2-search-choice',
16886 cls: 'roo-select2-search-choice-close fa fa-times',
16891 }, this.searchField);
16893 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16895 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16903 this.inputEl().dom.value = '';
16908 onRemoveItem : function(e, _self, o)
16910 e.preventDefault();
16912 this.lastItem = Roo.apply([], this.item);
16914 var index = this.item.indexOf(o.data) * 1;
16917 Roo.log('not this item?!');
16921 this.item.splice(index, 1);
16926 this.fireEvent('remove', this, e);
16932 syncValue : function()
16934 if(!this.item.length){
16941 Roo.each(this.item, function(i){
16942 if(_this.valueField){
16943 value.push(i[_this.valueField]);
16950 this.value = value.join(',');
16952 if(this.hiddenField){
16953 this.hiddenField.dom.value = this.value;
16956 this.store.fireEvent("datachanged", this.store);
16961 clearItem : function()
16963 if(!this.multiple){
16969 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16977 if(this.tickable && !Roo.isTouch){
16978 this.view.refresh();
16982 inputEl: function ()
16984 if(Roo.isIOS && this.useNativeIOS){
16985 return this.el.select('select.roo-ios-select', true).first();
16988 if(Roo.isTouch && this.mobileTouchView){
16989 return this.el.select('input.form-control',true).first();
16993 return this.searchField;
16996 return this.el.select('input.form-control',true).first();
16999 onTickableFooterButtonClick : function(e, btn, el)
17001 e.preventDefault();
17003 this.lastItem = Roo.apply([], this.item);
17005 if(btn && btn.name == 'cancel'){
17006 this.tickItems = Roo.apply([], this.item);
17015 Roo.each(this.tickItems, function(o){
17023 validate : function()
17025 if(this.getVisibilityEl().hasClass('hidden')){
17029 var v = this.getRawValue();
17032 v = this.getValue();
17035 if(this.disabled || this.allowBlank || v.length){
17040 this.markInvalid();
17044 tickableInputEl : function()
17046 if(!this.tickable || !this.editable){
17047 return this.inputEl();
17050 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17054 getAutoCreateTouchView : function()
17059 cls: 'form-group' //input-group
17065 type : this.inputType,
17066 cls : 'form-control x-combo-noedit',
17067 autocomplete: 'new-password',
17068 placeholder : this.placeholder || '',
17073 input.name = this.name;
17077 input.cls += ' input-' + this.size;
17080 if (this.disabled) {
17081 input.disabled = true;
17085 cls : 'roo-combobox-wrap',
17092 inputblock.cls += ' input-group';
17094 inputblock.cn.unshift({
17096 cls : 'input-group-addon input-group-prepend input-group-text',
17101 if(this.removable && !this.multiple){
17102 inputblock.cls += ' roo-removable';
17104 inputblock.cn.push({
17107 cls : 'roo-combo-removable-btn close'
17111 if(this.hasFeedback && !this.allowBlank){
17113 inputblock.cls += ' has-feedback';
17115 inputblock.cn.push({
17117 cls: 'glyphicon form-control-feedback'
17124 inputblock.cls += (this.before) ? '' : ' input-group';
17126 inputblock.cn.push({
17128 cls : 'input-group-addon input-group-append input-group-text',
17134 var ibwrap = inputblock;
17139 cls: 'roo-select2-choices',
17143 cls: 'roo-select2-search-field',
17156 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17161 cls: 'form-hidden-field'
17167 if(!this.multiple && this.showToggleBtn){
17173 if (this.caret != false) {
17176 cls: 'fa fa-' + this.caret
17183 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17185 Roo.bootstrap.version == 3 ? caret : '',
17188 cls: 'combobox-clear',
17202 combobox.cls += ' roo-select2-container-multi';
17205 var align = this.labelAlign || this.parentLabelAlign();
17207 if (align ==='left' && this.fieldLabel.length) {
17212 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17213 tooltip : 'This field is required'
17217 cls : 'control-label col-form-label',
17218 html : this.fieldLabel
17222 cls : 'roo-combobox-wrap ',
17229 var labelCfg = cfg.cn[1];
17230 var contentCfg = cfg.cn[2];
17233 if(this.indicatorpos == 'right'){
17238 cls : 'control-label col-form-label',
17242 html : this.fieldLabel
17246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17247 tooltip : 'This field is required'
17252 cls : "roo-combobox-wrap ",
17260 labelCfg = cfg.cn[0];
17261 contentCfg = cfg.cn[1];
17266 if(this.labelWidth > 12){
17267 labelCfg.style = "width: " + this.labelWidth + 'px';
17270 if(this.labelWidth < 13 && this.labelmd == 0){
17271 this.labelmd = this.labelWidth;
17274 if(this.labellg > 0){
17275 labelCfg.cls += ' col-lg-' + this.labellg;
17276 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17279 if(this.labelmd > 0){
17280 labelCfg.cls += ' col-md-' + this.labelmd;
17281 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17284 if(this.labelsm > 0){
17285 labelCfg.cls += ' col-sm-' + this.labelsm;
17286 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17289 if(this.labelxs > 0){
17290 labelCfg.cls += ' col-xs-' + this.labelxs;
17291 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17295 } else if ( this.fieldLabel.length) {
17299 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17300 tooltip : 'This field is required'
17304 cls : 'control-label',
17305 html : this.fieldLabel
17316 if(this.indicatorpos == 'right'){
17320 cls : 'control-label',
17321 html : this.fieldLabel,
17325 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17326 tooltip : 'This field is required'
17343 var settings = this;
17345 ['xs','sm','md','lg'].map(function(size){
17346 if (settings[size]) {
17347 cfg.cls += ' col-' + size + '-' + settings[size];
17354 initTouchView : function()
17356 this.renderTouchView();
17358 this.touchViewEl.on('scroll', function(){
17359 this.el.dom.scrollTop = 0;
17362 this.originalValue = this.getValue();
17364 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17366 this.inputEl().on("click", this.showTouchView, this);
17367 if (this.triggerEl) {
17368 this.triggerEl.on("click", this.showTouchView, this);
17372 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17373 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17375 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17377 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17378 this.store.on('load', this.onTouchViewLoad, this);
17379 this.store.on('loadexception', this.onTouchViewLoadException, this);
17381 if(this.hiddenName){
17383 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17385 this.hiddenField.dom.value =
17386 this.hiddenValue !== undefined ? this.hiddenValue :
17387 this.value !== undefined ? this.value : '';
17389 this.el.dom.removeAttribute('name');
17390 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17394 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17395 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17398 if(this.removable && !this.multiple){
17399 var close = this.closeTriggerEl();
17401 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17402 close.on('click', this.removeBtnClick, this, close);
17406 * fix the bug in Safari iOS8
17408 this.inputEl().on("focus", function(e){
17409 document.activeElement.blur();
17412 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17419 renderTouchView : function()
17421 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17422 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17424 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17425 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17427 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17428 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17429 this.touchViewBodyEl.setStyle('overflow', 'auto');
17431 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17432 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17435 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439 showTouchView : function()
17445 this.touchViewHeaderEl.hide();
17447 if(this.modalTitle.length){
17448 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17449 this.touchViewHeaderEl.show();
17452 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17453 this.touchViewEl.show();
17455 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17457 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17458 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17460 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17462 if(this.modalTitle.length){
17463 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17466 this.touchViewBodyEl.setHeight(bodyHeight);
17470 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17472 this.touchViewEl.addClass(['in','show']);
17475 if(this._touchViewMask){
17476 Roo.get(document.body).addClass("x-body-masked");
17477 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17478 this._touchViewMask.setStyle('z-index', 10000);
17479 this._touchViewMask.addClass('show');
17482 this.doTouchViewQuery();
17486 hideTouchView : function()
17488 this.touchViewEl.removeClass(['in','show']);
17492 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17494 this.touchViewEl.setStyle('display', 'none');
17497 if(this._touchViewMask){
17498 this._touchViewMask.removeClass('show');
17499 Roo.get(document.body).removeClass("x-body-masked");
17503 setTouchViewValue : function()
17510 Roo.each(this.tickItems, function(o){
17515 this.hideTouchView();
17518 doTouchViewQuery : function()
17527 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17531 if(!this.alwaysQuery || this.mode == 'local'){
17532 this.onTouchViewLoad();
17539 onTouchViewBeforeLoad : function(combo,opts)
17545 onTouchViewLoad : function()
17547 if(this.store.getCount() < 1){
17548 this.onTouchViewEmptyResults();
17552 this.clearTouchView();
17554 var rawValue = this.getRawValue();
17556 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17558 this.tickItems = [];
17560 this.store.data.each(function(d, rowIndex){
17561 var row = this.touchViewListGroup.createChild(template);
17563 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17564 row.addClass(d.data.cls);
17567 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17570 html : d.data[this.displayField]
17573 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17574 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17577 row.removeClass('selected');
17578 if(!this.multiple && this.valueField &&
17579 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17582 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17583 row.addClass('selected');
17586 if(this.multiple && this.valueField &&
17587 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17591 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17592 this.tickItems.push(d.data);
17595 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17599 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17601 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17603 if(this.modalTitle.length){
17604 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17607 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17609 if(this.mobile_restrict_height && listHeight < bodyHeight){
17610 this.touchViewBodyEl.setHeight(listHeight);
17615 if(firstChecked && listHeight > bodyHeight){
17616 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17621 onTouchViewLoadException : function()
17623 this.hideTouchView();
17626 onTouchViewEmptyResults : function()
17628 this.clearTouchView();
17630 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17632 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17636 clearTouchView : function()
17638 this.touchViewListGroup.dom.innerHTML = '';
17641 onTouchViewClick : function(e, el, o)
17643 e.preventDefault();
17646 var rowIndex = o.rowIndex;
17648 var r = this.store.getAt(rowIndex);
17650 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17652 if(!this.multiple){
17653 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17654 c.dom.removeAttribute('checked');
17657 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17659 this.setFromData(r.data);
17661 var close = this.closeTriggerEl();
17667 this.hideTouchView();
17669 this.fireEvent('select', this, r, rowIndex);
17674 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17675 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17676 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17680 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17681 this.addItem(r.data);
17682 this.tickItems.push(r.data);
17686 getAutoCreateNativeIOS : function()
17689 cls: 'form-group' //input-group,
17694 cls : 'roo-ios-select'
17698 combobox.name = this.name;
17701 if (this.disabled) {
17702 combobox.disabled = true;
17705 var settings = this;
17707 ['xs','sm','md','lg'].map(function(size){
17708 if (settings[size]) {
17709 cfg.cls += ' col-' + size + '-' + settings[size];
17719 initIOSView : function()
17721 this.store.on('load', this.onIOSViewLoad, this);
17726 onIOSViewLoad : function()
17728 if(this.store.getCount() < 1){
17732 this.clearIOSView();
17734 if(this.allowBlank) {
17736 var default_text = '-- SELECT --';
17738 if(this.placeholder.length){
17739 default_text = this.placeholder;
17742 if(this.emptyTitle.length){
17743 default_text += ' - ' + this.emptyTitle + ' -';
17746 var opt = this.inputEl().createChild({
17749 html : default_text
17753 o[this.valueField] = 0;
17754 o[this.displayField] = default_text;
17756 this.ios_options.push({
17763 this.store.data.each(function(d, rowIndex){
17767 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17768 html = d.data[this.displayField];
17773 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17774 value = d.data[this.valueField];
17783 if(this.value == d.data[this.valueField]){
17784 option['selected'] = true;
17787 var opt = this.inputEl().createChild(option);
17789 this.ios_options.push({
17796 this.inputEl().on('change', function(){
17797 this.fireEvent('select', this);
17802 clearIOSView: function()
17804 this.inputEl().dom.innerHTML = '';
17806 this.ios_options = [];
17809 setIOSValue: function(v)
17813 if(!this.ios_options){
17817 Roo.each(this.ios_options, function(opts){
17819 opts.el.dom.removeAttribute('selected');
17821 if(opts.data[this.valueField] != v){
17825 opts.el.dom.setAttribute('selected', true);
17831 * @cfg {Boolean} grow
17835 * @cfg {Number} growMin
17839 * @cfg {Number} growMax
17848 Roo.apply(Roo.bootstrap.ComboBox, {
17852 cls: 'modal-header',
17874 cls: 'list-group-item',
17878 cls: 'roo-combobox-list-group-item-value'
17882 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896 listItemCheckbox : {
17898 cls: 'list-group-item',
17902 cls: 'roo-combobox-list-group-item-value'
17906 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17922 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17927 cls: 'modal-footer',
17935 cls: 'col-xs-6 text-left',
17938 cls: 'btn btn-danger roo-touch-view-cancel',
17944 cls: 'col-xs-6 text-right',
17947 cls: 'btn btn-success roo-touch-view-ok',
17958 Roo.apply(Roo.bootstrap.ComboBox, {
17960 touchViewTemplate : {
17962 cls: 'modal fade roo-combobox-touch-view',
17966 cls: 'modal-dialog',
17967 style : 'position:fixed', // we have to fix position....
17971 cls: 'modal-content',
17973 Roo.bootstrap.ComboBox.header,
17974 Roo.bootstrap.ComboBox.body,
17975 Roo.bootstrap.ComboBox.footer
17984 * Ext JS Library 1.1.1
17985 * Copyright(c) 2006-2007, Ext JS, LLC.
17987 * Originally Released Under LGPL - original licence link has changed is not relivant.
17990 * <script type="text/javascript">
17995 * @extends Roo.util.Observable
17996 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17997 * This class also supports single and multi selection modes. <br>
17998 * Create a data model bound view:
18000 var store = new Roo.data.Store(...);
18002 var view = new Roo.View({
18004 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18006 singleSelect: true,
18007 selectedClass: "ydataview-selected",
18011 // listen for node click?
18012 view.on("click", function(vw, index, node, e){
18013 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18017 dataModel.load("foobar.xml");
18019 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18021 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18022 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18024 * Note: old style constructor is still suported (container, template, config)
18027 * Create a new View
18028 * @param {Object} config The config object
18031 Roo.View = function(config, depreciated_tpl, depreciated_config){
18033 this.parent = false;
18035 if (typeof(depreciated_tpl) == 'undefined') {
18036 // new way.. - universal constructor.
18037 Roo.apply(this, config);
18038 this.el = Roo.get(this.el);
18041 this.el = Roo.get(config);
18042 this.tpl = depreciated_tpl;
18043 Roo.apply(this, depreciated_config);
18045 this.wrapEl = this.el.wrap().wrap();
18046 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18049 if(typeof(this.tpl) == "string"){
18050 this.tpl = new Roo.Template(this.tpl);
18052 // support xtype ctors..
18053 this.tpl = new Roo.factory(this.tpl, Roo);
18057 this.tpl.compile();
18062 * @event beforeclick
18063 * Fires before a click is processed. Returns false to cancel the default action.
18064 * @param {Roo.View} this
18065 * @param {Number} index The index of the target node
18066 * @param {HTMLElement} node The target node
18067 * @param {Roo.EventObject} e The raw event object
18069 "beforeclick" : true,
18072 * Fires when a template node is clicked.
18073 * @param {Roo.View} this
18074 * @param {Number} index The index of the target node
18075 * @param {HTMLElement} node The target node
18076 * @param {Roo.EventObject} e The raw event object
18081 * Fires when a template node is double clicked.
18082 * @param {Roo.View} this
18083 * @param {Number} index The index of the target node
18084 * @param {HTMLElement} node The target node
18085 * @param {Roo.EventObject} e The raw event object
18089 * @event contextmenu
18090 * Fires when a template node is right clicked.
18091 * @param {Roo.View} this
18092 * @param {Number} index The index of the target node
18093 * @param {HTMLElement} node The target node
18094 * @param {Roo.EventObject} e The raw event object
18096 "contextmenu" : true,
18098 * @event selectionchange
18099 * Fires when the selected nodes change.
18100 * @param {Roo.View} this
18101 * @param {Array} selections Array of the selected nodes
18103 "selectionchange" : true,
18106 * @event beforeselect
18107 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18108 * @param {Roo.View} this
18109 * @param {HTMLElement} node The node to be selected
18110 * @param {Array} selections Array of currently selected nodes
18112 "beforeselect" : true,
18114 * @event preparedata
18115 * Fires on every row to render, to allow you to change the data.
18116 * @param {Roo.View} this
18117 * @param {Object} data to be rendered (change this)
18119 "preparedata" : true
18127 "click": this.onClick,
18128 "dblclick": this.onDblClick,
18129 "contextmenu": this.onContextMenu,
18133 this.selections = [];
18135 this.cmp = new Roo.CompositeElementLite([]);
18137 this.store = Roo.factory(this.store, Roo.data);
18138 this.setStore(this.store, true);
18141 if ( this.footer && this.footer.xtype) {
18143 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18145 this.footer.dataSource = this.store;
18146 this.footer.container = fctr;
18147 this.footer = Roo.factory(this.footer, Roo);
18148 fctr.insertFirst(this.el);
18150 // this is a bit insane - as the paging toolbar seems to detach the el..
18151 // dom.parentNode.parentNode.parentNode
18152 // they get detached?
18156 Roo.View.superclass.constructor.call(this);
18161 Roo.extend(Roo.View, Roo.util.Observable, {
18164 * @cfg {Roo.data.Store} store Data store to load data from.
18169 * @cfg {String|Roo.Element} el The container element.
18174 * @cfg {String|Roo.Template} tpl The template used by this View
18178 * @cfg {String} dataName the named area of the template to use as the data area
18179 * Works with domtemplates roo-name="name"
18183 * @cfg {String} selectedClass The css class to add to selected nodes
18185 selectedClass : "x-view-selected",
18187 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18192 * @cfg {String} text to display on mask (default Loading)
18196 * @cfg {Boolean} multiSelect Allow multiple selection
18198 multiSelect : false,
18200 * @cfg {Boolean} singleSelect Allow single selection
18202 singleSelect: false,
18205 * @cfg {Boolean} toggleSelect - selecting
18207 toggleSelect : false,
18210 * @cfg {Boolean} tickable - selecting
18215 * Returns the element this view is bound to.
18216 * @return {Roo.Element}
18218 getEl : function(){
18219 return this.wrapEl;
18225 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18227 refresh : function(){
18228 //Roo.log('refresh');
18231 // if we are using something like 'domtemplate', then
18232 // the what gets used is:
18233 // t.applySubtemplate(NAME, data, wrapping data..)
18234 // the outer template then get' applied with
18235 // the store 'extra data'
18236 // and the body get's added to the
18237 // roo-name="data" node?
18238 // <span class='roo-tpl-{name}'></span> ?????
18242 this.clearSelections();
18243 this.el.update("");
18245 var records = this.store.getRange();
18246 if(records.length < 1) {
18248 // is this valid?? = should it render a template??
18250 this.el.update(this.emptyText);
18254 if (this.dataName) {
18255 this.el.update(t.apply(this.store.meta)); //????
18256 el = this.el.child('.roo-tpl-' + this.dataName);
18259 for(var i = 0, len = records.length; i < len; i++){
18260 var data = this.prepareData(records[i].data, i, records[i]);
18261 this.fireEvent("preparedata", this, data, i, records[i]);
18263 var d = Roo.apply({}, data);
18266 Roo.apply(d, {'roo-id' : Roo.id()});
18270 Roo.each(this.parent.item, function(item){
18271 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18274 Roo.apply(d, {'roo-data-checked' : 'checked'});
18278 html[html.length] = Roo.util.Format.trim(
18280 t.applySubtemplate(this.dataName, d, this.store.meta) :
18287 el.update(html.join(""));
18288 this.nodes = el.dom.childNodes;
18289 this.updateIndexes(0);
18294 * Function to override to reformat the data that is sent to
18295 * the template for each node.
18296 * DEPRICATED - use the preparedata event handler.
18297 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18298 * a JSON object for an UpdateManager bound view).
18300 prepareData : function(data, index, record)
18302 this.fireEvent("preparedata", this, data, index, record);
18306 onUpdate : function(ds, record){
18307 // Roo.log('on update');
18308 this.clearSelections();
18309 var index = this.store.indexOf(record);
18310 var n = this.nodes[index];
18311 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18312 n.parentNode.removeChild(n);
18313 this.updateIndexes(index, index);
18319 onAdd : function(ds, records, index)
18321 //Roo.log(['on Add', ds, records, index] );
18322 this.clearSelections();
18323 if(this.nodes.length == 0){
18327 var n = this.nodes[index];
18328 for(var i = 0, len = records.length; i < len; i++){
18329 var d = this.prepareData(records[i].data, i, records[i]);
18331 this.tpl.insertBefore(n, d);
18334 this.tpl.append(this.el, d);
18337 this.updateIndexes(index);
18340 onRemove : function(ds, record, index){
18341 // Roo.log('onRemove');
18342 this.clearSelections();
18343 var el = this.dataName ?
18344 this.el.child('.roo-tpl-' + this.dataName) :
18347 el.dom.removeChild(this.nodes[index]);
18348 this.updateIndexes(index);
18352 * Refresh an individual node.
18353 * @param {Number} index
18355 refreshNode : function(index){
18356 this.onUpdate(this.store, this.store.getAt(index));
18359 updateIndexes : function(startIndex, endIndex){
18360 var ns = this.nodes;
18361 startIndex = startIndex || 0;
18362 endIndex = endIndex || ns.length - 1;
18363 for(var i = startIndex; i <= endIndex; i++){
18364 ns[i].nodeIndex = i;
18369 * Changes the data store this view uses and refresh the view.
18370 * @param {Store} store
18372 setStore : function(store, initial){
18373 if(!initial && this.store){
18374 this.store.un("datachanged", this.refresh);
18375 this.store.un("add", this.onAdd);
18376 this.store.un("remove", this.onRemove);
18377 this.store.un("update", this.onUpdate);
18378 this.store.un("clear", this.refresh);
18379 this.store.un("beforeload", this.onBeforeLoad);
18380 this.store.un("load", this.onLoad);
18381 this.store.un("loadexception", this.onLoad);
18385 store.on("datachanged", this.refresh, this);
18386 store.on("add", this.onAdd, this);
18387 store.on("remove", this.onRemove, this);
18388 store.on("update", this.onUpdate, this);
18389 store.on("clear", this.refresh, this);
18390 store.on("beforeload", this.onBeforeLoad, this);
18391 store.on("load", this.onLoad, this);
18392 store.on("loadexception", this.onLoad, this);
18400 * onbeforeLoad - masks the loading area.
18403 onBeforeLoad : function(store,opts)
18405 //Roo.log('onBeforeLoad');
18407 this.el.update("");
18409 this.el.mask(this.mask ? this.mask : "Loading" );
18411 onLoad : function ()
18418 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18419 * @param {HTMLElement} node
18420 * @return {HTMLElement} The template node
18422 findItemFromChild : function(node){
18423 var el = this.dataName ?
18424 this.el.child('.roo-tpl-' + this.dataName,true) :
18427 if(!node || node.parentNode == el){
18430 var p = node.parentNode;
18431 while(p && p != el){
18432 if(p.parentNode == el){
18441 onClick : function(e){
18442 var item = this.findItemFromChild(e.getTarget());
18444 var index = this.indexOf(item);
18445 if(this.onItemClick(item, index, e) !== false){
18446 this.fireEvent("click", this, index, item, e);
18449 this.clearSelections();
18454 onContextMenu : function(e){
18455 var item = this.findItemFromChild(e.getTarget());
18457 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18462 onDblClick : function(e){
18463 var item = this.findItemFromChild(e.getTarget());
18465 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18469 onItemClick : function(item, index, e)
18471 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18474 if (this.toggleSelect) {
18475 var m = this.isSelected(item) ? 'unselect' : 'select';
18478 _t[m](item, true, false);
18481 if(this.multiSelect || this.singleSelect){
18482 if(this.multiSelect && e.shiftKey && this.lastSelection){
18483 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18485 this.select(item, this.multiSelect && e.ctrlKey);
18486 this.lastSelection = item;
18489 if(!this.tickable){
18490 e.preventDefault();
18498 * Get the number of selected nodes.
18501 getSelectionCount : function(){
18502 return this.selections.length;
18506 * Get the currently selected nodes.
18507 * @return {Array} An array of HTMLElements
18509 getSelectedNodes : function(){
18510 return this.selections;
18514 * Get the indexes of the selected nodes.
18517 getSelectedIndexes : function(){
18518 var indexes = [], s = this.selections;
18519 for(var i = 0, len = s.length; i < len; i++){
18520 indexes.push(s[i].nodeIndex);
18526 * Clear all selections
18527 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18529 clearSelections : function(suppressEvent){
18530 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18531 this.cmp.elements = this.selections;
18532 this.cmp.removeClass(this.selectedClass);
18533 this.selections = [];
18534 if(!suppressEvent){
18535 this.fireEvent("selectionchange", this, this.selections);
18541 * Returns true if the passed node is selected
18542 * @param {HTMLElement/Number} node The node or node index
18543 * @return {Boolean}
18545 isSelected : function(node){
18546 var s = this.selections;
18550 node = this.getNode(node);
18551 return s.indexOf(node) !== -1;
18556 * @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
18557 * @param {Boolean} keepExisting (optional) true to keep existing selections
18558 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18560 select : function(nodeInfo, keepExisting, suppressEvent){
18561 if(nodeInfo instanceof Array){
18563 this.clearSelections(true);
18565 for(var i = 0, len = nodeInfo.length; i < len; i++){
18566 this.select(nodeInfo[i], true, true);
18570 var node = this.getNode(nodeInfo);
18571 if(!node || this.isSelected(node)){
18572 return; // already selected.
18575 this.clearSelections(true);
18578 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18579 Roo.fly(node).addClass(this.selectedClass);
18580 this.selections.push(node);
18581 if(!suppressEvent){
18582 this.fireEvent("selectionchange", this, this.selections);
18590 * @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
18591 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18592 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18594 unselect : function(nodeInfo, keepExisting, suppressEvent)
18596 if(nodeInfo instanceof Array){
18597 Roo.each(this.selections, function(s) {
18598 this.unselect(s, nodeInfo);
18602 var node = this.getNode(nodeInfo);
18603 if(!node || !this.isSelected(node)){
18604 //Roo.log("not selected");
18605 return; // not selected.
18609 Roo.each(this.selections, function(s) {
18611 Roo.fly(node).removeClass(this.selectedClass);
18618 this.selections= ns;
18619 this.fireEvent("selectionchange", this, this.selections);
18623 * Gets a template node.
18624 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18625 * @return {HTMLElement} The node or null if it wasn't found
18627 getNode : function(nodeInfo){
18628 if(typeof nodeInfo == "string"){
18629 return document.getElementById(nodeInfo);
18630 }else if(typeof nodeInfo == "number"){
18631 return this.nodes[nodeInfo];
18637 * Gets a range template nodes.
18638 * @param {Number} startIndex
18639 * @param {Number} endIndex
18640 * @return {Array} An array of nodes
18642 getNodes : function(start, end){
18643 var ns = this.nodes;
18644 start = start || 0;
18645 end = typeof end == "undefined" ? ns.length - 1 : end;
18648 for(var i = start; i <= end; i++){
18652 for(var i = start; i >= end; i--){
18660 * Finds the index of the passed node
18661 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18662 * @return {Number} The index of the node or -1
18664 indexOf : function(node){
18665 node = this.getNode(node);
18666 if(typeof node.nodeIndex == "number"){
18667 return node.nodeIndex;
18669 var ns = this.nodes;
18670 for(var i = 0, len = ns.length; i < len; i++){
18681 * based on jquery fullcalendar
18685 Roo.bootstrap = Roo.bootstrap || {};
18687 * @class Roo.bootstrap.Calendar
18688 * @extends Roo.bootstrap.Component
18689 * Bootstrap Calendar class
18690 * @cfg {Boolean} loadMask (true|false) default false
18691 * @cfg {Object} header generate the user specific header of the calendar, default false
18694 * Create a new Container
18695 * @param {Object} config The config object
18700 Roo.bootstrap.Calendar = function(config){
18701 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18705 * Fires when a date is selected
18706 * @param {DatePicker} this
18707 * @param {Date} date The selected date
18711 * @event monthchange
18712 * Fires when the displayed month changes
18713 * @param {DatePicker} this
18714 * @param {Date} date The selected month
18716 'monthchange': true,
18718 * @event evententer
18719 * Fires when mouse over an event
18720 * @param {Calendar} this
18721 * @param {event} Event
18723 'evententer': true,
18725 * @event eventleave
18726 * Fires when the mouse leaves an
18727 * @param {Calendar} this
18730 'eventleave': true,
18732 * @event eventclick
18733 * Fires when the mouse click an
18734 * @param {Calendar} this
18743 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18746 * @cfg {Number} startDay
18747 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18755 getAutoCreate : function(){
18758 var fc_button = function(name, corner, style, content ) {
18759 return Roo.apply({},{
18761 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18763 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18766 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18777 style : 'width:100%',
18784 cls : 'fc-header-left',
18786 fc_button('prev', 'left', 'arrow', '‹' ),
18787 fc_button('next', 'right', 'arrow', '›' ),
18788 { tag: 'span', cls: 'fc-header-space' },
18789 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18797 cls : 'fc-header-center',
18801 cls: 'fc-header-title',
18804 html : 'month / year'
18812 cls : 'fc-header-right',
18814 /* fc_button('month', 'left', '', 'month' ),
18815 fc_button('week', '', '', 'week' ),
18816 fc_button('day', 'right', '', 'day' )
18828 header = this.header;
18831 var cal_heads = function() {
18833 // fixme - handle this.
18835 for (var i =0; i < Date.dayNames.length; i++) {
18836 var d = Date.dayNames[i];
18839 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18840 html : d.substring(0,3)
18844 ret[0].cls += ' fc-first';
18845 ret[6].cls += ' fc-last';
18848 var cal_cell = function(n) {
18851 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18856 cls: 'fc-day-number',
18860 cls: 'fc-day-content',
18864 style: 'position: relative;' // height: 17px;
18876 var cal_rows = function() {
18879 for (var r = 0; r < 6; r++) {
18886 for (var i =0; i < Date.dayNames.length; i++) {
18887 var d = Date.dayNames[i];
18888 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18891 row.cn[0].cls+=' fc-first';
18892 row.cn[0].cn[0].style = 'min-height:90px';
18893 row.cn[6].cls+=' fc-last';
18897 ret[0].cls += ' fc-first';
18898 ret[4].cls += ' fc-prev-last';
18899 ret[5].cls += ' fc-last';
18906 cls: 'fc-border-separate',
18907 style : 'width:100%',
18915 cls : 'fc-first fc-last',
18933 cls : 'fc-content',
18934 style : "position: relative;",
18937 cls : 'fc-view fc-view-month fc-grid',
18938 style : 'position: relative',
18939 unselectable : 'on',
18942 cls : 'fc-event-container',
18943 style : 'position:absolute;z-index:8;top:0;left:0;'
18961 initEvents : function()
18964 throw "can not find store for calendar";
18970 style: "text-align:center",
18974 style: "background-color:white;width:50%;margin:250 auto",
18978 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18989 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18991 var size = this.el.select('.fc-content', true).first().getSize();
18992 this.maskEl.setSize(size.width, size.height);
18993 this.maskEl.enableDisplayMode("block");
18994 if(!this.loadMask){
18995 this.maskEl.hide();
18998 this.store = Roo.factory(this.store, Roo.data);
18999 this.store.on('load', this.onLoad, this);
19000 this.store.on('beforeload', this.onBeforeLoad, this);
19004 this.cells = this.el.select('.fc-day',true);
19005 //Roo.log(this.cells);
19006 this.textNodes = this.el.query('.fc-day-number');
19007 this.cells.addClassOnOver('fc-state-hover');
19009 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19010 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19011 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19012 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19014 this.on('monthchange', this.onMonthChange, this);
19016 this.update(new Date().clearTime());
19019 resize : function() {
19020 var sz = this.el.getSize();
19022 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19023 this.el.select('.fc-day-content div',true).setHeight(34);
19028 showPrevMonth : function(e){
19029 this.update(this.activeDate.add("mo", -1));
19031 showToday : function(e){
19032 this.update(new Date().clearTime());
19035 showNextMonth : function(e){
19036 this.update(this.activeDate.add("mo", 1));
19040 showPrevYear : function(){
19041 this.update(this.activeDate.add("y", -1));
19045 showNextYear : function(){
19046 this.update(this.activeDate.add("y", 1));
19051 update : function(date)
19053 var vd = this.activeDate;
19054 this.activeDate = date;
19055 // if(vd && this.el){
19056 // var t = date.getTime();
19057 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19058 // Roo.log('using add remove');
19060 // this.fireEvent('monthchange', this, date);
19062 // this.cells.removeClass("fc-state-highlight");
19063 // this.cells.each(function(c){
19064 // if(c.dateValue == t){
19065 // c.addClass("fc-state-highlight");
19066 // setTimeout(function(){
19067 // try{c.dom.firstChild.focus();}catch(e){}
19077 var days = date.getDaysInMonth();
19079 var firstOfMonth = date.getFirstDateOfMonth();
19080 var startingPos = firstOfMonth.getDay()-this.startDay;
19082 if(startingPos < this.startDay){
19086 var pm = date.add(Date.MONTH, -1);
19087 var prevStart = pm.getDaysInMonth()-startingPos;
19089 this.cells = this.el.select('.fc-day',true);
19090 this.textNodes = this.el.query('.fc-day-number');
19091 this.cells.addClassOnOver('fc-state-hover');
19093 var cells = this.cells.elements;
19094 var textEls = this.textNodes;
19096 Roo.each(cells, function(cell){
19097 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19100 days += startingPos;
19102 // convert everything to numbers so it's fast
19103 var day = 86400000;
19104 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19107 //Roo.log(prevStart);
19109 var today = new Date().clearTime().getTime();
19110 var sel = date.clearTime().getTime();
19111 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19112 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19113 var ddMatch = this.disabledDatesRE;
19114 var ddText = this.disabledDatesText;
19115 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19116 var ddaysText = this.disabledDaysText;
19117 var format = this.format;
19119 var setCellClass = function(cal, cell){
19123 //Roo.log('set Cell Class');
19125 var t = d.getTime();
19129 cell.dateValue = t;
19131 cell.className += " fc-today";
19132 cell.className += " fc-state-highlight";
19133 cell.title = cal.todayText;
19136 // disable highlight in other month..
19137 //cell.className += " fc-state-highlight";
19142 cell.className = " fc-state-disabled";
19143 cell.title = cal.minText;
19147 cell.className = " fc-state-disabled";
19148 cell.title = cal.maxText;
19152 if(ddays.indexOf(d.getDay()) != -1){
19153 cell.title = ddaysText;
19154 cell.className = " fc-state-disabled";
19157 if(ddMatch && format){
19158 var fvalue = d.dateFormat(format);
19159 if(ddMatch.test(fvalue)){
19160 cell.title = ddText.replace("%0", fvalue);
19161 cell.className = " fc-state-disabled";
19165 if (!cell.initialClassName) {
19166 cell.initialClassName = cell.dom.className;
19169 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19174 for(; i < startingPos; i++) {
19175 textEls[i].innerHTML = (++prevStart);
19176 d.setDate(d.getDate()+1);
19178 cells[i].className = "fc-past fc-other-month";
19179 setCellClass(this, cells[i]);
19184 for(; i < days; i++){
19185 intDay = i - startingPos + 1;
19186 textEls[i].innerHTML = (intDay);
19187 d.setDate(d.getDate()+1);
19189 cells[i].className = ''; // "x-date-active";
19190 setCellClass(this, cells[i]);
19194 for(; i < 42; i++) {
19195 textEls[i].innerHTML = (++extraDays);
19196 d.setDate(d.getDate()+1);
19198 cells[i].className = "fc-future fc-other-month";
19199 setCellClass(this, cells[i]);
19202 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19204 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19206 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19207 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19209 if(totalRows != 6){
19210 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19211 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19214 this.fireEvent('monthchange', this, date);
19218 if(!this.internalRender){
19219 var main = this.el.dom.firstChild;
19220 var w = main.offsetWidth;
19221 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19222 Roo.fly(main).setWidth(w);
19223 this.internalRender = true;
19224 // opera does not respect the auto grow header center column
19225 // then, after it gets a width opera refuses to recalculate
19226 // without a second pass
19227 if(Roo.isOpera && !this.secondPass){
19228 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19229 this.secondPass = true;
19230 this.update.defer(10, this, [date]);
19237 findCell : function(dt) {
19238 dt = dt.clearTime().getTime();
19240 this.cells.each(function(c){
19241 //Roo.log("check " +c.dateValue + '?=' + dt);
19242 if(c.dateValue == dt){
19252 findCells : function(ev) {
19253 var s = ev.start.clone().clearTime().getTime();
19255 var e= ev.end.clone().clearTime().getTime();
19258 this.cells.each(function(c){
19259 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19261 if(c.dateValue > e){
19264 if(c.dateValue < s){
19273 // findBestRow: function(cells)
19277 // for (var i =0 ; i < cells.length;i++) {
19278 // ret = Math.max(cells[i].rows || 0,ret);
19285 addItem : function(ev)
19287 // look for vertical location slot in
19288 var cells = this.findCells(ev);
19290 // ev.row = this.findBestRow(cells);
19292 // work out the location.
19296 for(var i =0; i < cells.length; i++) {
19298 cells[i].row = cells[0].row;
19301 cells[i].row = cells[i].row + 1;
19311 if (crow.start.getY() == cells[i].getY()) {
19313 crow.end = cells[i];
19330 cells[0].events.push(ev);
19332 this.calevents.push(ev);
19335 clearEvents: function() {
19337 if(!this.calevents){
19341 Roo.each(this.cells.elements, function(c){
19347 Roo.each(this.calevents, function(e) {
19348 Roo.each(e.els, function(el) {
19349 el.un('mouseenter' ,this.onEventEnter, this);
19350 el.un('mouseleave' ,this.onEventLeave, this);
19355 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19361 renderEvents: function()
19365 this.cells.each(function(c) {
19374 if(c.row != c.events.length){
19375 r = 4 - (4 - (c.row - c.events.length));
19378 c.events = ev.slice(0, r);
19379 c.more = ev.slice(r);
19381 if(c.more.length && c.more.length == 1){
19382 c.events.push(c.more.pop());
19385 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19389 this.cells.each(function(c) {
19391 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19394 for (var e = 0; e < c.events.length; e++){
19395 var ev = c.events[e];
19396 var rows = ev.rows;
19398 for(var i = 0; i < rows.length; i++) {
19400 // how many rows should it span..
19403 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19404 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19406 unselectable : "on",
19409 cls: 'fc-event-inner',
19413 // cls: 'fc-event-time',
19414 // html : cells.length > 1 ? '' : ev.time
19418 cls: 'fc-event-title',
19419 html : String.format('{0}', ev.title)
19426 cls: 'ui-resizable-handle ui-resizable-e',
19427 html : '  '
19434 cfg.cls += ' fc-event-start';
19436 if ((i+1) == rows.length) {
19437 cfg.cls += ' fc-event-end';
19440 var ctr = _this.el.select('.fc-event-container',true).first();
19441 var cg = ctr.createChild(cfg);
19443 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19444 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19446 var r = (c.more.length) ? 1 : 0;
19447 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19448 cg.setWidth(ebox.right - sbox.x -2);
19450 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19451 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19452 cg.on('click', _this.onEventClick, _this, ev);
19463 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19464 style : 'position: absolute',
19465 unselectable : "on",
19468 cls: 'fc-event-inner',
19472 cls: 'fc-event-title',
19480 cls: 'ui-resizable-handle ui-resizable-e',
19481 html : '  '
19487 var ctr = _this.el.select('.fc-event-container',true).first();
19488 var cg = ctr.createChild(cfg);
19490 var sbox = c.select('.fc-day-content',true).first().getBox();
19491 var ebox = c.select('.fc-day-content',true).first().getBox();
19493 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19494 cg.setWidth(ebox.right - sbox.x -2);
19496 cg.on('click', _this.onMoreEventClick, _this, c.more);
19506 onEventEnter: function (e, el,event,d) {
19507 this.fireEvent('evententer', this, el, event);
19510 onEventLeave: function (e, el,event,d) {
19511 this.fireEvent('eventleave', this, el, event);
19514 onEventClick: function (e, el,event,d) {
19515 this.fireEvent('eventclick', this, el, event);
19518 onMonthChange: function () {
19522 onMoreEventClick: function(e, el, more)
19526 this.calpopover.placement = 'right';
19527 this.calpopover.setTitle('More');
19529 this.calpopover.setContent('');
19531 var ctr = this.calpopover.el.select('.popover-content', true).first();
19533 Roo.each(more, function(m){
19535 cls : 'fc-event-hori fc-event-draggable',
19538 var cg = ctr.createChild(cfg);
19540 cg.on('click', _this.onEventClick, _this, m);
19543 this.calpopover.show(el);
19548 onLoad: function ()
19550 this.calevents = [];
19553 if(this.store.getCount() > 0){
19554 this.store.data.each(function(d){
19557 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19558 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19559 time : d.data.start_time,
19560 title : d.data.title,
19561 description : d.data.description,
19562 venue : d.data.venue
19567 this.renderEvents();
19569 if(this.calevents.length && this.loadMask){
19570 this.maskEl.hide();
19574 onBeforeLoad: function()
19576 this.clearEvents();
19578 this.maskEl.show();
19592 * @class Roo.bootstrap.Popover
19593 * @extends Roo.bootstrap.Component
19594 * Bootstrap Popover class
19595 * @cfg {String} html contents of the popover (or false to use children..)
19596 * @cfg {String} title of popover (or false to hide)
19597 * @cfg {String} placement how it is placed
19598 * @cfg {String} trigger click || hover (or false to trigger manually)
19599 * @cfg {String} over what (parent or false to trigger manually.)
19600 * @cfg {Number} delay - delay before showing
19603 * Create a new Popover
19604 * @param {Object} config The config object
19607 Roo.bootstrap.Popover = function(config){
19608 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19614 * After the popover show
19616 * @param {Roo.bootstrap.Popover} this
19621 * After the popover hide
19623 * @param {Roo.bootstrap.Popover} this
19629 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19631 title: 'Fill in a title',
19634 placement : 'right',
19635 trigger : 'hover', // hover
19641 can_build_overlaid : false,
19643 getChildContainer : function()
19645 return this.el.select('.popover-content',true).first();
19648 getAutoCreate : function(){
19651 cls : 'popover roo-dynamic',
19652 style: 'display:block',
19658 cls : 'popover-inner',
19662 cls: 'popover-title popover-header',
19666 cls : 'popover-content popover-body',
19677 setTitle: function(str)
19680 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19682 setContent: function(str)
19685 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19687 // as it get's added to the bottom of the page.
19688 onRender : function(ct, position)
19690 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19692 var cfg = Roo.apply({}, this.getAutoCreate());
19696 cfg.cls += ' ' + this.cls;
19699 cfg.style = this.style;
19701 //Roo.log("adding to ");
19702 this.el = Roo.get(document.body).createChild(cfg, position);
19703 // Roo.log(this.el);
19708 initEvents : function()
19710 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19711 this.el.enableDisplayMode('block');
19713 if (this.over === false) {
19716 if (this.triggers === false) {
19719 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19720 var triggers = this.trigger ? this.trigger.split(' ') : [];
19721 Roo.each(triggers, function(trigger) {
19723 if (trigger == 'click') {
19724 on_el.on('click', this.toggle, this);
19725 } else if (trigger != 'manual') {
19726 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19727 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19729 on_el.on(eventIn ,this.enter, this);
19730 on_el.on(eventOut, this.leave, this);
19741 toggle : function () {
19742 this.hoverState == 'in' ? this.leave() : this.enter();
19745 enter : function () {
19747 clearTimeout(this.timeout);
19749 this.hoverState = 'in';
19751 if (!this.delay || !this.delay.show) {
19756 this.timeout = setTimeout(function () {
19757 if (_t.hoverState == 'in') {
19760 }, this.delay.show)
19763 leave : function() {
19764 clearTimeout(this.timeout);
19766 this.hoverState = 'out';
19768 if (!this.delay || !this.delay.hide) {
19773 this.timeout = setTimeout(function () {
19774 if (_t.hoverState == 'out') {
19777 }, this.delay.hide)
19780 show : function (on_el)
19783 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19787 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19788 if (this.html !== false) {
19789 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19791 this.el.removeClass([
19792 'fade','top','bottom', 'left', 'right','in',
19793 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19795 if (!this.title.length) {
19796 this.el.select('.popover-title',true).hide();
19799 var placement = typeof this.placement == 'function' ?
19800 this.placement.call(this, this.el, on_el) :
19803 var autoToken = /\s?auto?\s?/i;
19804 var autoPlace = autoToken.test(placement);
19806 placement = placement.replace(autoToken, '') || 'top';
19810 //this.el.setXY([0,0]);
19812 this.el.dom.style.display='block';
19813 this.el.addClass(placement);
19815 //this.el.appendTo(on_el);
19817 var p = this.getPosition();
19818 var box = this.el.getBox();
19823 var align = Roo.bootstrap.Popover.alignment[placement];
19826 this.el.alignTo(on_el, align[0],align[1]);
19827 //var arrow = this.el.select('.arrow',true).first();
19828 //arrow.set(align[2],
19830 this.el.addClass('in');
19833 if (this.el.hasClass('fade')) {
19837 this.hoverState = 'in';
19839 this.fireEvent('show', this);
19844 this.el.setXY([0,0]);
19845 this.el.removeClass('in');
19847 this.hoverState = null;
19849 this.fireEvent('hide', this);
19854 Roo.bootstrap.Popover.alignment = {
19855 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19856 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19857 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19858 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19869 * @class Roo.bootstrap.Progress
19870 * @extends Roo.bootstrap.Component
19871 * Bootstrap Progress class
19872 * @cfg {Boolean} striped striped of the progress bar
19873 * @cfg {Boolean} active animated of the progress bar
19877 * Create a new Progress
19878 * @param {Object} config The config object
19881 Roo.bootstrap.Progress = function(config){
19882 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19885 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19890 getAutoCreate : function(){
19898 cfg.cls += ' progress-striped';
19902 cfg.cls += ' active';
19921 * @class Roo.bootstrap.ProgressBar
19922 * @extends Roo.bootstrap.Component
19923 * Bootstrap ProgressBar class
19924 * @cfg {Number} aria_valuenow aria-value now
19925 * @cfg {Number} aria_valuemin aria-value min
19926 * @cfg {Number} aria_valuemax aria-value max
19927 * @cfg {String} label label for the progress bar
19928 * @cfg {String} panel (success | info | warning | danger )
19929 * @cfg {String} role role of the progress bar
19930 * @cfg {String} sr_only text
19934 * Create a new ProgressBar
19935 * @param {Object} config The config object
19938 Roo.bootstrap.ProgressBar = function(config){
19939 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19942 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19946 aria_valuemax : 100,
19952 getAutoCreate : function()
19957 cls: 'progress-bar',
19958 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19970 cfg.role = this.role;
19973 if(this.aria_valuenow){
19974 cfg['aria-valuenow'] = this.aria_valuenow;
19977 if(this.aria_valuemin){
19978 cfg['aria-valuemin'] = this.aria_valuemin;
19981 if(this.aria_valuemax){
19982 cfg['aria-valuemax'] = this.aria_valuemax;
19985 if(this.label && !this.sr_only){
19986 cfg.html = this.label;
19990 cfg.cls += ' progress-bar-' + this.panel;
19996 update : function(aria_valuenow)
19998 this.aria_valuenow = aria_valuenow;
20000 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20015 * @class Roo.bootstrap.TabGroup
20016 * @extends Roo.bootstrap.Column
20017 * Bootstrap Column class
20018 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20019 * @cfg {Boolean} carousel true to make the group behave like a carousel
20020 * @cfg {Boolean} bullets show bullets for the panels
20021 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20022 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20023 * @cfg {Boolean} showarrow (true|false) show arrow default true
20026 * Create a new TabGroup
20027 * @param {Object} config The config object
20030 Roo.bootstrap.TabGroup = function(config){
20031 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20033 this.navId = Roo.id();
20036 Roo.bootstrap.TabGroup.register(this);
20040 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20043 transition : false,
20048 slideOnTouch : false,
20051 getAutoCreate : function()
20053 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20055 cfg.cls += ' tab-content';
20057 if (this.carousel) {
20058 cfg.cls += ' carousel slide';
20061 cls : 'carousel-inner',
20065 if(this.bullets && !Roo.isTouch){
20068 cls : 'carousel-bullets',
20072 if(this.bullets_cls){
20073 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20080 cfg.cn[0].cn.push(bullets);
20083 if(this.showarrow){
20084 cfg.cn[0].cn.push({
20086 class : 'carousel-arrow',
20090 class : 'carousel-prev',
20094 class : 'fa fa-chevron-left'
20100 class : 'carousel-next',
20104 class : 'fa fa-chevron-right'
20117 initEvents: function()
20119 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20120 // this.el.on("touchstart", this.onTouchStart, this);
20123 if(this.autoslide){
20126 this.slideFn = window.setInterval(function() {
20127 _this.showPanelNext();
20131 if(this.showarrow){
20132 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20133 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20139 // onTouchStart : function(e, el, o)
20141 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20145 // this.showPanelNext();
20149 getChildContainer : function()
20151 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20155 * register a Navigation item
20156 * @param {Roo.bootstrap.NavItem} the navitem to add
20158 register : function(item)
20160 this.tabs.push( item);
20161 item.navId = this.navId; // not really needed..
20166 getActivePanel : function()
20169 Roo.each(this.tabs, function(t) {
20179 getPanelByName : function(n)
20182 Roo.each(this.tabs, function(t) {
20183 if (t.tabId == n) {
20191 indexOfPanel : function(p)
20194 Roo.each(this.tabs, function(t,i) {
20195 if (t.tabId == p.tabId) {
20204 * show a specific panel
20205 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20206 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20208 showPanel : function (pan)
20210 if(this.transition || typeof(pan) == 'undefined'){
20211 Roo.log("waiting for the transitionend");
20215 if (typeof(pan) == 'number') {
20216 pan = this.tabs[pan];
20219 if (typeof(pan) == 'string') {
20220 pan = this.getPanelByName(pan);
20223 var cur = this.getActivePanel();
20226 Roo.log('pan or acitve pan is undefined');
20230 if (pan.tabId == this.getActivePanel().tabId) {
20234 if (false === cur.fireEvent('beforedeactivate')) {
20238 if(this.bullets > 0 && !Roo.isTouch){
20239 this.setActiveBullet(this.indexOfPanel(pan));
20242 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20244 //class="carousel-item carousel-item-next carousel-item-left"
20246 this.transition = true;
20247 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20248 var lr = dir == 'next' ? 'left' : 'right';
20249 pan.el.addClass(dir); // or prev
20250 pan.el.addClass('carousel-item-' + dir); // or prev
20251 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20252 cur.el.addClass(lr); // or right
20253 pan.el.addClass(lr);
20254 cur.el.addClass('carousel-item-' +lr); // or right
20255 pan.el.addClass('carousel-item-' +lr);
20259 cur.el.on('transitionend', function() {
20260 Roo.log("trans end?");
20262 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20263 pan.setActive(true);
20265 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20266 cur.setActive(false);
20268 _this.transition = false;
20270 }, this, { single: true } );
20275 cur.setActive(false);
20276 pan.setActive(true);
20281 showPanelNext : function()
20283 var i = this.indexOfPanel(this.getActivePanel());
20285 if (i >= this.tabs.length - 1 && !this.autoslide) {
20289 if (i >= this.tabs.length - 1 && this.autoslide) {
20293 this.showPanel(this.tabs[i+1]);
20296 showPanelPrev : function()
20298 var i = this.indexOfPanel(this.getActivePanel());
20300 if (i < 1 && !this.autoslide) {
20304 if (i < 1 && this.autoslide) {
20305 i = this.tabs.length;
20308 this.showPanel(this.tabs[i-1]);
20312 addBullet: function()
20314 if(!this.bullets || Roo.isTouch){
20317 var ctr = this.el.select('.carousel-bullets',true).first();
20318 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20319 var bullet = ctr.createChild({
20320 cls : 'bullet bullet-' + i
20321 },ctr.dom.lastChild);
20326 bullet.on('click', (function(e, el, o, ii, t){
20328 e.preventDefault();
20330 this.showPanel(ii);
20332 if(this.autoslide && this.slideFn){
20333 clearInterval(this.slideFn);
20334 this.slideFn = window.setInterval(function() {
20335 _this.showPanelNext();
20339 }).createDelegate(this, [i, bullet], true));
20344 setActiveBullet : function(i)
20350 Roo.each(this.el.select('.bullet', true).elements, function(el){
20351 el.removeClass('selected');
20354 var bullet = this.el.select('.bullet-' + i, true).first();
20360 bullet.addClass('selected');
20371 Roo.apply(Roo.bootstrap.TabGroup, {
20375 * register a Navigation Group
20376 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20378 register : function(navgrp)
20380 this.groups[navgrp.navId] = navgrp;
20384 * fetch a Navigation Group based on the navigation ID
20385 * if one does not exist , it will get created.
20386 * @param {string} the navgroup to add
20387 * @returns {Roo.bootstrap.NavGroup} the navgroup
20389 get: function(navId) {
20390 if (typeof(this.groups[navId]) == 'undefined') {
20391 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20393 return this.groups[navId] ;
20408 * @class Roo.bootstrap.TabPanel
20409 * @extends Roo.bootstrap.Component
20410 * Bootstrap TabPanel class
20411 * @cfg {Boolean} active panel active
20412 * @cfg {String} html panel content
20413 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20414 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20415 * @cfg {String} href click to link..
20416 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20420 * Create a new TabPanel
20421 * @param {Object} config The config object
20424 Roo.bootstrap.TabPanel = function(config){
20425 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20429 * Fires when the active status changes
20430 * @param {Roo.bootstrap.TabPanel} this
20431 * @param {Boolean} state the new state
20436 * @event beforedeactivate
20437 * Fires before a tab is de-activated - can be used to do validation on a form.
20438 * @param {Roo.bootstrap.TabPanel} this
20439 * @return {Boolean} false if there is an error
20442 'beforedeactivate': true
20445 this.tabId = this.tabId || Roo.id();
20449 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20456 touchSlide : false,
20457 getAutoCreate : function(){
20462 // item is needed for carousel - not sure if it has any effect otherwise
20463 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20464 html: this.html || ''
20468 cfg.cls += ' active';
20472 cfg.tabId = this.tabId;
20480 initEvents: function()
20482 var p = this.parent();
20484 this.navId = this.navId || p.navId;
20486 if (typeof(this.navId) != 'undefined') {
20487 // not really needed.. but just in case.. parent should be a NavGroup.
20488 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20492 var i = tg.tabs.length - 1;
20494 if(this.active && tg.bullets > 0 && i < tg.bullets){
20495 tg.setActiveBullet(i);
20499 this.el.on('click', this.onClick, this);
20501 if(Roo.isTouch && this.touchSlide){
20502 this.el.on("touchstart", this.onTouchStart, this);
20503 this.el.on("touchmove", this.onTouchMove, this);
20504 this.el.on("touchend", this.onTouchEnd, this);
20509 onRender : function(ct, position)
20511 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20514 setActive : function(state)
20516 Roo.log("panel - set active " + this.tabId + "=" + state);
20518 this.active = state;
20520 this.el.removeClass('active');
20522 } else if (!this.el.hasClass('active')) {
20523 this.el.addClass('active');
20526 this.fireEvent('changed', this, state);
20529 onClick : function(e)
20531 e.preventDefault();
20533 if(!this.href.length){
20537 window.location.href = this.href;
20546 onTouchStart : function(e)
20548 this.swiping = false;
20550 this.startX = e.browserEvent.touches[0].clientX;
20551 this.startY = e.browserEvent.touches[0].clientY;
20554 onTouchMove : function(e)
20556 this.swiping = true;
20558 this.endX = e.browserEvent.touches[0].clientX;
20559 this.endY = e.browserEvent.touches[0].clientY;
20562 onTouchEnd : function(e)
20569 var tabGroup = this.parent();
20571 if(this.endX > this.startX){ // swiping right
20572 tabGroup.showPanelPrev();
20576 if(this.startX > this.endX){ // swiping left
20577 tabGroup.showPanelNext();
20596 * @class Roo.bootstrap.DateField
20597 * @extends Roo.bootstrap.Input
20598 * Bootstrap DateField class
20599 * @cfg {Number} weekStart default 0
20600 * @cfg {String} viewMode default empty, (months|years)
20601 * @cfg {String} minViewMode default empty, (months|years)
20602 * @cfg {Number} startDate default -Infinity
20603 * @cfg {Number} endDate default Infinity
20604 * @cfg {Boolean} todayHighlight default false
20605 * @cfg {Boolean} todayBtn default false
20606 * @cfg {Boolean} calendarWeeks default false
20607 * @cfg {Object} daysOfWeekDisabled default empty
20608 * @cfg {Boolean} singleMode default false (true | false)
20610 * @cfg {Boolean} keyboardNavigation default true
20611 * @cfg {String} language default en
20614 * Create a new DateField
20615 * @param {Object} config The config object
20618 Roo.bootstrap.DateField = function(config){
20619 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20623 * Fires when this field show.
20624 * @param {Roo.bootstrap.DateField} this
20625 * @param {Mixed} date The date value
20630 * Fires when this field hide.
20631 * @param {Roo.bootstrap.DateField} this
20632 * @param {Mixed} date The date value
20637 * Fires when select a date.
20638 * @param {Roo.bootstrap.DateField} this
20639 * @param {Mixed} date The date value
20643 * @event beforeselect
20644 * Fires when before select a date.
20645 * @param {Roo.bootstrap.DateField} this
20646 * @param {Mixed} date The date value
20648 beforeselect : true
20652 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20655 * @cfg {String} format
20656 * The default date format string which can be overriden for localization support. The format must be
20657 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20661 * @cfg {String} altFormats
20662 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20663 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20665 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20673 todayHighlight : false,
20679 keyboardNavigation: true,
20681 calendarWeeks: false,
20683 startDate: -Infinity,
20687 daysOfWeekDisabled: [],
20691 singleMode : false,
20693 UTCDate: function()
20695 return new Date(Date.UTC.apply(Date, arguments));
20698 UTCToday: function()
20700 var today = new Date();
20701 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20704 getDate: function() {
20705 var d = this.getUTCDate();
20706 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20709 getUTCDate: function() {
20713 setDate: function(d) {
20714 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20717 setUTCDate: function(d) {
20719 this.setValue(this.formatDate(this.date));
20722 onRender: function(ct, position)
20725 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20727 this.language = this.language || 'en';
20728 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20729 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20731 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20732 this.format = this.format || 'm/d/y';
20733 this.isInline = false;
20734 this.isInput = true;
20735 this.component = this.el.select('.add-on', true).first() || false;
20736 this.component = (this.component && this.component.length === 0) ? false : this.component;
20737 this.hasInput = this.component && this.inputEl().length;
20739 if (typeof(this.minViewMode === 'string')) {
20740 switch (this.minViewMode) {
20742 this.minViewMode = 1;
20745 this.minViewMode = 2;
20748 this.minViewMode = 0;
20753 if (typeof(this.viewMode === 'string')) {
20754 switch (this.viewMode) {
20767 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20769 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20771 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20773 this.picker().on('mousedown', this.onMousedown, this);
20774 this.picker().on('click', this.onClick, this);
20776 this.picker().addClass('datepicker-dropdown');
20778 this.startViewMode = this.viewMode;
20780 if(this.singleMode){
20781 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20782 v.setVisibilityMode(Roo.Element.DISPLAY);
20786 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20787 v.setStyle('width', '189px');
20791 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20792 if(!this.calendarWeeks){
20797 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20798 v.attr('colspan', function(i, val){
20799 return parseInt(val) + 1;
20804 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20806 this.setStartDate(this.startDate);
20807 this.setEndDate(this.endDate);
20809 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20816 if(this.isInline) {
20821 picker : function()
20823 return this.pickerEl;
20824 // return this.el.select('.datepicker', true).first();
20827 fillDow: function()
20829 var dowCnt = this.weekStart;
20838 if(this.calendarWeeks){
20846 while (dowCnt < this.weekStart + 7) {
20850 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20854 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20857 fillMonths: function()
20860 var months = this.picker().select('>.datepicker-months td', true).first();
20862 months.dom.innerHTML = '';
20868 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20871 months.createChild(month);
20878 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;
20880 if (this.date < this.startDate) {
20881 this.viewDate = new Date(this.startDate);
20882 } else if (this.date > this.endDate) {
20883 this.viewDate = new Date(this.endDate);
20885 this.viewDate = new Date(this.date);
20893 var d = new Date(this.viewDate),
20894 year = d.getUTCFullYear(),
20895 month = d.getUTCMonth(),
20896 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20897 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20898 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20899 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20900 currentDate = this.date && this.date.valueOf(),
20901 today = this.UTCToday();
20903 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20905 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20907 // this.picker.select('>tfoot th.today').
20908 // .text(dates[this.language].today)
20909 // .toggle(this.todayBtn !== false);
20911 this.updateNavArrows();
20914 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20916 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20918 prevMonth.setUTCDate(day);
20920 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20922 var nextMonth = new Date(prevMonth);
20924 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20926 nextMonth = nextMonth.valueOf();
20928 var fillMonths = false;
20930 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20932 while(prevMonth.valueOf() <= nextMonth) {
20935 if (prevMonth.getUTCDay() === this.weekStart) {
20937 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20945 if(this.calendarWeeks){
20946 // ISO 8601: First week contains first thursday.
20947 // ISO also states week starts on Monday, but we can be more abstract here.
20949 // Start of current week: based on weekstart/current date
20950 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20951 // Thursday of this week
20952 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20953 // First Thursday of year, year from thursday
20954 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20955 // Calendar week: ms between thursdays, div ms per day, div 7 days
20956 calWeek = (th - yth) / 864e5 / 7 + 1;
20958 fillMonths.cn.push({
20966 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20968 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20971 if (this.todayHighlight &&
20972 prevMonth.getUTCFullYear() == today.getFullYear() &&
20973 prevMonth.getUTCMonth() == today.getMonth() &&
20974 prevMonth.getUTCDate() == today.getDate()) {
20975 clsName += ' today';
20978 if (currentDate && prevMonth.valueOf() === currentDate) {
20979 clsName += ' active';
20982 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20983 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20984 clsName += ' disabled';
20987 fillMonths.cn.push({
20989 cls: 'day ' + clsName,
20990 html: prevMonth.getDate()
20993 prevMonth.setDate(prevMonth.getDate()+1);
20996 var currentYear = this.date && this.date.getUTCFullYear();
20997 var currentMonth = this.date && this.date.getUTCMonth();
20999 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21001 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21002 v.removeClass('active');
21004 if(currentYear === year && k === currentMonth){
21005 v.addClass('active');
21008 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21009 v.addClass('disabled');
21015 year = parseInt(year/10, 10) * 10;
21017 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21019 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21022 for (var i = -1; i < 11; i++) {
21023 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21025 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21033 showMode: function(dir)
21036 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21039 Roo.each(this.picker().select('>div',true).elements, function(v){
21040 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21043 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21048 if(this.isInline) {
21052 this.picker().removeClass(['bottom', 'top']);
21054 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21056 * place to the top of element!
21060 this.picker().addClass('top');
21061 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21066 this.picker().addClass('bottom');
21068 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21071 parseDate : function(value)
21073 if(!value || value instanceof Date){
21076 var v = Date.parseDate(value, this.format);
21077 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21078 v = Date.parseDate(value, 'Y-m-d');
21080 if(!v && this.altFormats){
21081 if(!this.altFormatsArray){
21082 this.altFormatsArray = this.altFormats.split("|");
21084 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21085 v = Date.parseDate(value, this.altFormatsArray[i]);
21091 formatDate : function(date, fmt)
21093 return (!date || !(date instanceof Date)) ?
21094 date : date.dateFormat(fmt || this.format);
21097 onFocus : function()
21099 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21103 onBlur : function()
21105 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21107 var d = this.inputEl().getValue();
21114 showPopup : function()
21116 this.picker().show();
21120 this.fireEvent('showpopup', this, this.date);
21123 hidePopup : function()
21125 if(this.isInline) {
21128 this.picker().hide();
21129 this.viewMode = this.startViewMode;
21132 this.fireEvent('hidepopup', this, this.date);
21136 onMousedown: function(e)
21138 e.stopPropagation();
21139 e.preventDefault();
21144 Roo.bootstrap.DateField.superclass.keyup.call(this);
21148 setValue: function(v)
21150 if(this.fireEvent('beforeselect', this, v) !== false){
21151 var d = new Date(this.parseDate(v) ).clearTime();
21153 if(isNaN(d.getTime())){
21154 this.date = this.viewDate = '';
21155 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21159 v = this.formatDate(d);
21161 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21163 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21167 this.fireEvent('select', this, this.date);
21171 getValue: function()
21173 return this.formatDate(this.date);
21176 fireKey: function(e)
21178 if (!this.picker().isVisible()){
21179 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21185 var dateChanged = false,
21187 newDate, newViewDate;
21192 e.preventDefault();
21196 if (!this.keyboardNavigation) {
21199 dir = e.keyCode == 37 ? -1 : 1;
21202 newDate = this.moveYear(this.date, dir);
21203 newViewDate = this.moveYear(this.viewDate, dir);
21204 } else if (e.shiftKey){
21205 newDate = this.moveMonth(this.date, dir);
21206 newViewDate = this.moveMonth(this.viewDate, dir);
21208 newDate = new Date(this.date);
21209 newDate.setUTCDate(this.date.getUTCDate() + dir);
21210 newViewDate = new Date(this.viewDate);
21211 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21213 if (this.dateWithinRange(newDate)){
21214 this.date = newDate;
21215 this.viewDate = newViewDate;
21216 this.setValue(this.formatDate(this.date));
21218 e.preventDefault();
21219 dateChanged = true;
21224 if (!this.keyboardNavigation) {
21227 dir = e.keyCode == 38 ? -1 : 1;
21229 newDate = this.moveYear(this.date, dir);
21230 newViewDate = this.moveYear(this.viewDate, dir);
21231 } else if (e.shiftKey){
21232 newDate = this.moveMonth(this.date, dir);
21233 newViewDate = this.moveMonth(this.viewDate, dir);
21235 newDate = new Date(this.date);
21236 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21237 newViewDate = new Date(this.viewDate);
21238 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21240 if (this.dateWithinRange(newDate)){
21241 this.date = newDate;
21242 this.viewDate = newViewDate;
21243 this.setValue(this.formatDate(this.date));
21245 e.preventDefault();
21246 dateChanged = true;
21250 this.setValue(this.formatDate(this.date));
21252 e.preventDefault();
21255 this.setValue(this.formatDate(this.date));
21269 onClick: function(e)
21271 e.stopPropagation();
21272 e.preventDefault();
21274 var target = e.getTarget();
21276 if(target.nodeName.toLowerCase() === 'i'){
21277 target = Roo.get(target).dom.parentNode;
21280 var nodeName = target.nodeName;
21281 var className = target.className;
21282 var html = target.innerHTML;
21283 //Roo.log(nodeName);
21285 switch(nodeName.toLowerCase()) {
21287 switch(className) {
21293 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21294 switch(this.viewMode){
21296 this.viewDate = this.moveMonth(this.viewDate, dir);
21300 this.viewDate = this.moveYear(this.viewDate, dir);
21306 var date = new Date();
21307 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21309 this.setValue(this.formatDate(this.date));
21316 if (className.indexOf('disabled') < 0) {
21317 this.viewDate.setUTCDate(1);
21318 if (className.indexOf('month') > -1) {
21319 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21321 var year = parseInt(html, 10) || 0;
21322 this.viewDate.setUTCFullYear(year);
21326 if(this.singleMode){
21327 this.setValue(this.formatDate(this.viewDate));
21338 //Roo.log(className);
21339 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21340 var day = parseInt(html, 10) || 1;
21341 var year = this.viewDate.getUTCFullYear(),
21342 month = this.viewDate.getUTCMonth();
21344 if (className.indexOf('old') > -1) {
21351 } else if (className.indexOf('new') > -1) {
21359 //Roo.log([year,month,day]);
21360 this.date = this.UTCDate(year, month, day,0,0,0,0);
21361 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21363 //Roo.log(this.formatDate(this.date));
21364 this.setValue(this.formatDate(this.date));
21371 setStartDate: function(startDate)
21373 this.startDate = startDate || -Infinity;
21374 if (this.startDate !== -Infinity) {
21375 this.startDate = this.parseDate(this.startDate);
21378 this.updateNavArrows();
21381 setEndDate: function(endDate)
21383 this.endDate = endDate || Infinity;
21384 if (this.endDate !== Infinity) {
21385 this.endDate = this.parseDate(this.endDate);
21388 this.updateNavArrows();
21391 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21393 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21394 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21395 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21397 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21398 return parseInt(d, 10);
21401 this.updateNavArrows();
21404 updateNavArrows: function()
21406 if(this.singleMode){
21410 var d = new Date(this.viewDate),
21411 year = d.getUTCFullYear(),
21412 month = d.getUTCMonth();
21414 Roo.each(this.picker().select('.prev', true).elements, function(v){
21416 switch (this.viewMode) {
21419 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21425 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21432 Roo.each(this.picker().select('.next', true).elements, function(v){
21434 switch (this.viewMode) {
21437 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21443 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21451 moveMonth: function(date, dir)
21456 var new_date = new Date(date.valueOf()),
21457 day = new_date.getUTCDate(),
21458 month = new_date.getUTCMonth(),
21459 mag = Math.abs(dir),
21461 dir = dir > 0 ? 1 : -1;
21464 // If going back one month, make sure month is not current month
21465 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21467 return new_date.getUTCMonth() == month;
21469 // If going forward one month, make sure month is as expected
21470 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21472 return new_date.getUTCMonth() != new_month;
21474 new_month = month + dir;
21475 new_date.setUTCMonth(new_month);
21476 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21477 if (new_month < 0 || new_month > 11) {
21478 new_month = (new_month + 12) % 12;
21481 // For magnitudes >1, move one month at a time...
21482 for (var i=0; i<mag; i++) {
21483 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21484 new_date = this.moveMonth(new_date, dir);
21486 // ...then reset the day, keeping it in the new month
21487 new_month = new_date.getUTCMonth();
21488 new_date.setUTCDate(day);
21490 return new_month != new_date.getUTCMonth();
21493 // Common date-resetting loop -- if date is beyond end of month, make it
21496 new_date.setUTCDate(--day);
21497 new_date.setUTCMonth(new_month);
21502 moveYear: function(date, dir)
21504 return this.moveMonth(date, dir*12);
21507 dateWithinRange: function(date)
21509 return date >= this.startDate && date <= this.endDate;
21515 this.picker().remove();
21518 validateValue : function(value)
21520 if(this.getVisibilityEl().hasClass('hidden')){
21524 if(value.length < 1) {
21525 if(this.allowBlank){
21531 if(value.length < this.minLength){
21534 if(value.length > this.maxLength){
21538 var vt = Roo.form.VTypes;
21539 if(!vt[this.vtype](value, this)){
21543 if(typeof this.validator == "function"){
21544 var msg = this.validator(value);
21550 if(this.regex && !this.regex.test(value)){
21554 if(typeof(this.parseDate(value)) == 'undefined'){
21558 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21562 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21572 this.date = this.viewDate = '';
21574 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21579 Roo.apply(Roo.bootstrap.DateField, {
21590 html: '<i class="fa fa-arrow-left"/>'
21600 html: '<i class="fa fa-arrow-right"/>'
21642 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21643 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21644 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21645 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21646 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21659 navFnc: 'FullYear',
21664 navFnc: 'FullYear',
21669 Roo.apply(Roo.bootstrap.DateField, {
21673 cls: 'datepicker dropdown-menu roo-dynamic',
21677 cls: 'datepicker-days',
21681 cls: 'table-condensed',
21683 Roo.bootstrap.DateField.head,
21687 Roo.bootstrap.DateField.footer
21694 cls: 'datepicker-months',
21698 cls: 'table-condensed',
21700 Roo.bootstrap.DateField.head,
21701 Roo.bootstrap.DateField.content,
21702 Roo.bootstrap.DateField.footer
21709 cls: 'datepicker-years',
21713 cls: 'table-condensed',
21715 Roo.bootstrap.DateField.head,
21716 Roo.bootstrap.DateField.content,
21717 Roo.bootstrap.DateField.footer
21736 * @class Roo.bootstrap.TimeField
21737 * @extends Roo.bootstrap.Input
21738 * Bootstrap DateField class
21742 * Create a new TimeField
21743 * @param {Object} config The config object
21746 Roo.bootstrap.TimeField = function(config){
21747 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21751 * Fires when this field show.
21752 * @param {Roo.bootstrap.DateField} thisthis
21753 * @param {Mixed} date The date value
21758 * Fires when this field hide.
21759 * @param {Roo.bootstrap.DateField} this
21760 * @param {Mixed} date The date value
21765 * Fires when select a date.
21766 * @param {Roo.bootstrap.DateField} this
21767 * @param {Mixed} date The date value
21773 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21776 * @cfg {String} format
21777 * The default time format string which can be overriden for localization support. The format must be
21778 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21782 onRender: function(ct, position)
21785 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21787 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21789 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21791 this.pop = this.picker().select('>.datepicker-time',true).first();
21792 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21794 this.picker().on('mousedown', this.onMousedown, this);
21795 this.picker().on('click', this.onClick, this);
21797 this.picker().addClass('datepicker-dropdown');
21802 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21803 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21804 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21805 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21806 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21807 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21811 fireKey: function(e){
21812 if (!this.picker().isVisible()){
21813 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21819 e.preventDefault();
21827 this.onTogglePeriod();
21830 this.onIncrementMinutes();
21833 this.onDecrementMinutes();
21842 onClick: function(e) {
21843 e.stopPropagation();
21844 e.preventDefault();
21847 picker : function()
21849 return this.el.select('.datepicker', true).first();
21852 fillTime: function()
21854 var time = this.pop.select('tbody', true).first();
21856 time.dom.innerHTML = '';
21871 cls: 'hours-up glyphicon glyphicon-chevron-up'
21891 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21912 cls: 'timepicker-hour',
21927 cls: 'timepicker-minute',
21942 cls: 'btn btn-primary period',
21964 cls: 'hours-down glyphicon glyphicon-chevron-down'
21984 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22002 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22009 var hours = this.time.getHours();
22010 var minutes = this.time.getMinutes();
22023 hours = hours - 12;
22027 hours = '0' + hours;
22031 minutes = '0' + minutes;
22034 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22035 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22036 this.pop.select('button', true).first().dom.innerHTML = period;
22042 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22044 var cls = ['bottom'];
22046 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22053 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22058 this.picker().addClass(cls.join('-'));
22062 Roo.each(cls, function(c){
22064 _this.picker().setTop(_this.inputEl().getHeight());
22068 _this.picker().setTop(0 - _this.picker().getHeight());
22073 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22077 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22084 onFocus : function()
22086 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22090 onBlur : function()
22092 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22098 this.picker().show();
22103 this.fireEvent('show', this, this.date);
22108 this.picker().hide();
22111 this.fireEvent('hide', this, this.date);
22114 setTime : function()
22117 this.setValue(this.time.format(this.format));
22119 this.fireEvent('select', this, this.date);
22124 onMousedown: function(e){
22125 e.stopPropagation();
22126 e.preventDefault();
22129 onIncrementHours: function()
22131 Roo.log('onIncrementHours');
22132 this.time = this.time.add(Date.HOUR, 1);
22137 onDecrementHours: function()
22139 Roo.log('onDecrementHours');
22140 this.time = this.time.add(Date.HOUR, -1);
22144 onIncrementMinutes: function()
22146 Roo.log('onIncrementMinutes');
22147 this.time = this.time.add(Date.MINUTE, 1);
22151 onDecrementMinutes: function()
22153 Roo.log('onDecrementMinutes');
22154 this.time = this.time.add(Date.MINUTE, -1);
22158 onTogglePeriod: function()
22160 Roo.log('onTogglePeriod');
22161 this.time = this.time.add(Date.HOUR, 12);
22168 Roo.apply(Roo.bootstrap.TimeField, {
22198 cls: 'btn btn-info ok',
22210 Roo.apply(Roo.bootstrap.TimeField, {
22214 cls: 'datepicker dropdown-menu',
22218 cls: 'datepicker-time',
22222 cls: 'table-condensed',
22224 Roo.bootstrap.TimeField.content,
22225 Roo.bootstrap.TimeField.footer
22244 * @class Roo.bootstrap.MonthField
22245 * @extends Roo.bootstrap.Input
22246 * Bootstrap MonthField class
22248 * @cfg {String} language default en
22251 * Create a new MonthField
22252 * @param {Object} config The config object
22255 Roo.bootstrap.MonthField = function(config){
22256 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22261 * Fires when this field show.
22262 * @param {Roo.bootstrap.MonthField} this
22263 * @param {Mixed} date The date value
22268 * Fires when this field hide.
22269 * @param {Roo.bootstrap.MonthField} this
22270 * @param {Mixed} date The date value
22275 * Fires when select a date.
22276 * @param {Roo.bootstrap.MonthField} this
22277 * @param {String} oldvalue The old value
22278 * @param {String} newvalue The new value
22284 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22286 onRender: function(ct, position)
22289 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22291 this.language = this.language || 'en';
22292 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22293 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22295 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22296 this.isInline = false;
22297 this.isInput = true;
22298 this.component = this.el.select('.add-on', true).first() || false;
22299 this.component = (this.component && this.component.length === 0) ? false : this.component;
22300 this.hasInput = this.component && this.inputEL().length;
22302 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22304 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22306 this.picker().on('mousedown', this.onMousedown, this);
22307 this.picker().on('click', this.onClick, this);
22309 this.picker().addClass('datepicker-dropdown');
22311 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22312 v.setStyle('width', '189px');
22319 if(this.isInline) {
22325 setValue: function(v, suppressEvent)
22327 var o = this.getValue();
22329 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22333 if(suppressEvent !== true){
22334 this.fireEvent('select', this, o, v);
22339 getValue: function()
22344 onClick: function(e)
22346 e.stopPropagation();
22347 e.preventDefault();
22349 var target = e.getTarget();
22351 if(target.nodeName.toLowerCase() === 'i'){
22352 target = Roo.get(target).dom.parentNode;
22355 var nodeName = target.nodeName;
22356 var className = target.className;
22357 var html = target.innerHTML;
22359 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22363 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22365 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22371 picker : function()
22373 return this.pickerEl;
22376 fillMonths: function()
22379 var months = this.picker().select('>.datepicker-months td', true).first();
22381 months.dom.innerHTML = '';
22387 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22390 months.createChild(month);
22399 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22400 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22403 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22404 e.removeClass('active');
22406 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22407 e.addClass('active');
22414 if(this.isInline) {
22418 this.picker().removeClass(['bottom', 'top']);
22420 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22422 * place to the top of element!
22426 this.picker().addClass('top');
22427 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22432 this.picker().addClass('bottom');
22434 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22437 onFocus : function()
22439 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22443 onBlur : function()
22445 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22447 var d = this.inputEl().getValue();
22456 this.picker().show();
22457 this.picker().select('>.datepicker-months', true).first().show();
22461 this.fireEvent('show', this, this.date);
22466 if(this.isInline) {
22469 this.picker().hide();
22470 this.fireEvent('hide', this, this.date);
22474 onMousedown: function(e)
22476 e.stopPropagation();
22477 e.preventDefault();
22482 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22486 fireKey: function(e)
22488 if (!this.picker().isVisible()){
22489 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22500 e.preventDefault();
22504 dir = e.keyCode == 37 ? -1 : 1;
22506 this.vIndex = this.vIndex + dir;
22508 if(this.vIndex < 0){
22512 if(this.vIndex > 11){
22516 if(isNaN(this.vIndex)){
22520 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22526 dir = e.keyCode == 38 ? -1 : 1;
22528 this.vIndex = this.vIndex + dir * 4;
22530 if(this.vIndex < 0){
22534 if(this.vIndex > 11){
22538 if(isNaN(this.vIndex)){
22542 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22547 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22548 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22552 e.preventDefault();
22555 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22556 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22572 this.picker().remove();
22577 Roo.apply(Roo.bootstrap.MonthField, {
22596 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22597 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22602 Roo.apply(Roo.bootstrap.MonthField, {
22606 cls: 'datepicker dropdown-menu roo-dynamic',
22610 cls: 'datepicker-months',
22614 cls: 'table-condensed',
22616 Roo.bootstrap.DateField.content
22636 * @class Roo.bootstrap.CheckBox
22637 * @extends Roo.bootstrap.Input
22638 * Bootstrap CheckBox class
22640 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22641 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22642 * @cfg {String} boxLabel The text that appears beside the checkbox
22643 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22644 * @cfg {Boolean} checked initnal the element
22645 * @cfg {Boolean} inline inline the element (default false)
22646 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22647 * @cfg {String} tooltip label tooltip
22650 * Create a new CheckBox
22651 * @param {Object} config The config object
22654 Roo.bootstrap.CheckBox = function(config){
22655 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22660 * Fires when the element is checked or unchecked.
22661 * @param {Roo.bootstrap.CheckBox} this This input
22662 * @param {Boolean} checked The new checked value
22667 * Fires when the element is click.
22668 * @param {Roo.bootstrap.CheckBox} this This input
22675 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22677 inputType: 'checkbox',
22686 // checkbox success does not make any sense really..
22691 getAutoCreate : function()
22693 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22699 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22702 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22708 type : this.inputType,
22709 value : this.inputValue,
22710 cls : 'roo-' + this.inputType, //'form-box',
22711 placeholder : this.placeholder || ''
22715 if(this.inputType != 'radio'){
22719 cls : 'roo-hidden-value',
22720 value : this.checked ? this.inputValue : this.valueOff
22725 if (this.weight) { // Validity check?
22726 cfg.cls += " " + this.inputType + "-" + this.weight;
22729 if (this.disabled) {
22730 input.disabled=true;
22734 input.checked = this.checked;
22739 input.name = this.name;
22741 if(this.inputType != 'radio'){
22742 hidden.name = this.name;
22743 input.name = '_hidden_' + this.name;
22748 input.cls += ' input-' + this.size;
22753 ['xs','sm','md','lg'].map(function(size){
22754 if (settings[size]) {
22755 cfg.cls += ' col-' + size + '-' + settings[size];
22759 var inputblock = input;
22761 if (this.before || this.after) {
22764 cls : 'input-group',
22769 inputblock.cn.push({
22771 cls : 'input-group-addon',
22776 inputblock.cn.push(input);
22778 if(this.inputType != 'radio'){
22779 inputblock.cn.push(hidden);
22783 inputblock.cn.push({
22785 cls : 'input-group-addon',
22791 var boxLabelCfg = false;
22797 //'for': id, // box label is handled by onclick - so no for...
22799 html: this.boxLabel
22802 boxLabelCfg.tooltip = this.tooltip;
22808 if (align ==='left' && this.fieldLabel.length) {
22809 // Roo.log("left and has label");
22814 cls : 'control-label',
22815 html : this.fieldLabel
22826 cfg.cn[1].cn.push(boxLabelCfg);
22829 if(this.labelWidth > 12){
22830 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22833 if(this.labelWidth < 13 && this.labelmd == 0){
22834 this.labelmd = this.labelWidth;
22837 if(this.labellg > 0){
22838 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22839 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22842 if(this.labelmd > 0){
22843 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22844 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22847 if(this.labelsm > 0){
22848 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22849 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22852 if(this.labelxs > 0){
22853 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22854 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22857 } else if ( this.fieldLabel.length) {
22858 // Roo.log(" label");
22862 tag: this.boxLabel ? 'span' : 'label',
22864 cls: 'control-label box-input-label',
22865 //cls : 'input-group-addon',
22866 html : this.fieldLabel
22873 cfg.cn.push(boxLabelCfg);
22878 // Roo.log(" no label && no align");
22879 cfg.cn = [ inputblock ] ;
22881 cfg.cn.push(boxLabelCfg);
22889 if(this.inputType != 'radio'){
22890 cfg.cn.push(hidden);
22898 * return the real input element.
22900 inputEl: function ()
22902 return this.el.select('input.roo-' + this.inputType,true).first();
22904 hiddenEl: function ()
22906 return this.el.select('input.roo-hidden-value',true).first();
22909 labelEl: function()
22911 return this.el.select('label.control-label',true).first();
22913 /* depricated... */
22917 return this.labelEl();
22920 boxLabelEl: function()
22922 return this.el.select('label.box-label',true).first();
22925 initEvents : function()
22927 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22929 this.inputEl().on('click', this.onClick, this);
22931 if (this.boxLabel) {
22932 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22935 this.startValue = this.getValue();
22938 Roo.bootstrap.CheckBox.register(this);
22942 onClick : function(e)
22944 if(this.fireEvent('click', this, e) !== false){
22945 this.setChecked(!this.checked);
22950 setChecked : function(state,suppressEvent)
22952 this.startValue = this.getValue();
22954 if(this.inputType == 'radio'){
22956 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22957 e.dom.checked = false;
22960 this.inputEl().dom.checked = true;
22962 this.inputEl().dom.value = this.inputValue;
22964 if(suppressEvent !== true){
22965 this.fireEvent('check', this, true);
22973 this.checked = state;
22975 this.inputEl().dom.checked = state;
22978 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22980 if(suppressEvent !== true){
22981 this.fireEvent('check', this, state);
22987 getValue : function()
22989 if(this.inputType == 'radio'){
22990 return this.getGroupValue();
22993 return this.hiddenEl().dom.value;
22997 getGroupValue : function()
22999 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23003 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23006 setValue : function(v,suppressEvent)
23008 if(this.inputType == 'radio'){
23009 this.setGroupValue(v, suppressEvent);
23013 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23018 setGroupValue : function(v, suppressEvent)
23020 this.startValue = this.getValue();
23022 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23023 e.dom.checked = false;
23025 if(e.dom.value == v){
23026 e.dom.checked = true;
23030 if(suppressEvent !== true){
23031 this.fireEvent('check', this, true);
23039 validate : function()
23041 if(this.getVisibilityEl().hasClass('hidden')){
23047 (this.inputType == 'radio' && this.validateRadio()) ||
23048 (this.inputType == 'checkbox' && this.validateCheckbox())
23054 this.markInvalid();
23058 validateRadio : function()
23060 if(this.getVisibilityEl().hasClass('hidden')){
23064 if(this.allowBlank){
23070 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23071 if(!e.dom.checked){
23083 validateCheckbox : function()
23086 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23087 //return (this.getValue() == this.inputValue) ? true : false;
23090 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23098 for(var i in group){
23099 if(group[i].el.isVisible(true)){
23107 for(var i in group){
23112 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23119 * Mark this field as valid
23121 markValid : function()
23125 this.fireEvent('valid', this);
23127 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23130 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23137 if(this.inputType == 'radio'){
23138 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23139 var fg = e.findParent('.form-group', false, true);
23140 if (Roo.bootstrap.version == 3) {
23141 fg.removeClass([_this.invalidClass, _this.validClass]);
23142 fg.addClass(_this.validClass);
23144 fg.removeClass(['is-valid', 'is-invalid']);
23145 fg.addClass('is-valid');
23153 var fg = this.el.findParent('.form-group', false, true);
23154 if (Roo.bootstrap.version == 3) {
23155 fg.removeClass([this.invalidClass, this.validClass]);
23156 fg.addClass(this.validClass);
23158 fg.removeClass(['is-valid', 'is-invalid']);
23159 fg.addClass('is-valid');
23164 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23170 for(var i in group){
23171 var fg = group[i].el.findParent('.form-group', false, true);
23172 if (Roo.bootstrap.version == 3) {
23173 fg.removeClass([this.invalidClass, this.validClass]);
23174 fg.addClass(this.validClass);
23176 fg.removeClass(['is-valid', 'is-invalid']);
23177 fg.addClass('is-valid');
23183 * Mark this field as invalid
23184 * @param {String} msg The validation message
23186 markInvalid : function(msg)
23188 if(this.allowBlank){
23194 this.fireEvent('invalid', this, msg);
23196 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23199 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23203 label.markInvalid();
23206 if(this.inputType == 'radio'){
23208 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23209 var fg = e.findParent('.form-group', false, true);
23210 if (Roo.bootstrap.version == 3) {
23211 fg.removeClass([_this.invalidClass, _this.validClass]);
23212 fg.addClass(_this.invalidClass);
23214 fg.removeClass(['is-invalid', 'is-valid']);
23215 fg.addClass('is-invalid');
23223 var fg = this.el.findParent('.form-group', false, true);
23224 if (Roo.bootstrap.version == 3) {
23225 fg.removeClass([_this.invalidClass, _this.validClass]);
23226 fg.addClass(_this.invalidClass);
23228 fg.removeClass(['is-invalid', 'is-valid']);
23229 fg.addClass('is-invalid');
23234 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23240 for(var i in group){
23241 var fg = group[i].el.findParent('.form-group', false, true);
23242 if (Roo.bootstrap.version == 3) {
23243 fg.removeClass([_this.invalidClass, _this.validClass]);
23244 fg.addClass(_this.invalidClass);
23246 fg.removeClass(['is-invalid', 'is-valid']);
23247 fg.addClass('is-invalid');
23253 clearInvalid : function()
23255 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23257 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23259 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23261 if (label && label.iconEl) {
23262 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23263 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23267 disable : function()
23269 if(this.inputType != 'radio'){
23270 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23277 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23278 _this.getActionEl().addClass(this.disabledClass);
23279 e.dom.disabled = true;
23283 this.disabled = true;
23284 this.fireEvent("disable", this);
23288 enable : function()
23290 if(this.inputType != 'radio'){
23291 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23298 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23299 _this.getActionEl().removeClass(this.disabledClass);
23300 e.dom.disabled = false;
23304 this.disabled = false;
23305 this.fireEvent("enable", this);
23309 setBoxLabel : function(v)
23314 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23320 Roo.apply(Roo.bootstrap.CheckBox, {
23325 * register a CheckBox Group
23326 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23328 register : function(checkbox)
23330 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23331 this.groups[checkbox.groupId] = {};
23334 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23338 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23342 * fetch a CheckBox Group based on the group ID
23343 * @param {string} the group ID
23344 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23346 get: function(groupId) {
23347 if (typeof(this.groups[groupId]) == 'undefined') {
23351 return this.groups[groupId] ;
23364 * @class Roo.bootstrap.Radio
23365 * @extends Roo.bootstrap.Component
23366 * Bootstrap Radio class
23367 * @cfg {String} boxLabel - the label associated
23368 * @cfg {String} value - the value of radio
23371 * Create a new Radio
23372 * @param {Object} config The config object
23374 Roo.bootstrap.Radio = function(config){
23375 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23379 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23385 getAutoCreate : function()
23389 cls : 'form-group radio',
23394 html : this.boxLabel
23402 initEvents : function()
23404 this.parent().register(this);
23406 this.el.on('click', this.onClick, this);
23410 onClick : function(e)
23412 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23413 this.setChecked(true);
23417 setChecked : function(state, suppressEvent)
23419 this.parent().setValue(this.value, suppressEvent);
23423 setBoxLabel : function(v)
23428 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23443 * @class Roo.bootstrap.SecurePass
23444 * @extends Roo.bootstrap.Input
23445 * Bootstrap SecurePass class
23449 * Create a new SecurePass
23450 * @param {Object} config The config object
23453 Roo.bootstrap.SecurePass = function (config) {
23454 // these go here, so the translation tool can replace them..
23456 PwdEmpty: "Please type a password, and then retype it to confirm.",
23457 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23458 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23459 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23460 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23461 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23462 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23463 TooWeak: "Your password is Too Weak."
23465 this.meterLabel = "Password strength:";
23466 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23467 this.meterClass = [
23468 "roo-password-meter-tooweak",
23469 "roo-password-meter-weak",
23470 "roo-password-meter-medium",
23471 "roo-password-meter-strong",
23472 "roo-password-meter-grey"
23477 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23480 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23482 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23484 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23485 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23486 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23487 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23488 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23489 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23490 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23500 * @cfg {String/Object} Label for the strength meter (defaults to
23501 * 'Password strength:')
23506 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23507 * ['Weak', 'Medium', 'Strong'])
23510 pwdStrengths: false,
23523 initEvents: function ()
23525 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23527 if (this.el.is('input[type=password]') && Roo.isSafari) {
23528 this.el.on('keydown', this.SafariOnKeyDown, this);
23531 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23534 onRender: function (ct, position)
23536 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23537 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23538 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23540 this.trigger.createChild({
23545 cls: 'roo-password-meter-grey col-xs-12',
23548 //width: this.meterWidth + 'px'
23552 cls: 'roo-password-meter-text'
23558 if (this.hideTrigger) {
23559 this.trigger.setDisplayed(false);
23561 this.setSize(this.width || '', this.height || '');
23564 onDestroy: function ()
23566 if (this.trigger) {
23567 this.trigger.removeAllListeners();
23568 this.trigger.remove();
23571 this.wrap.remove();
23573 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23576 checkStrength: function ()
23578 var pwd = this.inputEl().getValue();
23579 if (pwd == this._lastPwd) {
23584 if (this.ClientSideStrongPassword(pwd)) {
23586 } else if (this.ClientSideMediumPassword(pwd)) {
23588 } else if (this.ClientSideWeakPassword(pwd)) {
23594 Roo.log('strength1: ' + strength);
23596 //var pm = this.trigger.child('div/div/div').dom;
23597 var pm = this.trigger.child('div/div');
23598 pm.removeClass(this.meterClass);
23599 pm.addClass(this.meterClass[strength]);
23602 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23604 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23606 this._lastPwd = pwd;
23610 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23612 this._lastPwd = '';
23614 var pm = this.trigger.child('div/div');
23615 pm.removeClass(this.meterClass);
23616 pm.addClass('roo-password-meter-grey');
23619 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23622 this.inputEl().dom.type='password';
23625 validateValue: function (value)
23627 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23630 if (value.length == 0) {
23631 if (this.allowBlank) {
23632 this.clearInvalid();
23636 this.markInvalid(this.errors.PwdEmpty);
23637 this.errorMsg = this.errors.PwdEmpty;
23645 if (!value.match(/[\x21-\x7e]+/)) {
23646 this.markInvalid(this.errors.PwdBadChar);
23647 this.errorMsg = this.errors.PwdBadChar;
23650 if (value.length < 6) {
23651 this.markInvalid(this.errors.PwdShort);
23652 this.errorMsg = this.errors.PwdShort;
23655 if (value.length > 16) {
23656 this.markInvalid(this.errors.PwdLong);
23657 this.errorMsg = this.errors.PwdLong;
23661 if (this.ClientSideStrongPassword(value)) {
23663 } else if (this.ClientSideMediumPassword(value)) {
23665 } else if (this.ClientSideWeakPassword(value)) {
23672 if (strength < 2) {
23673 //this.markInvalid(this.errors.TooWeak);
23674 this.errorMsg = this.errors.TooWeak;
23679 console.log('strength2: ' + strength);
23681 //var pm = this.trigger.child('div/div/div').dom;
23683 var pm = this.trigger.child('div/div');
23684 pm.removeClass(this.meterClass);
23685 pm.addClass(this.meterClass[strength]);
23687 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23689 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23691 this.errorMsg = '';
23695 CharacterSetChecks: function (type)
23698 this.fResult = false;
23701 isctype: function (character, type)
23704 case this.kCapitalLetter:
23705 if (character >= 'A' && character <= 'Z') {
23710 case this.kSmallLetter:
23711 if (character >= 'a' && character <= 'z') {
23717 if (character >= '0' && character <= '9') {
23722 case this.kPunctuation:
23723 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23734 IsLongEnough: function (pwd, size)
23736 return !(pwd == null || isNaN(size) || pwd.length < size);
23739 SpansEnoughCharacterSets: function (word, nb)
23741 if (!this.IsLongEnough(word, nb))
23746 var characterSetChecks = new Array(
23747 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23748 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23751 for (var index = 0; index < word.length; ++index) {
23752 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23753 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23754 characterSetChecks[nCharSet].fResult = true;
23761 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23762 if (characterSetChecks[nCharSet].fResult) {
23767 if (nCharSets < nb) {
23773 ClientSideStrongPassword: function (pwd)
23775 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23778 ClientSideMediumPassword: function (pwd)
23780 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23783 ClientSideWeakPassword: function (pwd)
23785 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23788 })//<script type="text/javascript">
23791 * Based Ext JS Library 1.1.1
23792 * Copyright(c) 2006-2007, Ext JS, LLC.
23798 * @class Roo.HtmlEditorCore
23799 * @extends Roo.Component
23800 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23802 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23805 Roo.HtmlEditorCore = function(config){
23808 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23813 * @event initialize
23814 * Fires when the editor is fully initialized (including the iframe)
23815 * @param {Roo.HtmlEditorCore} this
23820 * Fires when the editor is first receives the focus. Any insertion must wait
23821 * until after this event.
23822 * @param {Roo.HtmlEditorCore} this
23826 * @event beforesync
23827 * Fires before the textarea is updated with content from the editor iframe. Return false
23828 * to cancel the sync.
23829 * @param {Roo.HtmlEditorCore} this
23830 * @param {String} html
23834 * @event beforepush
23835 * Fires before the iframe editor is updated with content from the textarea. Return false
23836 * to cancel the push.
23837 * @param {Roo.HtmlEditorCore} this
23838 * @param {String} html
23843 * Fires when the textarea is updated with content from the editor iframe.
23844 * @param {Roo.HtmlEditorCore} this
23845 * @param {String} html
23850 * Fires when the iframe editor is updated with content from the textarea.
23851 * @param {Roo.HtmlEditorCore} this
23852 * @param {String} html
23857 * @event editorevent
23858 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23859 * @param {Roo.HtmlEditorCore} this
23865 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23867 // defaults : white / black...
23868 this.applyBlacklists();
23875 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23879 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23885 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23890 * @cfg {Number} height (in pixels)
23894 * @cfg {Number} width (in pixels)
23899 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23902 stylesheets: false,
23907 // private properties
23908 validationEvent : false,
23910 initialized : false,
23912 sourceEditMode : false,
23913 onFocus : Roo.emptyFn,
23915 hideMode:'offsets',
23919 // blacklist + whitelisted elements..
23926 * Protected method that will not generally be called directly. It
23927 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23928 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23930 getDocMarkup : function(){
23934 // inherit styels from page...??
23935 if (this.stylesheets === false) {
23937 Roo.get(document.head).select('style').each(function(node) {
23938 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23941 Roo.get(document.head).select('link').each(function(node) {
23942 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23945 } else if (!this.stylesheets.length) {
23947 st = '<style type="text/css">' +
23948 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23951 for (var i in this.stylesheets) {
23952 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23957 st += '<style type="text/css">' +
23958 'IMG { cursor: pointer } ' +
23961 var cls = 'roo-htmleditor-body';
23963 if(this.bodyCls.length){
23964 cls += ' ' + this.bodyCls;
23967 return '<html><head>' + st +
23968 //<style type="text/css">' +
23969 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23971 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23975 onRender : function(ct, position)
23978 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23979 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23982 this.el.dom.style.border = '0 none';
23983 this.el.dom.setAttribute('tabIndex', -1);
23984 this.el.addClass('x-hidden hide');
23988 if(Roo.isIE){ // fix IE 1px bogus margin
23989 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23993 this.frameId = Roo.id();
23997 var iframe = this.owner.wrap.createChild({
23999 cls: 'form-control', // bootstrap..
24001 name: this.frameId,
24002 frameBorder : 'no',
24003 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24008 this.iframe = iframe.dom;
24010 this.assignDocWin();
24012 this.doc.designMode = 'on';
24015 this.doc.write(this.getDocMarkup());
24019 var task = { // must defer to wait for browser to be ready
24021 //console.log("run task?" + this.doc.readyState);
24022 this.assignDocWin();
24023 if(this.doc.body || this.doc.readyState == 'complete'){
24025 this.doc.designMode="on";
24029 Roo.TaskMgr.stop(task);
24030 this.initEditor.defer(10, this);
24037 Roo.TaskMgr.start(task);
24042 onResize : function(w, h)
24044 Roo.log('resize: ' +w + ',' + h );
24045 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24049 if(typeof w == 'number'){
24051 this.iframe.style.width = w + 'px';
24053 if(typeof h == 'number'){
24055 this.iframe.style.height = h + 'px';
24057 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24064 * Toggles the editor between standard and source edit mode.
24065 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24067 toggleSourceEdit : function(sourceEditMode){
24069 this.sourceEditMode = sourceEditMode === true;
24071 if(this.sourceEditMode){
24073 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24076 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24077 //this.iframe.className = '';
24080 //this.setSize(this.owner.wrap.getSize());
24081 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24088 * Protected method that will not generally be called directly. If you need/want
24089 * custom HTML cleanup, this is the method you should override.
24090 * @param {String} html The HTML to be cleaned
24091 * return {String} The cleaned HTML
24093 cleanHtml : function(html){
24094 html = String(html);
24095 if(html.length > 5){
24096 if(Roo.isSafari){ // strip safari nonsense
24097 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24100 if(html == ' '){
24107 * HTML Editor -> Textarea
24108 * Protected method that will not generally be called directly. Syncs the contents
24109 * of the editor iframe with the textarea.
24111 syncValue : function(){
24112 if(this.initialized){
24113 var bd = (this.doc.body || this.doc.documentElement);
24114 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24115 var html = bd.innerHTML;
24117 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24118 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24120 html = '<div style="'+m[0]+'">' + html + '</div>';
24123 html = this.cleanHtml(html);
24124 // fix up the special chars.. normaly like back quotes in word...
24125 // however we do not want to do this with chinese..
24126 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24128 var cc = match.charCodeAt();
24130 // Get the character value, handling surrogate pairs
24131 if (match.length == 2) {
24132 // It's a surrogate pair, calculate the Unicode code point
24133 var high = match.charCodeAt(0) - 0xD800;
24134 var low = match.charCodeAt(1) - 0xDC00;
24135 cc = (high * 0x400) + low + 0x10000;
24137 (cc >= 0x4E00 && cc < 0xA000 ) ||
24138 (cc >= 0x3400 && cc < 0x4E00 ) ||
24139 (cc >= 0xf900 && cc < 0xfb00 )
24144 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24145 return "&#" + cc + ";";
24152 if(this.owner.fireEvent('beforesync', this, html) !== false){
24153 this.el.dom.value = html;
24154 this.owner.fireEvent('sync', this, html);
24160 * Protected method that will not generally be called directly. Pushes the value of the textarea
24161 * into the iframe editor.
24163 pushValue : function(){
24164 if(this.initialized){
24165 var v = this.el.dom.value.trim();
24167 // if(v.length < 1){
24171 if(this.owner.fireEvent('beforepush', this, v) !== false){
24172 var d = (this.doc.body || this.doc.documentElement);
24174 this.cleanUpPaste();
24175 this.el.dom.value = d.innerHTML;
24176 this.owner.fireEvent('push', this, v);
24182 deferFocus : function(){
24183 this.focus.defer(10, this);
24187 focus : function(){
24188 if(this.win && !this.sourceEditMode){
24195 assignDocWin: function()
24197 var iframe = this.iframe;
24200 this.doc = iframe.contentWindow.document;
24201 this.win = iframe.contentWindow;
24203 // if (!Roo.get(this.frameId)) {
24206 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24207 // this.win = Roo.get(this.frameId).dom.contentWindow;
24209 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24213 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24214 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24219 initEditor : function(){
24220 //console.log("INIT EDITOR");
24221 this.assignDocWin();
24225 this.doc.designMode="on";
24227 this.doc.write(this.getDocMarkup());
24230 var dbody = (this.doc.body || this.doc.documentElement);
24231 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24232 // this copies styles from the containing element into thsi one..
24233 // not sure why we need all of this..
24234 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24236 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24237 //ss['background-attachment'] = 'fixed'; // w3c
24238 dbody.bgProperties = 'fixed'; // ie
24239 //Roo.DomHelper.applyStyles(dbody, ss);
24240 Roo.EventManager.on(this.doc, {
24241 //'mousedown': this.onEditorEvent,
24242 'mouseup': this.onEditorEvent,
24243 'dblclick': this.onEditorEvent,
24244 'click': this.onEditorEvent,
24245 'keyup': this.onEditorEvent,
24250 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24252 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24253 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24255 this.initialized = true;
24257 this.owner.fireEvent('initialize', this);
24262 onDestroy : function(){
24268 //for (var i =0; i < this.toolbars.length;i++) {
24269 // // fixme - ask toolbars for heights?
24270 // this.toolbars[i].onDestroy();
24273 //this.wrap.dom.innerHTML = '';
24274 //this.wrap.remove();
24279 onFirstFocus : function(){
24281 this.assignDocWin();
24284 this.activated = true;
24287 if(Roo.isGecko){ // prevent silly gecko errors
24289 var s = this.win.getSelection();
24290 if(!s.focusNode || s.focusNode.nodeType != 3){
24291 var r = s.getRangeAt(0);
24292 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24297 this.execCmd('useCSS', true);
24298 this.execCmd('styleWithCSS', false);
24301 this.owner.fireEvent('activate', this);
24305 adjustFont: function(btn){
24306 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24307 //if(Roo.isSafari){ // safari
24310 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24311 if(Roo.isSafari){ // safari
24312 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24313 v = (v < 10) ? 10 : v;
24314 v = (v > 48) ? 48 : v;
24315 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24320 v = Math.max(1, v+adjust);
24322 this.execCmd('FontSize', v );
24325 onEditorEvent : function(e)
24327 this.owner.fireEvent('editorevent', this, e);
24328 // this.updateToolbar();
24329 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24332 insertTag : function(tg)
24334 // could be a bit smarter... -> wrap the current selected tRoo..
24335 if (tg.toLowerCase() == 'span' ||
24336 tg.toLowerCase() == 'code' ||
24337 tg.toLowerCase() == 'sup' ||
24338 tg.toLowerCase() == 'sub'
24341 range = this.createRange(this.getSelection());
24342 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24343 wrappingNode.appendChild(range.extractContents());
24344 range.insertNode(wrappingNode);
24351 this.execCmd("formatblock", tg);
24355 insertText : function(txt)
24359 var range = this.createRange();
24360 range.deleteContents();
24361 //alert(Sender.getAttribute('label'));
24363 range.insertNode(this.doc.createTextNode(txt));
24369 * Executes a Midas editor command on the editor document and performs necessary focus and
24370 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24371 * @param {String} cmd The Midas command
24372 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24374 relayCmd : function(cmd, value){
24376 this.execCmd(cmd, value);
24377 this.owner.fireEvent('editorevent', this);
24378 //this.updateToolbar();
24379 this.owner.deferFocus();
24383 * Executes a Midas editor command directly on the editor document.
24384 * For visual commands, you should use {@link #relayCmd} instead.
24385 * <b>This should only be called after the editor is initialized.</b>
24386 * @param {String} cmd The Midas command
24387 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24389 execCmd : function(cmd, value){
24390 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24397 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24399 * @param {String} text | dom node..
24401 insertAtCursor : function(text)
24404 if(!this.activated){
24410 var r = this.doc.selection.createRange();
24421 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24425 // from jquery ui (MIT licenced)
24427 var win = this.win;
24429 if (win.getSelection && win.getSelection().getRangeAt) {
24430 range = win.getSelection().getRangeAt(0);
24431 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24432 range.insertNode(node);
24433 } else if (win.document.selection && win.document.selection.createRange) {
24434 // no firefox support
24435 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24436 win.document.selection.createRange().pasteHTML(txt);
24438 // no firefox support
24439 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24440 this.execCmd('InsertHTML', txt);
24449 mozKeyPress : function(e){
24451 var c = e.getCharCode(), cmd;
24454 c = String.fromCharCode(c).toLowerCase();
24468 this.cleanUpPaste.defer(100, this);
24476 e.preventDefault();
24484 fixKeys : function(){ // load time branching for fastest keydown performance
24486 return function(e){
24487 var k = e.getKey(), r;
24490 r = this.doc.selection.createRange();
24493 r.pasteHTML('    ');
24500 r = this.doc.selection.createRange();
24502 var target = r.parentElement();
24503 if(!target || target.tagName.toLowerCase() != 'li'){
24505 r.pasteHTML('<br />');
24511 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24512 this.cleanUpPaste.defer(100, this);
24518 }else if(Roo.isOpera){
24519 return function(e){
24520 var k = e.getKey();
24524 this.execCmd('InsertHTML','    ');
24527 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24528 this.cleanUpPaste.defer(100, this);
24533 }else if(Roo.isSafari){
24534 return function(e){
24535 var k = e.getKey();
24539 this.execCmd('InsertText','\t');
24543 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24544 this.cleanUpPaste.defer(100, this);
24552 getAllAncestors: function()
24554 var p = this.getSelectedNode();
24557 a.push(p); // push blank onto stack..
24558 p = this.getParentElement();
24562 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24566 a.push(this.doc.body);
24570 lastSelNode : false,
24573 getSelection : function()
24575 this.assignDocWin();
24576 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24579 getSelectedNode: function()
24581 // this may only work on Gecko!!!
24583 // should we cache this!!!!
24588 var range = this.createRange(this.getSelection()).cloneRange();
24591 var parent = range.parentElement();
24593 var testRange = range.duplicate();
24594 testRange.moveToElementText(parent);
24595 if (testRange.inRange(range)) {
24598 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24601 parent = parent.parentElement;
24606 // is ancestor a text element.
24607 var ac = range.commonAncestorContainer;
24608 if (ac.nodeType == 3) {
24609 ac = ac.parentNode;
24612 var ar = ac.childNodes;
24615 var other_nodes = [];
24616 var has_other_nodes = false;
24617 for (var i=0;i<ar.length;i++) {
24618 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24621 // fullly contained node.
24623 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24628 // probably selected..
24629 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24630 other_nodes.push(ar[i]);
24634 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24639 has_other_nodes = true;
24641 if (!nodes.length && other_nodes.length) {
24642 nodes= other_nodes;
24644 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24650 createRange: function(sel)
24652 // this has strange effects when using with
24653 // top toolbar - not sure if it's a great idea.
24654 //this.editor.contentWindow.focus();
24655 if (typeof sel != "undefined") {
24657 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24659 return this.doc.createRange();
24662 return this.doc.createRange();
24665 getParentElement: function()
24668 this.assignDocWin();
24669 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24671 var range = this.createRange(sel);
24674 var p = range.commonAncestorContainer;
24675 while (p.nodeType == 3) { // text node
24686 * Range intersection.. the hard stuff...
24690 * [ -- selected range --- ]
24694 * if end is before start or hits it. fail.
24695 * if start is after end or hits it fail.
24697 * if either hits (but other is outside. - then it's not
24703 // @see http://www.thismuchiknow.co.uk/?p=64.
24704 rangeIntersectsNode : function(range, node)
24706 var nodeRange = node.ownerDocument.createRange();
24708 nodeRange.selectNode(node);
24710 nodeRange.selectNodeContents(node);
24713 var rangeStartRange = range.cloneRange();
24714 rangeStartRange.collapse(true);
24716 var rangeEndRange = range.cloneRange();
24717 rangeEndRange.collapse(false);
24719 var nodeStartRange = nodeRange.cloneRange();
24720 nodeStartRange.collapse(true);
24722 var nodeEndRange = nodeRange.cloneRange();
24723 nodeEndRange.collapse(false);
24725 return rangeStartRange.compareBoundaryPoints(
24726 Range.START_TO_START, nodeEndRange) == -1 &&
24727 rangeEndRange.compareBoundaryPoints(
24728 Range.START_TO_START, nodeStartRange) == 1;
24732 rangeCompareNode : function(range, node)
24734 var nodeRange = node.ownerDocument.createRange();
24736 nodeRange.selectNode(node);
24738 nodeRange.selectNodeContents(node);
24742 range.collapse(true);
24744 nodeRange.collapse(true);
24746 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24747 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24749 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24751 var nodeIsBefore = ss == 1;
24752 var nodeIsAfter = ee == -1;
24754 if (nodeIsBefore && nodeIsAfter) {
24757 if (!nodeIsBefore && nodeIsAfter) {
24758 return 1; //right trailed.
24761 if (nodeIsBefore && !nodeIsAfter) {
24762 return 2; // left trailed.
24768 // private? - in a new class?
24769 cleanUpPaste : function()
24771 // cleans up the whole document..
24772 Roo.log('cleanuppaste');
24774 this.cleanUpChildren(this.doc.body);
24775 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24776 if (clean != this.doc.body.innerHTML) {
24777 this.doc.body.innerHTML = clean;
24782 cleanWordChars : function(input) {// change the chars to hex code
24783 var he = Roo.HtmlEditorCore;
24785 var output = input;
24786 Roo.each(he.swapCodes, function(sw) {
24787 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24789 output = output.replace(swapper, sw[1]);
24796 cleanUpChildren : function (n)
24798 if (!n.childNodes.length) {
24801 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24802 this.cleanUpChild(n.childNodes[i]);
24809 cleanUpChild : function (node)
24812 //console.log(node);
24813 if (node.nodeName == "#text") {
24814 // clean up silly Windows -- stuff?
24817 if (node.nodeName == "#comment") {
24818 node.parentNode.removeChild(node);
24819 // clean up silly Windows -- stuff?
24822 var lcname = node.tagName.toLowerCase();
24823 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24824 // whitelist of tags..
24826 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24828 node.parentNode.removeChild(node);
24833 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24835 // spans with no attributes - just remove them..
24836 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24837 remove_keep_children = true;
24840 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24841 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24843 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24844 // remove_keep_children = true;
24847 if (remove_keep_children) {
24848 this.cleanUpChildren(node);
24849 // inserts everything just before this node...
24850 while (node.childNodes.length) {
24851 var cn = node.childNodes[0];
24852 node.removeChild(cn);
24853 node.parentNode.insertBefore(cn, node);
24855 node.parentNode.removeChild(node);
24859 if (!node.attributes || !node.attributes.length) {
24864 this.cleanUpChildren(node);
24868 function cleanAttr(n,v)
24871 if (v.match(/^\./) || v.match(/^\//)) {
24874 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24877 if (v.match(/^#/)) {
24880 if (v.match(/^\{/)) { // allow template editing.
24883 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24884 node.removeAttribute(n);
24888 var cwhite = this.cwhite;
24889 var cblack = this.cblack;
24891 function cleanStyle(n,v)
24893 if (v.match(/expression/)) { //XSS?? should we even bother..
24894 node.removeAttribute(n);
24898 var parts = v.split(/;/);
24901 Roo.each(parts, function(p) {
24902 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24906 var l = p.split(':').shift().replace(/\s+/g,'');
24907 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24909 if ( cwhite.length && cblack.indexOf(l) > -1) {
24910 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24911 //node.removeAttribute(n);
24915 // only allow 'c whitelisted system attributes'
24916 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24917 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24918 //node.removeAttribute(n);
24928 if (clean.length) {
24929 node.setAttribute(n, clean.join(';'));
24931 node.removeAttribute(n);
24937 for (var i = node.attributes.length-1; i > -1 ; i--) {
24938 var a = node.attributes[i];
24941 if (a.name.toLowerCase().substr(0,2)=='on') {
24942 node.removeAttribute(a.name);
24945 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24946 node.removeAttribute(a.name);
24949 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24950 cleanAttr(a.name,a.value); // fixme..
24953 if (a.name == 'style') {
24954 cleanStyle(a.name,a.value);
24957 /// clean up MS crap..
24958 // tecnically this should be a list of valid class'es..
24961 if (a.name == 'class') {
24962 if (a.value.match(/^Mso/)) {
24963 node.removeAttribute('class');
24966 if (a.value.match(/^body$/)) {
24967 node.removeAttribute('class');
24978 this.cleanUpChildren(node);
24984 * Clean up MS wordisms...
24986 cleanWord : function(node)
24989 this.cleanWord(this.doc.body);
24994 node.nodeName == 'SPAN' &&
24995 !node.hasAttributes() &&
24996 node.childNodes.length == 1 &&
24997 node.firstChild.nodeName == "#text"
24999 var textNode = node.firstChild;
25000 node.removeChild(textNode);
25001 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25002 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25004 node.parentNode.insertBefore(textNode, node);
25005 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25006 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25008 node.parentNode.removeChild(node);
25011 if (node.nodeName == "#text") {
25012 // clean up silly Windows -- stuff?
25015 if (node.nodeName == "#comment") {
25016 node.parentNode.removeChild(node);
25017 // clean up silly Windows -- stuff?
25021 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25022 node.parentNode.removeChild(node);
25025 //Roo.log(node.tagName);
25026 // remove - but keep children..
25027 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25028 //Roo.log('-- removed');
25029 while (node.childNodes.length) {
25030 var cn = node.childNodes[0];
25031 node.removeChild(cn);
25032 node.parentNode.insertBefore(cn, node);
25033 // move node to parent - and clean it..
25034 this.cleanWord(cn);
25036 node.parentNode.removeChild(node);
25037 /// no need to iterate chidlren = it's got none..
25038 //this.iterateChildren(node, this.cleanWord);
25042 if (node.className.length) {
25044 var cn = node.className.split(/\W+/);
25046 Roo.each(cn, function(cls) {
25047 if (cls.match(/Mso[a-zA-Z]+/)) {
25052 node.className = cna.length ? cna.join(' ') : '';
25054 node.removeAttribute("class");
25058 if (node.hasAttribute("lang")) {
25059 node.removeAttribute("lang");
25062 if (node.hasAttribute("style")) {
25064 var styles = node.getAttribute("style").split(";");
25066 Roo.each(styles, function(s) {
25067 if (!s.match(/:/)) {
25070 var kv = s.split(":");
25071 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25074 // what ever is left... we allow.
25077 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25078 if (!nstyle.length) {
25079 node.removeAttribute('style');
25082 this.iterateChildren(node, this.cleanWord);
25088 * iterateChildren of a Node, calling fn each time, using this as the scole..
25089 * @param {DomNode} node node to iterate children of.
25090 * @param {Function} fn method of this class to call on each item.
25092 iterateChildren : function(node, fn)
25094 if (!node.childNodes.length) {
25097 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25098 fn.call(this, node.childNodes[i])
25104 * cleanTableWidths.
25106 * Quite often pasting from word etc.. results in tables with column and widths.
25107 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25110 cleanTableWidths : function(node)
25115 this.cleanTableWidths(this.doc.body);
25120 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25123 Roo.log(node.tagName);
25124 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25125 this.iterateChildren(node, this.cleanTableWidths);
25128 if (node.hasAttribute('width')) {
25129 node.removeAttribute('width');
25133 if (node.hasAttribute("style")) {
25136 var styles = node.getAttribute("style").split(";");
25138 Roo.each(styles, function(s) {
25139 if (!s.match(/:/)) {
25142 var kv = s.split(":");
25143 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25146 // what ever is left... we allow.
25149 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25150 if (!nstyle.length) {
25151 node.removeAttribute('style');
25155 this.iterateChildren(node, this.cleanTableWidths);
25163 domToHTML : function(currentElement, depth, nopadtext) {
25165 depth = depth || 0;
25166 nopadtext = nopadtext || false;
25168 if (!currentElement) {
25169 return this.domToHTML(this.doc.body);
25172 //Roo.log(currentElement);
25174 var allText = false;
25175 var nodeName = currentElement.nodeName;
25176 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25178 if (nodeName == '#text') {
25180 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25185 if (nodeName != 'BODY') {
25188 // Prints the node tagName, such as <A>, <IMG>, etc
25191 for(i = 0; i < currentElement.attributes.length;i++) {
25193 var aname = currentElement.attributes.item(i).name;
25194 if (!currentElement.attributes.item(i).value.length) {
25197 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25200 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25209 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25212 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25217 // Traverse the tree
25219 var currentElementChild = currentElement.childNodes.item(i);
25220 var allText = true;
25221 var innerHTML = '';
25223 while (currentElementChild) {
25224 // Formatting code (indent the tree so it looks nice on the screen)
25225 var nopad = nopadtext;
25226 if (lastnode == 'SPAN') {
25230 if (currentElementChild.nodeName == '#text') {
25231 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25232 toadd = nopadtext ? toadd : toadd.trim();
25233 if (!nopad && toadd.length > 80) {
25234 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25236 innerHTML += toadd;
25239 currentElementChild = currentElement.childNodes.item(i);
25245 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25247 // Recursively traverse the tree structure of the child node
25248 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25249 lastnode = currentElementChild.nodeName;
25251 currentElementChild=currentElement.childNodes.item(i);
25257 // The remaining code is mostly for formatting the tree
25258 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25263 ret+= "</"+tagName+">";
25269 applyBlacklists : function()
25271 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25272 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25276 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25277 if (b.indexOf(tag) > -1) {
25280 this.white.push(tag);
25284 Roo.each(w, function(tag) {
25285 if (b.indexOf(tag) > -1) {
25288 if (this.white.indexOf(tag) > -1) {
25291 this.white.push(tag);
25296 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25297 if (w.indexOf(tag) > -1) {
25300 this.black.push(tag);
25304 Roo.each(b, function(tag) {
25305 if (w.indexOf(tag) > -1) {
25308 if (this.black.indexOf(tag) > -1) {
25311 this.black.push(tag);
25316 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25317 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25321 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25322 if (b.indexOf(tag) > -1) {
25325 this.cwhite.push(tag);
25329 Roo.each(w, function(tag) {
25330 if (b.indexOf(tag) > -1) {
25333 if (this.cwhite.indexOf(tag) > -1) {
25336 this.cwhite.push(tag);
25341 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25342 if (w.indexOf(tag) > -1) {
25345 this.cblack.push(tag);
25349 Roo.each(b, function(tag) {
25350 if (w.indexOf(tag) > -1) {
25353 if (this.cblack.indexOf(tag) > -1) {
25356 this.cblack.push(tag);
25361 setStylesheets : function(stylesheets)
25363 if(typeof(stylesheets) == 'string'){
25364 Roo.get(this.iframe.contentDocument.head).createChild({
25366 rel : 'stylesheet',
25375 Roo.each(stylesheets, function(s) {
25380 Roo.get(_this.iframe.contentDocument.head).createChild({
25382 rel : 'stylesheet',
25391 removeStylesheets : function()
25395 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25400 setStyle : function(style)
25402 Roo.get(this.iframe.contentDocument.head).createChild({
25411 // hide stuff that is not compatible
25425 * @event specialkey
25429 * @cfg {String} fieldClass @hide
25432 * @cfg {String} focusClass @hide
25435 * @cfg {String} autoCreate @hide
25438 * @cfg {String} inputType @hide
25441 * @cfg {String} invalidClass @hide
25444 * @cfg {String} invalidText @hide
25447 * @cfg {String} msgFx @hide
25450 * @cfg {String} validateOnBlur @hide
25454 Roo.HtmlEditorCore.white = [
25455 'area', 'br', 'img', 'input', 'hr', 'wbr',
25457 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25458 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25459 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25460 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25461 'table', 'ul', 'xmp',
25463 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25466 'dir', 'menu', 'ol', 'ul', 'dl',
25472 Roo.HtmlEditorCore.black = [
25473 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25475 'base', 'basefont', 'bgsound', 'blink', 'body',
25476 'frame', 'frameset', 'head', 'html', 'ilayer',
25477 'iframe', 'layer', 'link', 'meta', 'object',
25478 'script', 'style' ,'title', 'xml' // clean later..
25480 Roo.HtmlEditorCore.clean = [
25481 'script', 'style', 'title', 'xml'
25483 Roo.HtmlEditorCore.remove = [
25488 Roo.HtmlEditorCore.ablack = [
25492 Roo.HtmlEditorCore.aclean = [
25493 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25497 Roo.HtmlEditorCore.pwhite= [
25498 'http', 'https', 'mailto'
25501 // white listed style attributes.
25502 Roo.HtmlEditorCore.cwhite= [
25503 // 'text-align', /// default is to allow most things..
25509 // black listed style attributes.
25510 Roo.HtmlEditorCore.cblack= [
25511 // 'font-size' -- this can be set by the project
25515 Roo.HtmlEditorCore.swapCodes =[
25534 * @class Roo.bootstrap.HtmlEditor
25535 * @extends Roo.bootstrap.TextArea
25536 * Bootstrap HtmlEditor class
25539 * Create a new HtmlEditor
25540 * @param {Object} config The config object
25543 Roo.bootstrap.HtmlEditor = function(config){
25544 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25545 if (!this.toolbars) {
25546 this.toolbars = [];
25549 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25552 * @event initialize
25553 * Fires when the editor is fully initialized (including the iframe)
25554 * @param {HtmlEditor} this
25559 * Fires when the editor is first receives the focus. Any insertion must wait
25560 * until after this event.
25561 * @param {HtmlEditor} this
25565 * @event beforesync
25566 * Fires before the textarea is updated with content from the editor iframe. Return false
25567 * to cancel the sync.
25568 * @param {HtmlEditor} this
25569 * @param {String} html
25573 * @event beforepush
25574 * Fires before the iframe editor is updated with content from the textarea. Return false
25575 * to cancel the push.
25576 * @param {HtmlEditor} this
25577 * @param {String} html
25582 * Fires when the textarea is updated with content from the editor iframe.
25583 * @param {HtmlEditor} this
25584 * @param {String} html
25589 * Fires when the iframe editor is updated with content from the textarea.
25590 * @param {HtmlEditor} this
25591 * @param {String} html
25595 * @event editmodechange
25596 * Fires when the editor switches edit modes
25597 * @param {HtmlEditor} this
25598 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25600 editmodechange: true,
25602 * @event editorevent
25603 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25604 * @param {HtmlEditor} this
25608 * @event firstfocus
25609 * Fires when on first focus - needed by toolbars..
25610 * @param {HtmlEditor} this
25615 * Auto save the htmlEditor value as a file into Events
25616 * @param {HtmlEditor} this
25620 * @event savedpreview
25621 * preview the saved version of htmlEditor
25622 * @param {HtmlEditor} this
25629 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25633 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25638 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25643 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25648 * @cfg {Number} height (in pixels)
25652 * @cfg {Number} width (in pixels)
25657 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25660 stylesheets: false,
25665 // private properties
25666 validationEvent : false,
25668 initialized : false,
25671 onFocus : Roo.emptyFn,
25673 hideMode:'offsets',
25675 tbContainer : false,
25679 toolbarContainer :function() {
25680 return this.wrap.select('.x-html-editor-tb',true).first();
25684 * Protected method that will not generally be called directly. It
25685 * is called when the editor creates its toolbar. Override this method if you need to
25686 * add custom toolbar buttons.
25687 * @param {HtmlEditor} editor
25689 createToolbar : function(){
25690 Roo.log('renewing');
25691 Roo.log("create toolbars");
25693 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25694 this.toolbars[0].render(this.toolbarContainer());
25698 // if (!editor.toolbars || !editor.toolbars.length) {
25699 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25702 // for (var i =0 ; i < editor.toolbars.length;i++) {
25703 // editor.toolbars[i] = Roo.factory(
25704 // typeof(editor.toolbars[i]) == 'string' ?
25705 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25706 // Roo.bootstrap.HtmlEditor);
25707 // editor.toolbars[i].init(editor);
25713 onRender : function(ct, position)
25715 // Roo.log("Call onRender: " + this.xtype);
25717 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25719 this.wrap = this.inputEl().wrap({
25720 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25723 this.editorcore.onRender(ct, position);
25725 if (this.resizable) {
25726 this.resizeEl = new Roo.Resizable(this.wrap, {
25730 minHeight : this.height,
25731 height: this.height,
25732 handles : this.resizable,
25735 resize : function(r, w, h) {
25736 _t.onResize(w,h); // -something
25742 this.createToolbar(this);
25745 if(!this.width && this.resizable){
25746 this.setSize(this.wrap.getSize());
25748 if (this.resizeEl) {
25749 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25750 // should trigger onReize..
25756 onResize : function(w, h)
25758 Roo.log('resize: ' +w + ',' + h );
25759 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25763 if(this.inputEl() ){
25764 if(typeof w == 'number'){
25765 var aw = w - this.wrap.getFrameWidth('lr');
25766 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25769 if(typeof h == 'number'){
25770 var tbh = -11; // fixme it needs to tool bar size!
25771 for (var i =0; i < this.toolbars.length;i++) {
25772 // fixme - ask toolbars for heights?
25773 tbh += this.toolbars[i].el.getHeight();
25774 //if (this.toolbars[i].footer) {
25775 // tbh += this.toolbars[i].footer.el.getHeight();
25783 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25784 ah -= 5; // knock a few pixes off for look..
25785 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25789 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25790 this.editorcore.onResize(ew,eh);
25795 * Toggles the editor between standard and source edit mode.
25796 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25798 toggleSourceEdit : function(sourceEditMode)
25800 this.editorcore.toggleSourceEdit(sourceEditMode);
25802 if(this.editorcore.sourceEditMode){
25803 Roo.log('editor - showing textarea');
25806 // Roo.log(this.syncValue());
25808 this.inputEl().removeClass(['hide', 'x-hidden']);
25809 this.inputEl().dom.removeAttribute('tabIndex');
25810 this.inputEl().focus();
25812 Roo.log('editor - hiding textarea');
25814 // Roo.log(this.pushValue());
25817 this.inputEl().addClass(['hide', 'x-hidden']);
25818 this.inputEl().dom.setAttribute('tabIndex', -1);
25819 //this.deferFocus();
25822 if(this.resizable){
25823 this.setSize(this.wrap.getSize());
25826 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25829 // private (for BoxComponent)
25830 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25832 // private (for BoxComponent)
25833 getResizeEl : function(){
25837 // private (for BoxComponent)
25838 getPositionEl : function(){
25843 initEvents : function(){
25844 this.originalValue = this.getValue();
25848 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25851 // markInvalid : Roo.emptyFn,
25853 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25856 // clearInvalid : Roo.emptyFn,
25858 setValue : function(v){
25859 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25860 this.editorcore.pushValue();
25865 deferFocus : function(){
25866 this.focus.defer(10, this);
25870 focus : function(){
25871 this.editorcore.focus();
25877 onDestroy : function(){
25883 for (var i =0; i < this.toolbars.length;i++) {
25884 // fixme - ask toolbars for heights?
25885 this.toolbars[i].onDestroy();
25888 this.wrap.dom.innerHTML = '';
25889 this.wrap.remove();
25894 onFirstFocus : function(){
25895 //Roo.log("onFirstFocus");
25896 this.editorcore.onFirstFocus();
25897 for (var i =0; i < this.toolbars.length;i++) {
25898 this.toolbars[i].onFirstFocus();
25904 syncValue : function()
25906 this.editorcore.syncValue();
25909 pushValue : function()
25911 this.editorcore.pushValue();
25915 // hide stuff that is not compatible
25929 * @event specialkey
25933 * @cfg {String} fieldClass @hide
25936 * @cfg {String} focusClass @hide
25939 * @cfg {String} autoCreate @hide
25942 * @cfg {String} inputType @hide
25946 * @cfg {String} invalidText @hide
25949 * @cfg {String} msgFx @hide
25952 * @cfg {String} validateOnBlur @hide
25961 Roo.namespace('Roo.bootstrap.htmleditor');
25963 * @class Roo.bootstrap.HtmlEditorToolbar1
25969 new Roo.bootstrap.HtmlEditor({
25972 new Roo.bootstrap.HtmlEditorToolbar1({
25973 disable : { fonts: 1 , format: 1, ..., ... , ...],
25979 * @cfg {Object} disable List of elements to disable..
25980 * @cfg {Array} btns List of additional buttons.
25984 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25987 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25990 Roo.apply(this, config);
25992 // default disabled, based on 'good practice'..
25993 this.disable = this.disable || {};
25994 Roo.applyIf(this.disable, {
25997 specialElements : true
25999 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26001 this.editor = config.editor;
26002 this.editorcore = config.editor.editorcore;
26004 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26006 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26007 // dont call parent... till later.
26009 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26014 editorcore : false,
26019 "h1","h2","h3","h4","h5","h6",
26021 "abbr", "acronym", "address", "cite", "samp", "var",
26025 onRender : function(ct, position)
26027 // Roo.log("Call onRender: " + this.xtype);
26029 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26031 this.el.dom.style.marginBottom = '0';
26033 var editorcore = this.editorcore;
26034 var editor= this.editor;
26037 var btn = function(id,cmd , toggle, handler, html){
26039 var event = toggle ? 'toggle' : 'click';
26044 xns: Roo.bootstrap,
26048 enableToggle:toggle !== false,
26050 pressed : toggle ? false : null,
26053 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26054 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26060 // var cb_box = function...
26065 xns: Roo.bootstrap,
26070 xns: Roo.bootstrap,
26074 Roo.each(this.formats, function(f) {
26075 style.menu.items.push({
26077 xns: Roo.bootstrap,
26078 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26083 editorcore.insertTag(this.tagname);
26090 children.push(style);
26092 btn('bold',false,true);
26093 btn('italic',false,true);
26094 btn('align-left', 'justifyleft',true);
26095 btn('align-center', 'justifycenter',true);
26096 btn('align-right' , 'justifyright',true);
26097 btn('link', false, false, function(btn) {
26098 //Roo.log("create link?");
26099 var url = prompt(this.createLinkText, this.defaultLinkValue);
26100 if(url && url != 'http:/'+'/'){
26101 this.editorcore.relayCmd('createlink', url);
26104 btn('list','insertunorderedlist',true);
26105 btn('pencil', false,true, function(btn){
26107 this.toggleSourceEdit(btn.pressed);
26110 if (this.editor.btns.length > 0) {
26111 for (var i = 0; i<this.editor.btns.length; i++) {
26112 children.push(this.editor.btns[i]);
26120 xns: Roo.bootstrap,
26125 xns: Roo.bootstrap,
26130 cog.menu.items.push({
26132 xns: Roo.bootstrap,
26133 html : Clean styles,
26138 editorcore.insertTag(this.tagname);
26147 this.xtype = 'NavSimplebar';
26149 for(var i=0;i< children.length;i++) {
26151 this.buttons.add(this.addxtypeChild(children[i]));
26155 editor.on('editorevent', this.updateToolbar, this);
26157 onBtnClick : function(id)
26159 this.editorcore.relayCmd(id);
26160 this.editorcore.focus();
26164 * Protected method that will not generally be called directly. It triggers
26165 * a toolbar update by reading the markup state of the current selection in the editor.
26167 updateToolbar: function(){
26169 if(!this.editorcore.activated){
26170 this.editor.onFirstFocus(); // is this neeed?
26174 var btns = this.buttons;
26175 var doc = this.editorcore.doc;
26176 btns.get('bold').setActive(doc.queryCommandState('bold'));
26177 btns.get('italic').setActive(doc.queryCommandState('italic'));
26178 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26180 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26181 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26182 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26184 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26185 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26188 var ans = this.editorcore.getAllAncestors();
26189 if (this.formatCombo) {
26192 var store = this.formatCombo.store;
26193 this.formatCombo.setValue("");
26194 for (var i =0; i < ans.length;i++) {
26195 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26197 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26205 // hides menus... - so this cant be on a menu...
26206 Roo.bootstrap.MenuMgr.hideAll();
26208 Roo.bootstrap.MenuMgr.hideAll();
26209 //this.editorsyncValue();
26211 onFirstFocus: function() {
26212 this.buttons.each(function(item){
26216 toggleSourceEdit : function(sourceEditMode){
26219 if(sourceEditMode){
26220 Roo.log("disabling buttons");
26221 this.buttons.each( function(item){
26222 if(item.cmd != 'pencil'){
26228 Roo.log("enabling buttons");
26229 if(this.editorcore.initialized){
26230 this.buttons.each( function(item){
26236 Roo.log("calling toggole on editor");
26237 // tell the editor that it's been pressed..
26238 this.editor.toggleSourceEdit(sourceEditMode);
26252 * @class Roo.bootstrap.Markdown
26253 * @extends Roo.bootstrap.TextArea
26254 * Bootstrap Showdown editable area
26255 * @cfg {string} content
26258 * Create a new Showdown
26261 Roo.bootstrap.Markdown = function(config){
26262 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26266 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26270 initEvents : function()
26273 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26274 this.markdownEl = this.el.createChild({
26275 cls : 'roo-markdown-area'
26277 this.inputEl().addClass('d-none');
26278 if (this.getValue() == '') {
26279 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26282 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26284 this.markdownEl.on('click', this.toggleTextEdit, this);
26285 this.on('blur', this.toggleTextEdit, this);
26286 this.on('specialkey', this.resizeTextArea, this);
26289 toggleTextEdit : function()
26291 var sh = this.markdownEl.getHeight();
26292 this.inputEl().addClass('d-none');
26293 this.markdownEl.addClass('d-none');
26294 if (!this.editing) {
26296 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26297 this.inputEl().removeClass('d-none');
26298 this.inputEl().focus();
26299 this.editing = true;
26302 // show showdown...
26303 this.updateMarkdown();
26304 this.markdownEl.removeClass('d-none');
26305 this.editing = false;
26308 updateMarkdown : function()
26310 if (this.getValue() == '') {
26311 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26315 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26318 resizeTextArea: function () {
26321 Roo.log([sh, this.getValue().split("\n").length * 30]);
26322 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26324 setValue : function(val)
26326 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26327 if (!this.editing) {
26328 this.updateMarkdown();
26334 if (!this.editing) {
26335 this.toggleTextEdit();
26343 * @class Roo.bootstrap.Table.AbstractSelectionModel
26344 * @extends Roo.util.Observable
26345 * Abstract base class for grid SelectionModels. It provides the interface that should be
26346 * implemented by descendant classes. This class should not be directly instantiated.
26349 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26350 this.locked = false;
26351 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26355 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26356 /** @ignore Called by the grid automatically. Do not call directly. */
26357 init : function(grid){
26363 * Locks the selections.
26366 this.locked = true;
26370 * Unlocks the selections.
26372 unlock : function(){
26373 this.locked = false;
26377 * Returns true if the selections are locked.
26378 * @return {Boolean}
26380 isLocked : function(){
26381 return this.locked;
26385 initEvents : function ()
26391 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26392 * @class Roo.bootstrap.Table.RowSelectionModel
26393 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26394 * It supports multiple selections and keyboard selection/navigation.
26396 * @param {Object} config
26399 Roo.bootstrap.Table.RowSelectionModel = function(config){
26400 Roo.apply(this, config);
26401 this.selections = new Roo.util.MixedCollection(false, function(o){
26406 this.lastActive = false;
26410 * @event selectionchange
26411 * Fires when the selection changes
26412 * @param {SelectionModel} this
26414 "selectionchange" : true,
26416 * @event afterselectionchange
26417 * Fires after the selection changes (eg. by key press or clicking)
26418 * @param {SelectionModel} this
26420 "afterselectionchange" : true,
26422 * @event beforerowselect
26423 * Fires when a row is selected being selected, return false to cancel.
26424 * @param {SelectionModel} this
26425 * @param {Number} rowIndex The selected index
26426 * @param {Boolean} keepExisting False if other selections will be cleared
26428 "beforerowselect" : true,
26431 * Fires when a row is selected.
26432 * @param {SelectionModel} this
26433 * @param {Number} rowIndex The selected index
26434 * @param {Roo.data.Record} r The record
26436 "rowselect" : true,
26438 * @event rowdeselect
26439 * Fires when a row is deselected.
26440 * @param {SelectionModel} this
26441 * @param {Number} rowIndex The selected index
26443 "rowdeselect" : true
26445 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26446 this.locked = false;
26449 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26451 * @cfg {Boolean} singleSelect
26452 * True to allow selection of only one row at a time (defaults to false)
26454 singleSelect : false,
26457 initEvents : function()
26460 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26461 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26462 //}else{ // allow click to work like normal
26463 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26465 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26466 this.grid.on("rowclick", this.handleMouseDown, this);
26468 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26469 "up" : function(e){
26471 this.selectPrevious(e.shiftKey);
26472 }else if(this.last !== false && this.lastActive !== false){
26473 var last = this.last;
26474 this.selectRange(this.last, this.lastActive-1);
26475 this.grid.getView().focusRow(this.lastActive);
26476 if(last !== false){
26480 this.selectFirstRow();
26482 this.fireEvent("afterselectionchange", this);
26484 "down" : function(e){
26486 this.selectNext(e.shiftKey);
26487 }else if(this.last !== false && this.lastActive !== false){
26488 var last = this.last;
26489 this.selectRange(this.last, this.lastActive+1);
26490 this.grid.getView().focusRow(this.lastActive);
26491 if(last !== false){
26495 this.selectFirstRow();
26497 this.fireEvent("afterselectionchange", this);
26501 this.grid.store.on('load', function(){
26502 this.selections.clear();
26505 var view = this.grid.view;
26506 view.on("refresh", this.onRefresh, this);
26507 view.on("rowupdated", this.onRowUpdated, this);
26508 view.on("rowremoved", this.onRemove, this);
26513 onRefresh : function()
26515 var ds = this.grid.store, i, v = this.grid.view;
26516 var s = this.selections;
26517 s.each(function(r){
26518 if((i = ds.indexOfId(r.id)) != -1){
26527 onRemove : function(v, index, r){
26528 this.selections.remove(r);
26532 onRowUpdated : function(v, index, r){
26533 if(this.isSelected(r)){
26534 v.onRowSelect(index);
26540 * @param {Array} records The records to select
26541 * @param {Boolean} keepExisting (optional) True to keep existing selections
26543 selectRecords : function(records, keepExisting)
26546 this.clearSelections();
26548 var ds = this.grid.store;
26549 for(var i = 0, len = records.length; i < len; i++){
26550 this.selectRow(ds.indexOf(records[i]), true);
26555 * Gets the number of selected rows.
26558 getCount : function(){
26559 return this.selections.length;
26563 * Selects the first row in the grid.
26565 selectFirstRow : function(){
26570 * Select the last row.
26571 * @param {Boolean} keepExisting (optional) True to keep existing selections
26573 selectLastRow : function(keepExisting){
26574 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26575 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26579 * Selects the row immediately following the last selected row.
26580 * @param {Boolean} keepExisting (optional) True to keep existing selections
26582 selectNext : function(keepExisting)
26584 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26585 this.selectRow(this.last+1, keepExisting);
26586 this.grid.getView().focusRow(this.last);
26591 * Selects the row that precedes the last selected row.
26592 * @param {Boolean} keepExisting (optional) True to keep existing selections
26594 selectPrevious : function(keepExisting){
26596 this.selectRow(this.last-1, keepExisting);
26597 this.grid.getView().focusRow(this.last);
26602 * Returns the selected records
26603 * @return {Array} Array of selected records
26605 getSelections : function(){
26606 return [].concat(this.selections.items);
26610 * Returns the first selected record.
26613 getSelected : function(){
26614 return this.selections.itemAt(0);
26619 * Clears all selections.
26621 clearSelections : function(fast)
26627 var ds = this.grid.store;
26628 var s = this.selections;
26629 s.each(function(r){
26630 this.deselectRow(ds.indexOfId(r.id));
26634 this.selections.clear();
26641 * Selects all rows.
26643 selectAll : function(){
26647 this.selections.clear();
26648 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26649 this.selectRow(i, true);
26654 * Returns True if there is a selection.
26655 * @return {Boolean}
26657 hasSelection : function(){
26658 return this.selections.length > 0;
26662 * Returns True if the specified row is selected.
26663 * @param {Number/Record} record The record or index of the record to check
26664 * @return {Boolean}
26666 isSelected : function(index){
26667 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26668 return (r && this.selections.key(r.id) ? true : false);
26672 * Returns True if the specified record id is selected.
26673 * @param {String} id The id of record to check
26674 * @return {Boolean}
26676 isIdSelected : function(id){
26677 return (this.selections.key(id) ? true : false);
26682 handleMouseDBClick : function(e, t){
26686 handleMouseDown : function(e, t)
26688 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26689 if(this.isLocked() || rowIndex < 0 ){
26692 if(e.shiftKey && this.last !== false){
26693 var last = this.last;
26694 this.selectRange(last, rowIndex, e.ctrlKey);
26695 this.last = last; // reset the last
26699 var isSelected = this.isSelected(rowIndex);
26700 //Roo.log("select row:" + rowIndex);
26702 this.deselectRow(rowIndex);
26704 this.selectRow(rowIndex, true);
26708 if(e.button !== 0 && isSelected){
26709 alert('rowIndex 2: ' + rowIndex);
26710 view.focusRow(rowIndex);
26711 }else if(e.ctrlKey && isSelected){
26712 this.deselectRow(rowIndex);
26713 }else if(!isSelected){
26714 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26715 view.focusRow(rowIndex);
26719 this.fireEvent("afterselectionchange", this);
26722 handleDragableRowClick : function(grid, rowIndex, e)
26724 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26725 this.selectRow(rowIndex, false);
26726 grid.view.focusRow(rowIndex);
26727 this.fireEvent("afterselectionchange", this);
26732 * Selects multiple rows.
26733 * @param {Array} rows Array of the indexes of the row to select
26734 * @param {Boolean} keepExisting (optional) True to keep existing selections
26736 selectRows : function(rows, keepExisting){
26738 this.clearSelections();
26740 for(var i = 0, len = rows.length; i < len; i++){
26741 this.selectRow(rows[i], true);
26746 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26747 * @param {Number} startRow The index of the first row in the range
26748 * @param {Number} endRow The index of the last row in the range
26749 * @param {Boolean} keepExisting (optional) True to retain existing selections
26751 selectRange : function(startRow, endRow, keepExisting){
26756 this.clearSelections();
26758 if(startRow <= endRow){
26759 for(var i = startRow; i <= endRow; i++){
26760 this.selectRow(i, true);
26763 for(var i = startRow; i >= endRow; i--){
26764 this.selectRow(i, true);
26770 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26771 * @param {Number} startRow The index of the first row in the range
26772 * @param {Number} endRow The index of the last row in the range
26774 deselectRange : function(startRow, endRow, preventViewNotify){
26778 for(var i = startRow; i <= endRow; i++){
26779 this.deselectRow(i, preventViewNotify);
26785 * @param {Number} row The index of the row to select
26786 * @param {Boolean} keepExisting (optional) True to keep existing selections
26788 selectRow : function(index, keepExisting, preventViewNotify)
26790 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26793 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26794 if(!keepExisting || this.singleSelect){
26795 this.clearSelections();
26798 var r = this.grid.store.getAt(index);
26799 //console.log('selectRow - record id :' + r.id);
26801 this.selections.add(r);
26802 this.last = this.lastActive = index;
26803 if(!preventViewNotify){
26804 var proxy = new Roo.Element(
26805 this.grid.getRowDom(index)
26807 proxy.addClass('bg-info info');
26809 this.fireEvent("rowselect", this, index, r);
26810 this.fireEvent("selectionchange", this);
26816 * @param {Number} row The index of the row to deselect
26818 deselectRow : function(index, preventViewNotify)
26823 if(this.last == index){
26826 if(this.lastActive == index){
26827 this.lastActive = false;
26830 var r = this.grid.store.getAt(index);
26835 this.selections.remove(r);
26836 //.console.log('deselectRow - record id :' + r.id);
26837 if(!preventViewNotify){
26839 var proxy = new Roo.Element(
26840 this.grid.getRowDom(index)
26842 proxy.removeClass('bg-info info');
26844 this.fireEvent("rowdeselect", this, index);
26845 this.fireEvent("selectionchange", this);
26849 restoreLast : function(){
26851 this.last = this._last;
26856 acceptsNav : function(row, col, cm){
26857 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26861 onEditorKey : function(field, e){
26862 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26867 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26869 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26871 }else if(k == e.ENTER && !e.ctrlKey){
26875 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26877 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26879 }else if(k == e.ESC){
26883 g.startEditing(newCell[0], newCell[1]);
26889 * Ext JS Library 1.1.1
26890 * Copyright(c) 2006-2007, Ext JS, LLC.
26892 * Originally Released Under LGPL - original licence link has changed is not relivant.
26895 * <script type="text/javascript">
26899 * @class Roo.bootstrap.PagingToolbar
26900 * @extends Roo.bootstrap.NavSimplebar
26901 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26903 * Create a new PagingToolbar
26904 * @param {Object} config The config object
26905 * @param {Roo.data.Store} store
26907 Roo.bootstrap.PagingToolbar = function(config)
26909 // old args format still supported... - xtype is prefered..
26910 // created from xtype...
26912 this.ds = config.dataSource;
26914 if (config.store && !this.ds) {
26915 this.store= Roo.factory(config.store, Roo.data);
26916 this.ds = this.store;
26917 this.ds.xmodule = this.xmodule || false;
26920 this.toolbarItems = [];
26921 if (config.items) {
26922 this.toolbarItems = config.items;
26925 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26930 this.bind(this.ds);
26933 if (Roo.bootstrap.version == 4) {
26934 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26936 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26941 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26943 * @cfg {Roo.data.Store} dataSource
26944 * The underlying data store providing the paged data
26947 * @cfg {String/HTMLElement/Element} container
26948 * container The id or element that will contain the toolbar
26951 * @cfg {Boolean} displayInfo
26952 * True to display the displayMsg (defaults to false)
26955 * @cfg {Number} pageSize
26956 * The number of records to display per page (defaults to 20)
26960 * @cfg {String} displayMsg
26961 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26963 displayMsg : 'Displaying {0} - {1} of {2}',
26965 * @cfg {String} emptyMsg
26966 * The message to display when no records are found (defaults to "No data to display")
26968 emptyMsg : 'No data to display',
26970 * Customizable piece of the default paging text (defaults to "Page")
26973 beforePageText : "Page",
26975 * Customizable piece of the default paging text (defaults to "of %0")
26978 afterPageText : "of {0}",
26980 * Customizable piece of the default paging text (defaults to "First Page")
26983 firstText : "First Page",
26985 * Customizable piece of the default paging text (defaults to "Previous Page")
26988 prevText : "Previous Page",
26990 * Customizable piece of the default paging text (defaults to "Next Page")
26993 nextText : "Next Page",
26995 * Customizable piece of the default paging text (defaults to "Last Page")
26998 lastText : "Last Page",
27000 * Customizable piece of the default paging text (defaults to "Refresh")
27003 refreshText : "Refresh",
27007 onRender : function(ct, position)
27009 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27010 this.navgroup.parentId = this.id;
27011 this.navgroup.onRender(this.el, null);
27012 // add the buttons to the navgroup
27014 if(this.displayInfo){
27015 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27016 this.displayEl = this.el.select('.x-paging-info', true).first();
27017 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27018 // this.displayEl = navel.el.select('span',true).first();
27024 Roo.each(_this.buttons, function(e){ // this might need to use render????
27025 Roo.factory(e).render(_this.el);
27029 Roo.each(_this.toolbarItems, function(e) {
27030 _this.navgroup.addItem(e);
27034 this.first = this.navgroup.addItem({
27035 tooltip: this.firstText,
27036 cls: "prev btn-outline-secondary",
27037 html : ' <i class="fa fa-step-backward"></i>',
27039 preventDefault: true,
27040 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27043 this.prev = this.navgroup.addItem({
27044 tooltip: this.prevText,
27045 cls: "prev btn-outline-secondary",
27046 html : ' <i class="fa fa-backward"></i>',
27048 preventDefault: true,
27049 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27051 //this.addSeparator();
27054 var field = this.navgroup.addItem( {
27056 cls : 'x-paging-position btn-outline-secondary',
27058 html : this.beforePageText +
27059 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27060 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27063 this.field = field.el.select('input', true).first();
27064 this.field.on("keydown", this.onPagingKeydown, this);
27065 this.field.on("focus", function(){this.dom.select();});
27068 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27069 //this.field.setHeight(18);
27070 //this.addSeparator();
27071 this.next = this.navgroup.addItem({
27072 tooltip: this.nextText,
27073 cls: "next btn-outline-secondary",
27074 html : ' <i class="fa fa-forward"></i>',
27076 preventDefault: true,
27077 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27079 this.last = this.navgroup.addItem({
27080 tooltip: this.lastText,
27081 html : ' <i class="fa fa-step-forward"></i>',
27082 cls: "next btn-outline-secondary",
27084 preventDefault: true,
27085 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27087 //this.addSeparator();
27088 this.loading = this.navgroup.addItem({
27089 tooltip: this.refreshText,
27090 cls: "btn-outline-secondary",
27091 html : ' <i class="fa fa-refresh"></i>',
27092 preventDefault: true,
27093 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27099 updateInfo : function(){
27100 if(this.displayEl){
27101 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27102 var msg = count == 0 ?
27106 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27108 this.displayEl.update(msg);
27113 onLoad : function(ds, r, o)
27115 this.cursor = o.params.start ? o.params.start : 0;
27117 var d = this.getPageData(),
27122 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27123 this.field.dom.value = ap;
27124 this.first.setDisabled(ap == 1);
27125 this.prev.setDisabled(ap == 1);
27126 this.next.setDisabled(ap == ps);
27127 this.last.setDisabled(ap == ps);
27128 this.loading.enable();
27133 getPageData : function(){
27134 var total = this.ds.getTotalCount();
27137 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27138 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27143 onLoadError : function(){
27144 this.loading.enable();
27148 onPagingKeydown : function(e){
27149 var k = e.getKey();
27150 var d = this.getPageData();
27152 var v = this.field.dom.value, pageNum;
27153 if(!v || isNaN(pageNum = parseInt(v, 10))){
27154 this.field.dom.value = d.activePage;
27157 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27158 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27161 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))
27163 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27164 this.field.dom.value = pageNum;
27165 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27168 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27170 var v = this.field.dom.value, pageNum;
27171 var increment = (e.shiftKey) ? 10 : 1;
27172 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27175 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27176 this.field.dom.value = d.activePage;
27179 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27181 this.field.dom.value = parseInt(v, 10) + increment;
27182 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27183 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27190 beforeLoad : function(){
27192 this.loading.disable();
27197 onClick : function(which){
27206 ds.load({params:{start: 0, limit: this.pageSize}});
27209 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27212 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27215 var total = ds.getTotalCount();
27216 var extra = total % this.pageSize;
27217 var lastStart = extra ? (total - extra) : total-this.pageSize;
27218 ds.load({params:{start: lastStart, limit: this.pageSize}});
27221 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27227 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27228 * @param {Roo.data.Store} store The data store to unbind
27230 unbind : function(ds){
27231 ds.un("beforeload", this.beforeLoad, this);
27232 ds.un("load", this.onLoad, this);
27233 ds.un("loadexception", this.onLoadError, this);
27234 ds.un("remove", this.updateInfo, this);
27235 ds.un("add", this.updateInfo, this);
27236 this.ds = undefined;
27240 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27241 * @param {Roo.data.Store} store The data store to bind
27243 bind : function(ds){
27244 ds.on("beforeload", this.beforeLoad, this);
27245 ds.on("load", this.onLoad, this);
27246 ds.on("loadexception", this.onLoadError, this);
27247 ds.on("remove", this.updateInfo, this);
27248 ds.on("add", this.updateInfo, this);
27259 * @class Roo.bootstrap.MessageBar
27260 * @extends Roo.bootstrap.Component
27261 * Bootstrap MessageBar class
27262 * @cfg {String} html contents of the MessageBar
27263 * @cfg {String} weight (info | success | warning | danger) default info
27264 * @cfg {String} beforeClass insert the bar before the given class
27265 * @cfg {Boolean} closable (true | false) default false
27266 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27269 * Create a new Element
27270 * @param {Object} config The config object
27273 Roo.bootstrap.MessageBar = function(config){
27274 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27277 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27283 beforeClass: 'bootstrap-sticky-wrap',
27285 getAutoCreate : function(){
27289 cls: 'alert alert-dismissable alert-' + this.weight,
27294 html: this.html || ''
27300 cfg.cls += ' alert-messages-fixed';
27314 onRender : function(ct, position)
27316 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27319 var cfg = Roo.apply({}, this.getAutoCreate());
27323 cfg.cls += ' ' + this.cls;
27326 cfg.style = this.style;
27328 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27330 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27333 this.el.select('>button.close').on('click', this.hide, this);
27339 if (!this.rendered) {
27345 this.fireEvent('show', this);
27351 if (!this.rendered) {
27357 this.fireEvent('hide', this);
27360 update : function()
27362 // var e = this.el.dom.firstChild;
27364 // if(this.closable){
27365 // e = e.nextSibling;
27368 // e.data = this.html || '';
27370 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27386 * @class Roo.bootstrap.Graph
27387 * @extends Roo.bootstrap.Component
27388 * Bootstrap Graph class
27392 @cfg {String} graphtype bar | vbar | pie
27393 @cfg {number} g_x coodinator | centre x (pie)
27394 @cfg {number} g_y coodinator | centre y (pie)
27395 @cfg {number} g_r radius (pie)
27396 @cfg {number} g_height height of the chart (respected by all elements in the set)
27397 @cfg {number} g_width width of the chart (respected by all elements in the set)
27398 @cfg {Object} title The title of the chart
27401 -opts (object) options for the chart
27403 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27404 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27406 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.
27407 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27409 o stretch (boolean)
27411 -opts (object) options for the pie
27414 o startAngle (number)
27415 o endAngle (number)
27419 * Create a new Input
27420 * @param {Object} config The config object
27423 Roo.bootstrap.Graph = function(config){
27424 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27430 * The img click event for the img.
27431 * @param {Roo.EventObject} e
27437 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27448 //g_colors: this.colors,
27455 getAutoCreate : function(){
27466 onRender : function(ct,position){
27469 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27471 if (typeof(Raphael) == 'undefined') {
27472 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27476 this.raphael = Raphael(this.el.dom);
27478 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27479 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27480 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27481 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27483 r.text(160, 10, "Single Series Chart").attr(txtattr);
27484 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27485 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27486 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27488 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27489 r.barchart(330, 10, 300, 220, data1);
27490 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27491 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27494 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27495 // r.barchart(30, 30, 560, 250, xdata, {
27496 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27497 // axis : "0 0 1 1",
27498 // axisxlabels : xdata
27499 // //yvalues : cols,
27502 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27504 // this.load(null,xdata,{
27505 // axis : "0 0 1 1",
27506 // axisxlabels : xdata
27511 load : function(graphtype,xdata,opts)
27513 this.raphael.clear();
27515 graphtype = this.graphtype;
27520 var r = this.raphael,
27521 fin = function () {
27522 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27524 fout = function () {
27525 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27527 pfin = function() {
27528 this.sector.stop();
27529 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27532 this.label[0].stop();
27533 this.label[0].attr({ r: 7.5 });
27534 this.label[1].attr({ "font-weight": 800 });
27537 pfout = function() {
27538 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27541 this.label[0].animate({ r: 5 }, 500, "bounce");
27542 this.label[1].attr({ "font-weight": 400 });
27548 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27551 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27554 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27555 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27557 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27564 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27569 setTitle: function(o)
27574 initEvents: function() {
27577 this.el.on('click', this.onClick, this);
27581 onClick : function(e)
27583 Roo.log('img onclick');
27584 this.fireEvent('click', this, e);
27596 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27599 * @class Roo.bootstrap.dash.NumberBox
27600 * @extends Roo.bootstrap.Component
27601 * Bootstrap NumberBox class
27602 * @cfg {String} headline Box headline
27603 * @cfg {String} content Box content
27604 * @cfg {String} icon Box icon
27605 * @cfg {String} footer Footer text
27606 * @cfg {String} fhref Footer href
27609 * Create a new NumberBox
27610 * @param {Object} config The config object
27614 Roo.bootstrap.dash.NumberBox = function(config){
27615 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27619 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27628 getAutoCreate : function(){
27632 cls : 'small-box ',
27640 cls : 'roo-headline',
27641 html : this.headline
27645 cls : 'roo-content',
27646 html : this.content
27660 cls : 'ion ' + this.icon
27669 cls : 'small-box-footer',
27670 href : this.fhref || '#',
27674 cfg.cn.push(footer);
27681 onRender : function(ct,position){
27682 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27689 setHeadline: function (value)
27691 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27694 setFooter: function (value, href)
27696 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27699 this.el.select('a.small-box-footer',true).first().attr('href', href);
27704 setContent: function (value)
27706 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27709 initEvents: function()
27723 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27726 * @class Roo.bootstrap.dash.TabBox
27727 * @extends Roo.bootstrap.Component
27728 * Bootstrap TabBox class
27729 * @cfg {String} title Title of the TabBox
27730 * @cfg {String} icon Icon of the TabBox
27731 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27732 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27735 * Create a new TabBox
27736 * @param {Object} config The config object
27740 Roo.bootstrap.dash.TabBox = function(config){
27741 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27746 * When a pane is added
27747 * @param {Roo.bootstrap.dash.TabPane} pane
27751 * @event activatepane
27752 * When a pane is activated
27753 * @param {Roo.bootstrap.dash.TabPane} pane
27755 "activatepane" : true
27763 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27768 tabScrollable : false,
27770 getChildContainer : function()
27772 return this.el.select('.tab-content', true).first();
27775 getAutoCreate : function(){
27779 cls: 'pull-left header',
27787 cls: 'fa ' + this.icon
27793 cls: 'nav nav-tabs pull-right',
27799 if(this.tabScrollable){
27806 cls: 'nav nav-tabs pull-right',
27817 cls: 'nav-tabs-custom',
27822 cls: 'tab-content no-padding',
27830 initEvents : function()
27832 //Roo.log('add add pane handler');
27833 this.on('addpane', this.onAddPane, this);
27836 * Updates the box title
27837 * @param {String} html to set the title to.
27839 setTitle : function(value)
27841 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27843 onAddPane : function(pane)
27845 this.panes.push(pane);
27846 //Roo.log('addpane');
27848 // tabs are rendere left to right..
27849 if(!this.showtabs){
27853 var ctr = this.el.select('.nav-tabs', true).first();
27856 var existing = ctr.select('.nav-tab',true);
27857 var qty = existing.getCount();;
27860 var tab = ctr.createChild({
27862 cls : 'nav-tab' + (qty ? '' : ' active'),
27870 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27873 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27875 pane.el.addClass('active');
27880 onTabClick : function(ev,un,ob,pane)
27882 //Roo.log('tab - prev default');
27883 ev.preventDefault();
27886 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27887 pane.tab.addClass('active');
27888 //Roo.log(pane.title);
27889 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27890 // technically we should have a deactivate event.. but maybe add later.
27891 // and it should not de-activate the selected tab...
27892 this.fireEvent('activatepane', pane);
27893 pane.el.addClass('active');
27894 pane.fireEvent('activate');
27899 getActivePane : function()
27902 Roo.each(this.panes, function(p) {
27903 if(p.el.hasClass('active')){
27924 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27926 * @class Roo.bootstrap.TabPane
27927 * @extends Roo.bootstrap.Component
27928 * Bootstrap TabPane class
27929 * @cfg {Boolean} active (false | true) Default false
27930 * @cfg {String} title title of panel
27934 * Create a new TabPane
27935 * @param {Object} config The config object
27938 Roo.bootstrap.dash.TabPane = function(config){
27939 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27945 * When a pane is activated
27946 * @param {Roo.bootstrap.dash.TabPane} pane
27953 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27958 // the tabBox that this is attached to.
27961 getAutoCreate : function()
27969 cfg.cls += ' active';
27974 initEvents : function()
27976 //Roo.log('trigger add pane handler');
27977 this.parent().fireEvent('addpane', this)
27981 * Updates the tab title
27982 * @param {String} html to set the title to.
27984 setTitle: function(str)
27990 this.tab.select('a', true).first().dom.innerHTML = str;
28007 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28010 * @class Roo.bootstrap.menu.Menu
28011 * @extends Roo.bootstrap.Component
28012 * Bootstrap Menu class - container for Menu
28013 * @cfg {String} html Text of the menu
28014 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28015 * @cfg {String} icon Font awesome icon
28016 * @cfg {String} pos Menu align to (top | bottom) default bottom
28020 * Create a new Menu
28021 * @param {Object} config The config object
28025 Roo.bootstrap.menu.Menu = function(config){
28026 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28030 * @event beforeshow
28031 * Fires before this menu is displayed
28032 * @param {Roo.bootstrap.menu.Menu} this
28036 * @event beforehide
28037 * Fires before this menu is hidden
28038 * @param {Roo.bootstrap.menu.Menu} this
28043 * Fires after this menu is displayed
28044 * @param {Roo.bootstrap.menu.Menu} this
28049 * Fires after this menu is hidden
28050 * @param {Roo.bootstrap.menu.Menu} this
28055 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28056 * @param {Roo.bootstrap.menu.Menu} this
28057 * @param {Roo.EventObject} e
28064 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28068 weight : 'default',
28073 getChildContainer : function() {
28074 if(this.isSubMenu){
28078 return this.el.select('ul.dropdown-menu', true).first();
28081 getAutoCreate : function()
28086 cls : 'roo-menu-text',
28094 cls : 'fa ' + this.icon
28105 cls : 'dropdown-button btn btn-' + this.weight,
28110 cls : 'dropdown-toggle btn btn-' + this.weight,
28120 cls : 'dropdown-menu'
28126 if(this.pos == 'top'){
28127 cfg.cls += ' dropup';
28130 if(this.isSubMenu){
28133 cls : 'dropdown-menu'
28140 onRender : function(ct, position)
28142 this.isSubMenu = ct.hasClass('dropdown-submenu');
28144 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28147 initEvents : function()
28149 if(this.isSubMenu){
28153 this.hidden = true;
28155 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28156 this.triggerEl.on('click', this.onTriggerPress, this);
28158 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28159 this.buttonEl.on('click', this.onClick, this);
28165 if(this.isSubMenu){
28169 return this.el.select('ul.dropdown-menu', true).first();
28172 onClick : function(e)
28174 this.fireEvent("click", this, e);
28177 onTriggerPress : function(e)
28179 if (this.isVisible()) {
28186 isVisible : function(){
28187 return !this.hidden;
28192 this.fireEvent("beforeshow", this);
28194 this.hidden = false;
28195 this.el.addClass('open');
28197 Roo.get(document).on("mouseup", this.onMouseUp, this);
28199 this.fireEvent("show", this);
28206 this.fireEvent("beforehide", this);
28208 this.hidden = true;
28209 this.el.removeClass('open');
28211 Roo.get(document).un("mouseup", this.onMouseUp);
28213 this.fireEvent("hide", this);
28216 onMouseUp : function()
28230 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28233 * @class Roo.bootstrap.menu.Item
28234 * @extends Roo.bootstrap.Component
28235 * Bootstrap MenuItem class
28236 * @cfg {Boolean} submenu (true | false) default false
28237 * @cfg {String} html text of the item
28238 * @cfg {String} href the link
28239 * @cfg {Boolean} disable (true | false) default false
28240 * @cfg {Boolean} preventDefault (true | false) default true
28241 * @cfg {String} icon Font awesome icon
28242 * @cfg {String} pos Submenu align to (left | right) default right
28246 * Create a new Item
28247 * @param {Object} config The config object
28251 Roo.bootstrap.menu.Item = function(config){
28252 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28256 * Fires when the mouse is hovering over this menu
28257 * @param {Roo.bootstrap.menu.Item} this
28258 * @param {Roo.EventObject} e
28263 * Fires when the mouse exits this menu
28264 * @param {Roo.bootstrap.menu.Item} this
28265 * @param {Roo.EventObject} e
28271 * The raw click event for the entire grid.
28272 * @param {Roo.EventObject} e
28278 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28283 preventDefault: true,
28288 getAutoCreate : function()
28293 cls : 'roo-menu-item-text',
28301 cls : 'fa ' + this.icon
28310 href : this.href || '#',
28317 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28321 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28323 if(this.pos == 'left'){
28324 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28331 initEvents : function()
28333 this.el.on('mouseover', this.onMouseOver, this);
28334 this.el.on('mouseout', this.onMouseOut, this);
28336 this.el.select('a', true).first().on('click', this.onClick, this);
28340 onClick : function(e)
28342 if(this.preventDefault){
28343 e.preventDefault();
28346 this.fireEvent("click", this, e);
28349 onMouseOver : function(e)
28351 if(this.submenu && this.pos == 'left'){
28352 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28355 this.fireEvent("mouseover", this, e);
28358 onMouseOut : function(e)
28360 this.fireEvent("mouseout", this, e);
28372 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28375 * @class Roo.bootstrap.menu.Separator
28376 * @extends Roo.bootstrap.Component
28377 * Bootstrap Separator class
28380 * Create a new Separator
28381 * @param {Object} config The config object
28385 Roo.bootstrap.menu.Separator = function(config){
28386 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28389 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28391 getAutoCreate : function(){
28412 * @class Roo.bootstrap.Tooltip
28413 * Bootstrap Tooltip class
28414 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28415 * to determine which dom element triggers the tooltip.
28417 * It needs to add support for additional attributes like tooltip-position
28420 * Create a new Toolti
28421 * @param {Object} config The config object
28424 Roo.bootstrap.Tooltip = function(config){
28425 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28427 this.alignment = Roo.bootstrap.Tooltip.alignment;
28429 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28430 this.alignment = config.alignment;
28435 Roo.apply(Roo.bootstrap.Tooltip, {
28437 * @function init initialize tooltip monitoring.
28441 currentTip : false,
28442 currentRegion : false,
28448 Roo.get(document).on('mouseover', this.enter ,this);
28449 Roo.get(document).on('mouseout', this.leave, this);
28452 this.currentTip = new Roo.bootstrap.Tooltip();
28455 enter : function(ev)
28457 var dom = ev.getTarget();
28459 //Roo.log(['enter',dom]);
28460 var el = Roo.fly(dom);
28461 if (this.currentEl) {
28463 //Roo.log(this.currentEl);
28464 //Roo.log(this.currentEl.contains(dom));
28465 if (this.currentEl == el) {
28468 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28474 if (this.currentTip.el) {
28475 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28479 if(!el || el.dom == document){
28485 // you can not look for children, as if el is the body.. then everythign is the child..
28486 if (!el.attr('tooltip')) { //
28487 if (!el.select("[tooltip]").elements.length) {
28490 // is the mouse over this child...?
28491 bindEl = el.select("[tooltip]").first();
28492 var xy = ev.getXY();
28493 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28494 //Roo.log("not in region.");
28497 //Roo.log("child element over..");
28500 this.currentEl = bindEl;
28501 this.currentTip.bind(bindEl);
28502 this.currentRegion = Roo.lib.Region.getRegion(dom);
28503 this.currentTip.enter();
28506 leave : function(ev)
28508 var dom = ev.getTarget();
28509 //Roo.log(['leave',dom]);
28510 if (!this.currentEl) {
28515 if (dom != this.currentEl.dom) {
28518 var xy = ev.getXY();
28519 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28522 // only activate leave if mouse cursor is outside... bounding box..
28527 if (this.currentTip) {
28528 this.currentTip.leave();
28530 //Roo.log('clear currentEl');
28531 this.currentEl = false;
28536 'left' : ['r-l', [-2,0], 'right'],
28537 'right' : ['l-r', [2,0], 'left'],
28538 'bottom' : ['t-b', [0,2], 'top'],
28539 'top' : [ 'b-t', [0,-2], 'bottom']
28545 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28550 delay : null, // can be { show : 300 , hide: 500}
28554 hoverState : null, //???
28556 placement : 'bottom',
28560 getAutoCreate : function(){
28567 cls : 'tooltip-arrow arrow'
28570 cls : 'tooltip-inner'
28577 bind : function(el)
28582 initEvents : function()
28584 this.arrowEl = this.el.select('.arrow', true).first();
28585 this.innerEl = this.el.select('.tooltip-inner', true).first();
28588 enter : function () {
28590 if (this.timeout != null) {
28591 clearTimeout(this.timeout);
28594 this.hoverState = 'in';
28595 //Roo.log("enter - show");
28596 if (!this.delay || !this.delay.show) {
28601 this.timeout = setTimeout(function () {
28602 if (_t.hoverState == 'in') {
28605 }, this.delay.show);
28609 clearTimeout(this.timeout);
28611 this.hoverState = 'out';
28612 if (!this.delay || !this.delay.hide) {
28618 this.timeout = setTimeout(function () {
28619 //Roo.log("leave - timeout");
28621 if (_t.hoverState == 'out') {
28623 Roo.bootstrap.Tooltip.currentEl = false;
28628 show : function (msg)
28631 this.render(document.body);
28634 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28636 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28638 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28640 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28641 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28643 var placement = typeof this.placement == 'function' ?
28644 this.placement.call(this, this.el, on_el) :
28647 var autoToken = /\s?auto?\s?/i;
28648 var autoPlace = autoToken.test(placement);
28650 placement = placement.replace(autoToken, '') || 'top';
28654 //this.el.setXY([0,0]);
28656 //this.el.dom.style.display='block';
28658 //this.el.appendTo(on_el);
28660 var p = this.getPosition();
28661 var box = this.el.getBox();
28667 var align = this.alignment[placement];
28669 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28671 if(placement == 'top' || placement == 'bottom'){
28673 placement = 'right';
28676 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28677 placement = 'left';
28680 var scroll = Roo.select('body', true).first().getScroll();
28682 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28686 align = this.alignment[placement];
28688 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28692 this.el.alignTo(this.bindEl, align[0],align[1]);
28693 //var arrow = this.el.select('.arrow',true).first();
28694 //arrow.set(align[2],
28696 this.el.addClass(placement);
28697 this.el.addClass("bs-tooltip-"+ placement);
28699 this.el.addClass('in fade show');
28701 this.hoverState = null;
28703 if (this.el.hasClass('fade')) {
28718 //this.el.setXY([0,0]);
28719 this.el.removeClass(['show', 'in']);
28735 * @class Roo.bootstrap.LocationPicker
28736 * @extends Roo.bootstrap.Component
28737 * Bootstrap LocationPicker class
28738 * @cfg {Number} latitude Position when init default 0
28739 * @cfg {Number} longitude Position when init default 0
28740 * @cfg {Number} zoom default 15
28741 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28742 * @cfg {Boolean} mapTypeControl default false
28743 * @cfg {Boolean} disableDoubleClickZoom default false
28744 * @cfg {Boolean} scrollwheel default true
28745 * @cfg {Boolean} streetViewControl default false
28746 * @cfg {Number} radius default 0
28747 * @cfg {String} locationName
28748 * @cfg {Boolean} draggable default true
28749 * @cfg {Boolean} enableAutocomplete default false
28750 * @cfg {Boolean} enableReverseGeocode default true
28751 * @cfg {String} markerTitle
28754 * Create a new LocationPicker
28755 * @param {Object} config The config object
28759 Roo.bootstrap.LocationPicker = function(config){
28761 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28766 * Fires when the picker initialized.
28767 * @param {Roo.bootstrap.LocationPicker} this
28768 * @param {Google Location} location
28772 * @event positionchanged
28773 * Fires when the picker position changed.
28774 * @param {Roo.bootstrap.LocationPicker} this
28775 * @param {Google Location} location
28777 positionchanged : true,
28780 * Fires when the map resize.
28781 * @param {Roo.bootstrap.LocationPicker} this
28786 * Fires when the map show.
28787 * @param {Roo.bootstrap.LocationPicker} this
28792 * Fires when the map hide.
28793 * @param {Roo.bootstrap.LocationPicker} this
28798 * Fires when click the map.
28799 * @param {Roo.bootstrap.LocationPicker} this
28800 * @param {Map event} e
28804 * @event mapRightClick
28805 * Fires when right click the map.
28806 * @param {Roo.bootstrap.LocationPicker} this
28807 * @param {Map event} e
28809 mapRightClick : true,
28811 * @event markerClick
28812 * Fires when click the marker.
28813 * @param {Roo.bootstrap.LocationPicker} this
28814 * @param {Map event} e
28816 markerClick : true,
28818 * @event markerRightClick
28819 * Fires when right click the marker.
28820 * @param {Roo.bootstrap.LocationPicker} this
28821 * @param {Map event} e
28823 markerRightClick : true,
28825 * @event OverlayViewDraw
28826 * Fires when OverlayView Draw
28827 * @param {Roo.bootstrap.LocationPicker} this
28829 OverlayViewDraw : true,
28831 * @event OverlayViewOnAdd
28832 * Fires when OverlayView Draw
28833 * @param {Roo.bootstrap.LocationPicker} this
28835 OverlayViewOnAdd : true,
28837 * @event OverlayViewOnRemove
28838 * Fires when OverlayView Draw
28839 * @param {Roo.bootstrap.LocationPicker} this
28841 OverlayViewOnRemove : true,
28843 * @event OverlayViewShow
28844 * Fires when OverlayView Draw
28845 * @param {Roo.bootstrap.LocationPicker} this
28846 * @param {Pixel} cpx
28848 OverlayViewShow : true,
28850 * @event OverlayViewHide
28851 * Fires when OverlayView Draw
28852 * @param {Roo.bootstrap.LocationPicker} this
28854 OverlayViewHide : true,
28856 * @event loadexception
28857 * Fires when load google lib failed.
28858 * @param {Roo.bootstrap.LocationPicker} this
28860 loadexception : true
28865 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28867 gMapContext: false,
28873 mapTypeControl: false,
28874 disableDoubleClickZoom: false,
28876 streetViewControl: false,
28880 enableAutocomplete: false,
28881 enableReverseGeocode: true,
28884 getAutoCreate: function()
28889 cls: 'roo-location-picker'
28895 initEvents: function(ct, position)
28897 if(!this.el.getWidth() || this.isApplied()){
28901 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28906 initial: function()
28908 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28909 this.fireEvent('loadexception', this);
28913 if(!this.mapTypeId){
28914 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28917 this.gMapContext = this.GMapContext();
28919 this.initOverlayView();
28921 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28925 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28926 _this.setPosition(_this.gMapContext.marker.position);
28929 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28930 _this.fireEvent('mapClick', this, event);
28934 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28935 _this.fireEvent('mapRightClick', this, event);
28939 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28940 _this.fireEvent('markerClick', this, event);
28944 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28945 _this.fireEvent('markerRightClick', this, event);
28949 this.setPosition(this.gMapContext.location);
28951 this.fireEvent('initial', this, this.gMapContext.location);
28954 initOverlayView: function()
28958 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28962 _this.fireEvent('OverlayViewDraw', _this);
28967 _this.fireEvent('OverlayViewOnAdd', _this);
28970 onRemove: function()
28972 _this.fireEvent('OverlayViewOnRemove', _this);
28975 show: function(cpx)
28977 _this.fireEvent('OverlayViewShow', _this, cpx);
28982 _this.fireEvent('OverlayViewHide', _this);
28988 fromLatLngToContainerPixel: function(event)
28990 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28993 isApplied: function()
28995 return this.getGmapContext() == false ? false : true;
28998 getGmapContext: function()
29000 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29003 GMapContext: function()
29005 var position = new google.maps.LatLng(this.latitude, this.longitude);
29007 var _map = new google.maps.Map(this.el.dom, {
29010 mapTypeId: this.mapTypeId,
29011 mapTypeControl: this.mapTypeControl,
29012 disableDoubleClickZoom: this.disableDoubleClickZoom,
29013 scrollwheel: this.scrollwheel,
29014 streetViewControl: this.streetViewControl,
29015 locationName: this.locationName,
29016 draggable: this.draggable,
29017 enableAutocomplete: this.enableAutocomplete,
29018 enableReverseGeocode: this.enableReverseGeocode
29021 var _marker = new google.maps.Marker({
29022 position: position,
29024 title: this.markerTitle,
29025 draggable: this.draggable
29032 location: position,
29033 radius: this.radius,
29034 locationName: this.locationName,
29035 addressComponents: {
29036 formatted_address: null,
29037 addressLine1: null,
29038 addressLine2: null,
29040 streetNumber: null,
29044 stateOrProvince: null
29047 domContainer: this.el.dom,
29048 geodecoder: new google.maps.Geocoder()
29052 drawCircle: function(center, radius, options)
29054 if (this.gMapContext.circle != null) {
29055 this.gMapContext.circle.setMap(null);
29059 options = Roo.apply({}, options, {
29060 strokeColor: "#0000FF",
29061 strokeOpacity: .35,
29063 fillColor: "#0000FF",
29067 options.map = this.gMapContext.map;
29068 options.radius = radius;
29069 options.center = center;
29070 this.gMapContext.circle = new google.maps.Circle(options);
29071 return this.gMapContext.circle;
29077 setPosition: function(location)
29079 this.gMapContext.location = location;
29080 this.gMapContext.marker.setPosition(location);
29081 this.gMapContext.map.panTo(location);
29082 this.drawCircle(location, this.gMapContext.radius, {});
29086 if (this.gMapContext.settings.enableReverseGeocode) {
29087 this.gMapContext.geodecoder.geocode({
29088 latLng: this.gMapContext.location
29089 }, function(results, status) {
29091 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29092 _this.gMapContext.locationName = results[0].formatted_address;
29093 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29095 _this.fireEvent('positionchanged', this, location);
29102 this.fireEvent('positionchanged', this, location);
29107 google.maps.event.trigger(this.gMapContext.map, "resize");
29109 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29111 this.fireEvent('resize', this);
29114 setPositionByLatLng: function(latitude, longitude)
29116 this.setPosition(new google.maps.LatLng(latitude, longitude));
29119 getCurrentPosition: function()
29122 latitude: this.gMapContext.location.lat(),
29123 longitude: this.gMapContext.location.lng()
29127 getAddressName: function()
29129 return this.gMapContext.locationName;
29132 getAddressComponents: function()
29134 return this.gMapContext.addressComponents;
29137 address_component_from_google_geocode: function(address_components)
29141 for (var i = 0; i < address_components.length; i++) {
29142 var component = address_components[i];
29143 if (component.types.indexOf("postal_code") >= 0) {
29144 result.postalCode = component.short_name;
29145 } else if (component.types.indexOf("street_number") >= 0) {
29146 result.streetNumber = component.short_name;
29147 } else if (component.types.indexOf("route") >= 0) {
29148 result.streetName = component.short_name;
29149 } else if (component.types.indexOf("neighborhood") >= 0) {
29150 result.city = component.short_name;
29151 } else if (component.types.indexOf("locality") >= 0) {
29152 result.city = component.short_name;
29153 } else if (component.types.indexOf("sublocality") >= 0) {
29154 result.district = component.short_name;
29155 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29156 result.stateOrProvince = component.short_name;
29157 } else if (component.types.indexOf("country") >= 0) {
29158 result.country = component.short_name;
29162 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29163 result.addressLine2 = "";
29167 setZoomLevel: function(zoom)
29169 this.gMapContext.map.setZoom(zoom);
29182 this.fireEvent('show', this);
29193 this.fireEvent('hide', this);
29198 Roo.apply(Roo.bootstrap.LocationPicker, {
29200 OverlayView : function(map, options)
29202 options = options || {};
29209 * @class Roo.bootstrap.Alert
29210 * @extends Roo.bootstrap.Component
29211 * Bootstrap Alert class - shows an alert area box
29213 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29214 Enter a valid email address
29217 * @cfg {String} title The title of alert
29218 * @cfg {String} html The content of alert
29219 * @cfg {String} weight ( success | info | warning | danger )
29220 * @cfg {String} faicon font-awesomeicon
29223 * Create a new alert
29224 * @param {Object} config The config object
29228 Roo.bootstrap.Alert = function(config){
29229 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29233 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29240 getAutoCreate : function()
29249 cls : 'roo-alert-icon'
29254 cls : 'roo-alert-title',
29259 cls : 'roo-alert-text',
29266 cfg.cn[0].cls += ' fa ' + this.faicon;
29270 cfg.cls += ' alert-' + this.weight;
29276 initEvents: function()
29278 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29281 setTitle : function(str)
29283 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29286 setText : function(str)
29288 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29291 setWeight : function(weight)
29294 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29297 this.weight = weight;
29299 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29302 setIcon : function(icon)
29305 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29308 this.faicon = icon;
29310 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29331 * @class Roo.bootstrap.UploadCropbox
29332 * @extends Roo.bootstrap.Component
29333 * Bootstrap UploadCropbox class
29334 * @cfg {String} emptyText show when image has been loaded
29335 * @cfg {String} rotateNotify show when image too small to rotate
29336 * @cfg {Number} errorTimeout default 3000
29337 * @cfg {Number} minWidth default 300
29338 * @cfg {Number} minHeight default 300
29339 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29340 * @cfg {Boolean} isDocument (true|false) default false
29341 * @cfg {String} url action url
29342 * @cfg {String} paramName default 'imageUpload'
29343 * @cfg {String} method default POST
29344 * @cfg {Boolean} loadMask (true|false) default true
29345 * @cfg {Boolean} loadingText default 'Loading...'
29348 * Create a new UploadCropbox
29349 * @param {Object} config The config object
29352 Roo.bootstrap.UploadCropbox = function(config){
29353 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29357 * @event beforeselectfile
29358 * Fire before select file
29359 * @param {Roo.bootstrap.UploadCropbox} this
29361 "beforeselectfile" : true,
29364 * Fire after initEvent
29365 * @param {Roo.bootstrap.UploadCropbox} this
29370 * Fire after initEvent
29371 * @param {Roo.bootstrap.UploadCropbox} this
29372 * @param {String} data
29377 * Fire when preparing the file data
29378 * @param {Roo.bootstrap.UploadCropbox} this
29379 * @param {Object} file
29384 * Fire when get exception
29385 * @param {Roo.bootstrap.UploadCropbox} this
29386 * @param {XMLHttpRequest} xhr
29388 "exception" : true,
29390 * @event beforeloadcanvas
29391 * Fire before load the canvas
29392 * @param {Roo.bootstrap.UploadCropbox} this
29393 * @param {String} src
29395 "beforeloadcanvas" : true,
29398 * Fire when trash image
29399 * @param {Roo.bootstrap.UploadCropbox} this
29404 * Fire when download the image
29405 * @param {Roo.bootstrap.UploadCropbox} this
29409 * @event footerbuttonclick
29410 * Fire when footerbuttonclick
29411 * @param {Roo.bootstrap.UploadCropbox} this
29412 * @param {String} type
29414 "footerbuttonclick" : true,
29418 * @param {Roo.bootstrap.UploadCropbox} this
29423 * Fire when rotate the image
29424 * @param {Roo.bootstrap.UploadCropbox} this
29425 * @param {String} pos
29430 * Fire when inspect the file
29431 * @param {Roo.bootstrap.UploadCropbox} this
29432 * @param {Object} file
29437 * Fire when xhr upload the file
29438 * @param {Roo.bootstrap.UploadCropbox} this
29439 * @param {Object} data
29444 * Fire when arrange the file data
29445 * @param {Roo.bootstrap.UploadCropbox} this
29446 * @param {Object} formData
29451 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29454 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29456 emptyText : 'Click to upload image',
29457 rotateNotify : 'Image is too small to rotate',
29458 errorTimeout : 3000,
29472 cropType : 'image/jpeg',
29474 canvasLoaded : false,
29475 isDocument : false,
29477 paramName : 'imageUpload',
29479 loadingText : 'Loading...',
29482 getAutoCreate : function()
29486 cls : 'roo-upload-cropbox',
29490 cls : 'roo-upload-cropbox-selector',
29495 cls : 'roo-upload-cropbox-body',
29496 style : 'cursor:pointer',
29500 cls : 'roo-upload-cropbox-preview'
29504 cls : 'roo-upload-cropbox-thumb'
29508 cls : 'roo-upload-cropbox-empty-notify',
29509 html : this.emptyText
29513 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29514 html : this.rotateNotify
29520 cls : 'roo-upload-cropbox-footer',
29523 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29533 onRender : function(ct, position)
29535 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29537 if (this.buttons.length) {
29539 Roo.each(this.buttons, function(bb) {
29541 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29543 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29549 this.maskEl = this.el;
29553 initEvents : function()
29555 this.urlAPI = (window.createObjectURL && window) ||
29556 (window.URL && URL.revokeObjectURL && URL) ||
29557 (window.webkitURL && webkitURL);
29559 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29560 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29562 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29563 this.selectorEl.hide();
29565 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29566 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29568 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29569 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29570 this.thumbEl.hide();
29572 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29573 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29575 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29576 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29577 this.errorEl.hide();
29579 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29580 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29581 this.footerEl.hide();
29583 this.setThumbBoxSize();
29589 this.fireEvent('initial', this);
29596 window.addEventListener("resize", function() { _this.resize(); } );
29598 this.bodyEl.on('click', this.beforeSelectFile, this);
29601 this.bodyEl.on('touchstart', this.onTouchStart, this);
29602 this.bodyEl.on('touchmove', this.onTouchMove, this);
29603 this.bodyEl.on('touchend', this.onTouchEnd, this);
29607 this.bodyEl.on('mousedown', this.onMouseDown, this);
29608 this.bodyEl.on('mousemove', this.onMouseMove, this);
29609 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29610 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29611 Roo.get(document).on('mouseup', this.onMouseUp, this);
29614 this.selectorEl.on('change', this.onFileSelected, this);
29620 this.baseScale = 1;
29622 this.baseRotate = 1;
29623 this.dragable = false;
29624 this.pinching = false;
29627 this.cropData = false;
29628 this.notifyEl.dom.innerHTML = this.emptyText;
29630 this.selectorEl.dom.value = '';
29634 resize : function()
29636 if(this.fireEvent('resize', this) != false){
29637 this.setThumbBoxPosition();
29638 this.setCanvasPosition();
29642 onFooterButtonClick : function(e, el, o, type)
29645 case 'rotate-left' :
29646 this.onRotateLeft(e);
29648 case 'rotate-right' :
29649 this.onRotateRight(e);
29652 this.beforeSelectFile(e);
29667 this.fireEvent('footerbuttonclick', this, type);
29670 beforeSelectFile : function(e)
29672 e.preventDefault();
29674 if(this.fireEvent('beforeselectfile', this) != false){
29675 this.selectorEl.dom.click();
29679 onFileSelected : function(e)
29681 e.preventDefault();
29683 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29687 var file = this.selectorEl.dom.files[0];
29689 if(this.fireEvent('inspect', this, file) != false){
29690 this.prepare(file);
29695 trash : function(e)
29697 this.fireEvent('trash', this);
29700 download : function(e)
29702 this.fireEvent('download', this);
29705 loadCanvas : function(src)
29707 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29711 this.imageEl = document.createElement('img');
29715 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29717 this.imageEl.src = src;
29721 onLoadCanvas : function()
29723 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29724 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29726 this.bodyEl.un('click', this.beforeSelectFile, this);
29728 this.notifyEl.hide();
29729 this.thumbEl.show();
29730 this.footerEl.show();
29732 this.baseRotateLevel();
29734 if(this.isDocument){
29735 this.setThumbBoxSize();
29738 this.setThumbBoxPosition();
29740 this.baseScaleLevel();
29746 this.canvasLoaded = true;
29749 this.maskEl.unmask();
29754 setCanvasPosition : function()
29756 if(!this.canvasEl){
29760 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29761 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29763 this.previewEl.setLeft(pw);
29764 this.previewEl.setTop(ph);
29768 onMouseDown : function(e)
29772 this.dragable = true;
29773 this.pinching = false;
29775 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29776 this.dragable = false;
29780 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29781 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29785 onMouseMove : function(e)
29789 if(!this.canvasLoaded){
29793 if (!this.dragable){
29797 var minX = Math.ceil(this.thumbEl.getLeft(true));
29798 var minY = Math.ceil(this.thumbEl.getTop(true));
29800 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29801 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29803 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29804 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29806 x = x - this.mouseX;
29807 y = y - this.mouseY;
29809 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29810 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29812 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29813 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29815 this.previewEl.setLeft(bgX);
29816 this.previewEl.setTop(bgY);
29818 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29819 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29822 onMouseUp : function(e)
29826 this.dragable = false;
29829 onMouseWheel : function(e)
29833 this.startScale = this.scale;
29835 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29837 if(!this.zoomable()){
29838 this.scale = this.startScale;
29847 zoomable : function()
29849 var minScale = this.thumbEl.getWidth() / this.minWidth;
29851 if(this.minWidth < this.minHeight){
29852 minScale = this.thumbEl.getHeight() / this.minHeight;
29855 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29856 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29860 (this.rotate == 0 || this.rotate == 180) &&
29862 width > this.imageEl.OriginWidth ||
29863 height > this.imageEl.OriginHeight ||
29864 (width < this.minWidth && height < this.minHeight)
29872 (this.rotate == 90 || this.rotate == 270) &&
29874 width > this.imageEl.OriginWidth ||
29875 height > this.imageEl.OriginHeight ||
29876 (width < this.minHeight && height < this.minWidth)
29883 !this.isDocument &&
29884 (this.rotate == 0 || this.rotate == 180) &&
29886 width < this.minWidth ||
29887 width > this.imageEl.OriginWidth ||
29888 height < this.minHeight ||
29889 height > this.imageEl.OriginHeight
29896 !this.isDocument &&
29897 (this.rotate == 90 || this.rotate == 270) &&
29899 width < this.minHeight ||
29900 width > this.imageEl.OriginWidth ||
29901 height < this.minWidth ||
29902 height > this.imageEl.OriginHeight
29912 onRotateLeft : function(e)
29914 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29916 var minScale = this.thumbEl.getWidth() / this.minWidth;
29918 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29919 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29921 this.startScale = this.scale;
29923 while (this.getScaleLevel() < minScale){
29925 this.scale = this.scale + 1;
29927 if(!this.zoomable()){
29932 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29933 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29938 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29945 this.scale = this.startScale;
29947 this.onRotateFail();
29952 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29954 if(this.isDocument){
29955 this.setThumbBoxSize();
29956 this.setThumbBoxPosition();
29957 this.setCanvasPosition();
29962 this.fireEvent('rotate', this, 'left');
29966 onRotateRight : function(e)
29968 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29970 var minScale = this.thumbEl.getWidth() / this.minWidth;
29972 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29973 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29975 this.startScale = this.scale;
29977 while (this.getScaleLevel() < minScale){
29979 this.scale = this.scale + 1;
29981 if(!this.zoomable()){
29986 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29987 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29992 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29999 this.scale = this.startScale;
30001 this.onRotateFail();
30006 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30008 if(this.isDocument){
30009 this.setThumbBoxSize();
30010 this.setThumbBoxPosition();
30011 this.setCanvasPosition();
30016 this.fireEvent('rotate', this, 'right');
30019 onRotateFail : function()
30021 this.errorEl.show(true);
30025 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30030 this.previewEl.dom.innerHTML = '';
30032 var canvasEl = document.createElement("canvas");
30034 var contextEl = canvasEl.getContext("2d");
30036 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30037 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30038 var center = this.imageEl.OriginWidth / 2;
30040 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30041 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30042 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30043 center = this.imageEl.OriginHeight / 2;
30046 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30048 contextEl.translate(center, center);
30049 contextEl.rotate(this.rotate * Math.PI / 180);
30051 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30053 this.canvasEl = document.createElement("canvas");
30055 this.contextEl = this.canvasEl.getContext("2d");
30057 switch (this.rotate) {
30060 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30061 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30063 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30068 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30069 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30071 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30072 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);
30076 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30081 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30082 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30084 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30085 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);
30089 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);
30094 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30095 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30097 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30098 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30102 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);
30109 this.previewEl.appendChild(this.canvasEl);
30111 this.setCanvasPosition();
30116 if(!this.canvasLoaded){
30120 var imageCanvas = document.createElement("canvas");
30122 var imageContext = imageCanvas.getContext("2d");
30124 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30125 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30127 var center = imageCanvas.width / 2;
30129 imageContext.translate(center, center);
30131 imageContext.rotate(this.rotate * Math.PI / 180);
30133 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30135 var canvas = document.createElement("canvas");
30137 var context = canvas.getContext("2d");
30139 canvas.width = this.minWidth;
30140 canvas.height = this.minHeight;
30142 switch (this.rotate) {
30145 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30146 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30148 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30149 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30151 var targetWidth = this.minWidth - 2 * x;
30152 var targetHeight = this.minHeight - 2 * y;
30156 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30157 scale = targetWidth / width;
30160 if(x > 0 && y == 0){
30161 scale = targetHeight / height;
30164 if(x > 0 && y > 0){
30165 scale = targetWidth / width;
30167 if(width < height){
30168 scale = targetHeight / height;
30172 context.scale(scale, scale);
30174 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30175 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30177 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30178 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30180 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30185 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30186 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30188 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30189 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30191 var targetWidth = this.minWidth - 2 * x;
30192 var targetHeight = this.minHeight - 2 * y;
30196 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30197 scale = targetWidth / width;
30200 if(x > 0 && y == 0){
30201 scale = targetHeight / height;
30204 if(x > 0 && y > 0){
30205 scale = targetWidth / width;
30207 if(width < height){
30208 scale = targetHeight / height;
30212 context.scale(scale, scale);
30214 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30215 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30217 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30218 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30220 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30222 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30227 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30228 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30230 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30231 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30233 var targetWidth = this.minWidth - 2 * x;
30234 var targetHeight = this.minHeight - 2 * y;
30238 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30239 scale = targetWidth / width;
30242 if(x > 0 && y == 0){
30243 scale = targetHeight / height;
30246 if(x > 0 && y > 0){
30247 scale = targetWidth / width;
30249 if(width < height){
30250 scale = targetHeight / height;
30254 context.scale(scale, scale);
30256 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30257 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30259 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30260 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30262 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30263 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30265 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30270 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30271 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30273 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30274 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30276 var targetWidth = this.minWidth - 2 * x;
30277 var targetHeight = this.minHeight - 2 * y;
30281 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30282 scale = targetWidth / width;
30285 if(x > 0 && y == 0){
30286 scale = targetHeight / height;
30289 if(x > 0 && y > 0){
30290 scale = targetWidth / width;
30292 if(width < height){
30293 scale = targetHeight / height;
30297 context.scale(scale, scale);
30299 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30300 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30302 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30303 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30305 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30307 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30314 this.cropData = canvas.toDataURL(this.cropType);
30316 if(this.fireEvent('crop', this, this.cropData) !== false){
30317 this.process(this.file, this.cropData);
30324 setThumbBoxSize : function()
30328 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30329 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30330 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30332 this.minWidth = width;
30333 this.minHeight = height;
30335 if(this.rotate == 90 || this.rotate == 270){
30336 this.minWidth = height;
30337 this.minHeight = width;
30342 width = Math.ceil(this.minWidth * height / this.minHeight);
30344 if(this.minWidth > this.minHeight){
30346 height = Math.ceil(this.minHeight * width / this.minWidth);
30349 this.thumbEl.setStyle({
30350 width : width + 'px',
30351 height : height + 'px'
30358 setThumbBoxPosition : function()
30360 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30361 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30363 this.thumbEl.setLeft(x);
30364 this.thumbEl.setTop(y);
30368 baseRotateLevel : function()
30370 this.baseRotate = 1;
30373 typeof(this.exif) != 'undefined' &&
30374 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30375 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30377 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30380 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30384 baseScaleLevel : function()
30388 if(this.isDocument){
30390 if(this.baseRotate == 6 || this.baseRotate == 8){
30392 height = this.thumbEl.getHeight();
30393 this.baseScale = height / this.imageEl.OriginWidth;
30395 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30396 width = this.thumbEl.getWidth();
30397 this.baseScale = width / this.imageEl.OriginHeight;
30403 height = this.thumbEl.getHeight();
30404 this.baseScale = height / this.imageEl.OriginHeight;
30406 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30407 width = this.thumbEl.getWidth();
30408 this.baseScale = width / this.imageEl.OriginWidth;
30414 if(this.baseRotate == 6 || this.baseRotate == 8){
30416 width = this.thumbEl.getHeight();
30417 this.baseScale = width / this.imageEl.OriginHeight;
30419 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30420 height = this.thumbEl.getWidth();
30421 this.baseScale = height / this.imageEl.OriginHeight;
30424 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30425 height = this.thumbEl.getWidth();
30426 this.baseScale = height / this.imageEl.OriginHeight;
30428 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30429 width = this.thumbEl.getHeight();
30430 this.baseScale = width / this.imageEl.OriginWidth;
30437 width = this.thumbEl.getWidth();
30438 this.baseScale = width / this.imageEl.OriginWidth;
30440 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30441 height = this.thumbEl.getHeight();
30442 this.baseScale = height / this.imageEl.OriginHeight;
30445 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30447 height = this.thumbEl.getHeight();
30448 this.baseScale = height / this.imageEl.OriginHeight;
30450 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30451 width = this.thumbEl.getWidth();
30452 this.baseScale = width / this.imageEl.OriginWidth;
30460 getScaleLevel : function()
30462 return this.baseScale * Math.pow(1.1, this.scale);
30465 onTouchStart : function(e)
30467 if(!this.canvasLoaded){
30468 this.beforeSelectFile(e);
30472 var touches = e.browserEvent.touches;
30478 if(touches.length == 1){
30479 this.onMouseDown(e);
30483 if(touches.length != 2){
30489 for(var i = 0, finger; finger = touches[i]; i++){
30490 coords.push(finger.pageX, finger.pageY);
30493 var x = Math.pow(coords[0] - coords[2], 2);
30494 var y = Math.pow(coords[1] - coords[3], 2);
30496 this.startDistance = Math.sqrt(x + y);
30498 this.startScale = this.scale;
30500 this.pinching = true;
30501 this.dragable = false;
30505 onTouchMove : function(e)
30507 if(!this.pinching && !this.dragable){
30511 var touches = e.browserEvent.touches;
30518 this.onMouseMove(e);
30524 for(var i = 0, finger; finger = touches[i]; i++){
30525 coords.push(finger.pageX, finger.pageY);
30528 var x = Math.pow(coords[0] - coords[2], 2);
30529 var y = Math.pow(coords[1] - coords[3], 2);
30531 this.endDistance = Math.sqrt(x + y);
30533 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30535 if(!this.zoomable()){
30536 this.scale = this.startScale;
30544 onTouchEnd : function(e)
30546 this.pinching = false;
30547 this.dragable = false;
30551 process : function(file, crop)
30554 this.maskEl.mask(this.loadingText);
30557 this.xhr = new XMLHttpRequest();
30559 file.xhr = this.xhr;
30561 this.xhr.open(this.method, this.url, true);
30564 "Accept": "application/json",
30565 "Cache-Control": "no-cache",
30566 "X-Requested-With": "XMLHttpRequest"
30569 for (var headerName in headers) {
30570 var headerValue = headers[headerName];
30572 this.xhr.setRequestHeader(headerName, headerValue);
30578 this.xhr.onload = function()
30580 _this.xhrOnLoad(_this.xhr);
30583 this.xhr.onerror = function()
30585 _this.xhrOnError(_this.xhr);
30588 var formData = new FormData();
30590 formData.append('returnHTML', 'NO');
30593 formData.append('crop', crop);
30596 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30597 formData.append(this.paramName, file, file.name);
30600 if(typeof(file.filename) != 'undefined'){
30601 formData.append('filename', file.filename);
30604 if(typeof(file.mimetype) != 'undefined'){
30605 formData.append('mimetype', file.mimetype);
30608 if(this.fireEvent('arrange', this, formData) != false){
30609 this.xhr.send(formData);
30613 xhrOnLoad : function(xhr)
30616 this.maskEl.unmask();
30619 if (xhr.readyState !== 4) {
30620 this.fireEvent('exception', this, xhr);
30624 var response = Roo.decode(xhr.responseText);
30626 if(!response.success){
30627 this.fireEvent('exception', this, xhr);
30631 var response = Roo.decode(xhr.responseText);
30633 this.fireEvent('upload', this, response);
30637 xhrOnError : function()
30640 this.maskEl.unmask();
30643 Roo.log('xhr on error');
30645 var response = Roo.decode(xhr.responseText);
30651 prepare : function(file)
30654 this.maskEl.mask(this.loadingText);
30660 if(typeof(file) === 'string'){
30661 this.loadCanvas(file);
30665 if(!file || !this.urlAPI){
30670 this.cropType = file.type;
30674 if(this.fireEvent('prepare', this, this.file) != false){
30676 var reader = new FileReader();
30678 reader.onload = function (e) {
30679 if (e.target.error) {
30680 Roo.log(e.target.error);
30684 var buffer = e.target.result,
30685 dataView = new DataView(buffer),
30687 maxOffset = dataView.byteLength - 4,
30691 if (dataView.getUint16(0) === 0xffd8) {
30692 while (offset < maxOffset) {
30693 markerBytes = dataView.getUint16(offset);
30695 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30696 markerLength = dataView.getUint16(offset + 2) + 2;
30697 if (offset + markerLength > dataView.byteLength) {
30698 Roo.log('Invalid meta data: Invalid segment size.');
30702 if(markerBytes == 0xffe1){
30703 _this.parseExifData(
30710 offset += markerLength;
30720 var url = _this.urlAPI.createObjectURL(_this.file);
30722 _this.loadCanvas(url);
30727 reader.readAsArrayBuffer(this.file);
30733 parseExifData : function(dataView, offset, length)
30735 var tiffOffset = offset + 10,
30739 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30740 // No Exif data, might be XMP data instead
30744 // Check for the ASCII code for "Exif" (0x45786966):
30745 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30746 // No Exif data, might be XMP data instead
30749 if (tiffOffset + 8 > dataView.byteLength) {
30750 Roo.log('Invalid Exif data: Invalid segment size.');
30753 // Check for the two null bytes:
30754 if (dataView.getUint16(offset + 8) !== 0x0000) {
30755 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30758 // Check the byte alignment:
30759 switch (dataView.getUint16(tiffOffset)) {
30761 littleEndian = true;
30764 littleEndian = false;
30767 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30770 // Check for the TIFF tag marker (0x002A):
30771 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30772 Roo.log('Invalid Exif data: Missing TIFF marker.');
30775 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30776 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30778 this.parseExifTags(
30781 tiffOffset + dirOffset,
30786 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30791 if (dirOffset + 6 > dataView.byteLength) {
30792 Roo.log('Invalid Exif data: Invalid directory offset.');
30795 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30796 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30797 if (dirEndOffset + 4 > dataView.byteLength) {
30798 Roo.log('Invalid Exif data: Invalid directory size.');
30801 for (i = 0; i < tagsNumber; i += 1) {
30805 dirOffset + 2 + 12 * i, // tag offset
30809 // Return the offset to the next directory:
30810 return dataView.getUint32(dirEndOffset, littleEndian);
30813 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30815 var tag = dataView.getUint16(offset, littleEndian);
30817 this.exif[tag] = this.getExifValue(
30821 dataView.getUint16(offset + 2, littleEndian), // tag type
30822 dataView.getUint32(offset + 4, littleEndian), // tag length
30827 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30829 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30838 Roo.log('Invalid Exif data: Invalid tag type.');
30842 tagSize = tagType.size * length;
30843 // Determine if the value is contained in the dataOffset bytes,
30844 // or if the value at the dataOffset is a pointer to the actual data:
30845 dataOffset = tagSize > 4 ?
30846 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30847 if (dataOffset + tagSize > dataView.byteLength) {
30848 Roo.log('Invalid Exif data: Invalid data offset.');
30851 if (length === 1) {
30852 return tagType.getValue(dataView, dataOffset, littleEndian);
30855 for (i = 0; i < length; i += 1) {
30856 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30859 if (tagType.ascii) {
30861 // Concatenate the chars:
30862 for (i = 0; i < values.length; i += 1) {
30864 // Ignore the terminating NULL byte(s):
30865 if (c === '\u0000') {
30877 Roo.apply(Roo.bootstrap.UploadCropbox, {
30879 'Orientation': 0x0112
30883 1: 0, //'top-left',
30885 3: 180, //'bottom-right',
30886 // 4: 'bottom-left',
30888 6: 90, //'right-top',
30889 // 7: 'right-bottom',
30890 8: 270 //'left-bottom'
30894 // byte, 8-bit unsigned int:
30896 getValue: function (dataView, dataOffset) {
30897 return dataView.getUint8(dataOffset);
30901 // ascii, 8-bit byte:
30903 getValue: function (dataView, dataOffset) {
30904 return String.fromCharCode(dataView.getUint8(dataOffset));
30909 // short, 16 bit int:
30911 getValue: function (dataView, dataOffset, littleEndian) {
30912 return dataView.getUint16(dataOffset, littleEndian);
30916 // long, 32 bit int:
30918 getValue: function (dataView, dataOffset, littleEndian) {
30919 return dataView.getUint32(dataOffset, littleEndian);
30923 // rational = two long values, first is numerator, second is denominator:
30925 getValue: function (dataView, dataOffset, littleEndian) {
30926 return dataView.getUint32(dataOffset, littleEndian) /
30927 dataView.getUint32(dataOffset + 4, littleEndian);
30931 // slong, 32 bit signed int:
30933 getValue: function (dataView, dataOffset, littleEndian) {
30934 return dataView.getInt32(dataOffset, littleEndian);
30938 // srational, two slongs, first is numerator, second is denominator:
30940 getValue: function (dataView, dataOffset, littleEndian) {
30941 return dataView.getInt32(dataOffset, littleEndian) /
30942 dataView.getInt32(dataOffset + 4, littleEndian);
30952 cls : 'btn-group roo-upload-cropbox-rotate-left',
30953 action : 'rotate-left',
30957 cls : 'btn btn-default',
30958 html : '<i class="fa fa-undo"></i>'
30964 cls : 'btn-group roo-upload-cropbox-picture',
30965 action : 'picture',
30969 cls : 'btn btn-default',
30970 html : '<i class="fa fa-picture-o"></i>'
30976 cls : 'btn-group roo-upload-cropbox-rotate-right',
30977 action : 'rotate-right',
30981 cls : 'btn btn-default',
30982 html : '<i class="fa fa-repeat"></i>'
30990 cls : 'btn-group roo-upload-cropbox-rotate-left',
30991 action : 'rotate-left',
30995 cls : 'btn btn-default',
30996 html : '<i class="fa fa-undo"></i>'
31002 cls : 'btn-group roo-upload-cropbox-download',
31003 action : 'download',
31007 cls : 'btn btn-default',
31008 html : '<i class="fa fa-download"></i>'
31014 cls : 'btn-group roo-upload-cropbox-crop',
31019 cls : 'btn btn-default',
31020 html : '<i class="fa fa-crop"></i>'
31026 cls : 'btn-group roo-upload-cropbox-trash',
31031 cls : 'btn btn-default',
31032 html : '<i class="fa fa-trash"></i>'
31038 cls : 'btn-group roo-upload-cropbox-rotate-right',
31039 action : 'rotate-right',
31043 cls : 'btn btn-default',
31044 html : '<i class="fa fa-repeat"></i>'
31052 cls : 'btn-group roo-upload-cropbox-rotate-left',
31053 action : 'rotate-left',
31057 cls : 'btn btn-default',
31058 html : '<i class="fa fa-undo"></i>'
31064 cls : 'btn-group roo-upload-cropbox-rotate-right',
31065 action : 'rotate-right',
31069 cls : 'btn btn-default',
31070 html : '<i class="fa fa-repeat"></i>'
31083 * @class Roo.bootstrap.DocumentManager
31084 * @extends Roo.bootstrap.Component
31085 * Bootstrap DocumentManager class
31086 * @cfg {String} paramName default 'imageUpload'
31087 * @cfg {String} toolTipName default 'filename'
31088 * @cfg {String} method default POST
31089 * @cfg {String} url action url
31090 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31091 * @cfg {Boolean} multiple multiple upload default true
31092 * @cfg {Number} thumbSize default 300
31093 * @cfg {String} fieldLabel
31094 * @cfg {Number} labelWidth default 4
31095 * @cfg {String} labelAlign (left|top) default left
31096 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31097 * @cfg {Number} labellg set the width of label (1-12)
31098 * @cfg {Number} labelmd set the width of label (1-12)
31099 * @cfg {Number} labelsm set the width of label (1-12)
31100 * @cfg {Number} labelxs set the width of label (1-12)
31103 * Create a new DocumentManager
31104 * @param {Object} config The config object
31107 Roo.bootstrap.DocumentManager = function(config){
31108 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31111 this.delegates = [];
31116 * Fire when initial the DocumentManager
31117 * @param {Roo.bootstrap.DocumentManager} this
31122 * inspect selected file
31123 * @param {Roo.bootstrap.DocumentManager} this
31124 * @param {File} file
31129 * Fire when xhr load exception
31130 * @param {Roo.bootstrap.DocumentManager} this
31131 * @param {XMLHttpRequest} xhr
31133 "exception" : true,
31135 * @event afterupload
31136 * Fire when xhr load exception
31137 * @param {Roo.bootstrap.DocumentManager} this
31138 * @param {XMLHttpRequest} xhr
31140 "afterupload" : true,
31143 * prepare the form data
31144 * @param {Roo.bootstrap.DocumentManager} this
31145 * @param {Object} formData
31150 * Fire when remove the file
31151 * @param {Roo.bootstrap.DocumentManager} this
31152 * @param {Object} file
31157 * Fire after refresh the file
31158 * @param {Roo.bootstrap.DocumentManager} this
31163 * Fire after click the image
31164 * @param {Roo.bootstrap.DocumentManager} this
31165 * @param {Object} file
31170 * Fire when upload a image and editable set to true
31171 * @param {Roo.bootstrap.DocumentManager} this
31172 * @param {Object} file
31176 * @event beforeselectfile
31177 * Fire before select file
31178 * @param {Roo.bootstrap.DocumentManager} this
31180 "beforeselectfile" : true,
31183 * Fire before process file
31184 * @param {Roo.bootstrap.DocumentManager} this
31185 * @param {Object} file
31189 * @event previewrendered
31190 * Fire when preview rendered
31191 * @param {Roo.bootstrap.DocumentManager} this
31192 * @param {Object} file
31194 "previewrendered" : true,
31197 "previewResize" : true
31202 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31211 paramName : 'imageUpload',
31212 toolTipName : 'filename',
31215 labelAlign : 'left',
31225 getAutoCreate : function()
31227 var managerWidget = {
31229 cls : 'roo-document-manager',
31233 cls : 'roo-document-manager-selector',
31238 cls : 'roo-document-manager-uploader',
31242 cls : 'roo-document-manager-upload-btn',
31243 html : '<i class="fa fa-plus"></i>'
31254 cls : 'column col-md-12',
31259 if(this.fieldLabel.length){
31264 cls : 'column col-md-12',
31265 html : this.fieldLabel
31269 cls : 'column col-md-12',
31274 if(this.labelAlign == 'left'){
31279 html : this.fieldLabel
31288 if(this.labelWidth > 12){
31289 content[0].style = "width: " + this.labelWidth + 'px';
31292 if(this.labelWidth < 13 && this.labelmd == 0){
31293 this.labelmd = this.labelWidth;
31296 if(this.labellg > 0){
31297 content[0].cls += ' col-lg-' + this.labellg;
31298 content[1].cls += ' col-lg-' + (12 - this.labellg);
31301 if(this.labelmd > 0){
31302 content[0].cls += ' col-md-' + this.labelmd;
31303 content[1].cls += ' col-md-' + (12 - this.labelmd);
31306 if(this.labelsm > 0){
31307 content[0].cls += ' col-sm-' + this.labelsm;
31308 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31311 if(this.labelxs > 0){
31312 content[0].cls += ' col-xs-' + this.labelxs;
31313 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31321 cls : 'row clearfix',
31329 initEvents : function()
31331 this.managerEl = this.el.select('.roo-document-manager', true).first();
31332 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31334 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31335 this.selectorEl.hide();
31338 this.selectorEl.attr('multiple', 'multiple');
31341 this.selectorEl.on('change', this.onFileSelected, this);
31343 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31344 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31346 this.uploader.on('click', this.onUploaderClick, this);
31348 this.renderProgressDialog();
31352 window.addEventListener("resize", function() { _this.refresh(); } );
31354 this.fireEvent('initial', this);
31357 renderProgressDialog : function()
31361 this.progressDialog = new Roo.bootstrap.Modal({
31362 cls : 'roo-document-manager-progress-dialog',
31363 allow_close : false,
31374 btnclick : function() {
31375 _this.uploadCancel();
31381 this.progressDialog.render(Roo.get(document.body));
31383 this.progress = new Roo.bootstrap.Progress({
31384 cls : 'roo-document-manager-progress',
31389 this.progress.render(this.progressDialog.getChildContainer());
31391 this.progressBar = new Roo.bootstrap.ProgressBar({
31392 cls : 'roo-document-manager-progress-bar',
31395 aria_valuemax : 12,
31399 this.progressBar.render(this.progress.getChildContainer());
31402 onUploaderClick : function(e)
31404 e.preventDefault();
31406 if(this.fireEvent('beforeselectfile', this) != false){
31407 this.selectorEl.dom.click();
31412 onFileSelected : function(e)
31414 e.preventDefault();
31416 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31420 Roo.each(this.selectorEl.dom.files, function(file){
31421 if(this.fireEvent('inspect', this, file) != false){
31422 this.files.push(file);
31432 this.selectorEl.dom.value = '';
31434 if(!this.files || !this.files.length){
31438 if(this.boxes > 0 && this.files.length > this.boxes){
31439 this.files = this.files.slice(0, this.boxes);
31442 this.uploader.show();
31444 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31445 this.uploader.hide();
31454 Roo.each(this.files, function(file){
31456 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31457 var f = this.renderPreview(file);
31462 if(file.type.indexOf('image') != -1){
31463 this.delegates.push(
31465 _this.process(file);
31466 }).createDelegate(this)
31474 _this.process(file);
31475 }).createDelegate(this)
31480 this.files = files;
31482 this.delegates = this.delegates.concat(docs);
31484 if(!this.delegates.length){
31489 this.progressBar.aria_valuemax = this.delegates.length;
31496 arrange : function()
31498 if(!this.delegates.length){
31499 this.progressDialog.hide();
31504 var delegate = this.delegates.shift();
31506 this.progressDialog.show();
31508 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31510 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31515 refresh : function()
31517 this.uploader.show();
31519 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31520 this.uploader.hide();
31523 Roo.isTouch ? this.closable(false) : this.closable(true);
31525 this.fireEvent('refresh', this);
31528 onRemove : function(e, el, o)
31530 e.preventDefault();
31532 this.fireEvent('remove', this, o);
31536 remove : function(o)
31540 Roo.each(this.files, function(file){
31541 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31550 this.files = files;
31557 Roo.each(this.files, function(file){
31562 file.target.remove();
31571 onClick : function(e, el, o)
31573 e.preventDefault();
31575 this.fireEvent('click', this, o);
31579 closable : function(closable)
31581 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31583 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31595 xhrOnLoad : function(xhr)
31597 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31601 if (xhr.readyState !== 4) {
31603 this.fireEvent('exception', this, xhr);
31607 var response = Roo.decode(xhr.responseText);
31609 if(!response.success){
31611 this.fireEvent('exception', this, xhr);
31615 var file = this.renderPreview(response.data);
31617 this.files.push(file);
31621 this.fireEvent('afterupload', this, xhr);
31625 xhrOnError : function(xhr)
31627 Roo.log('xhr on error');
31629 var response = Roo.decode(xhr.responseText);
31636 process : function(file)
31638 if(this.fireEvent('process', this, file) !== false){
31639 if(this.editable && file.type.indexOf('image') != -1){
31640 this.fireEvent('edit', this, file);
31644 this.uploadStart(file, false);
31651 uploadStart : function(file, crop)
31653 this.xhr = new XMLHttpRequest();
31655 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31660 file.xhr = this.xhr;
31662 this.managerEl.createChild({
31664 cls : 'roo-document-manager-loading',
31668 tooltip : file.name,
31669 cls : 'roo-document-manager-thumb',
31670 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31676 this.xhr.open(this.method, this.url, true);
31679 "Accept": "application/json",
31680 "Cache-Control": "no-cache",
31681 "X-Requested-With": "XMLHttpRequest"
31684 for (var headerName in headers) {
31685 var headerValue = headers[headerName];
31687 this.xhr.setRequestHeader(headerName, headerValue);
31693 this.xhr.onload = function()
31695 _this.xhrOnLoad(_this.xhr);
31698 this.xhr.onerror = function()
31700 _this.xhrOnError(_this.xhr);
31703 var formData = new FormData();
31705 formData.append('returnHTML', 'NO');
31708 formData.append('crop', crop);
31711 formData.append(this.paramName, file, file.name);
31718 if(this.fireEvent('prepare', this, formData, options) != false){
31720 if(options.manually){
31724 this.xhr.send(formData);
31728 this.uploadCancel();
31731 uploadCancel : function()
31737 this.delegates = [];
31739 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31746 renderPreview : function(file)
31748 if(typeof(file.target) != 'undefined' && file.target){
31752 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31754 var previewEl = this.managerEl.createChild({
31756 cls : 'roo-document-manager-preview',
31760 tooltip : file[this.toolTipName],
31761 cls : 'roo-document-manager-thumb',
31762 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31767 html : '<i class="fa fa-times-circle"></i>'
31772 var close = previewEl.select('button.close', true).first();
31774 close.on('click', this.onRemove, this, file);
31776 file.target = previewEl;
31778 var image = previewEl.select('img', true).first();
31782 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31784 image.on('click', this.onClick, this, file);
31786 this.fireEvent('previewrendered', this, file);
31792 onPreviewLoad : function(file, image)
31794 if(typeof(file.target) == 'undefined' || !file.target){
31798 var width = image.dom.naturalWidth || image.dom.width;
31799 var height = image.dom.naturalHeight || image.dom.height;
31801 if(!this.previewResize) {
31805 if(width > height){
31806 file.target.addClass('wide');
31810 file.target.addClass('tall');
31815 uploadFromSource : function(file, crop)
31817 this.xhr = new XMLHttpRequest();
31819 this.managerEl.createChild({
31821 cls : 'roo-document-manager-loading',
31825 tooltip : file.name,
31826 cls : 'roo-document-manager-thumb',
31827 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31833 this.xhr.open(this.method, this.url, true);
31836 "Accept": "application/json",
31837 "Cache-Control": "no-cache",
31838 "X-Requested-With": "XMLHttpRequest"
31841 for (var headerName in headers) {
31842 var headerValue = headers[headerName];
31844 this.xhr.setRequestHeader(headerName, headerValue);
31850 this.xhr.onload = function()
31852 _this.xhrOnLoad(_this.xhr);
31855 this.xhr.onerror = function()
31857 _this.xhrOnError(_this.xhr);
31860 var formData = new FormData();
31862 formData.append('returnHTML', 'NO');
31864 formData.append('crop', crop);
31866 if(typeof(file.filename) != 'undefined'){
31867 formData.append('filename', file.filename);
31870 if(typeof(file.mimetype) != 'undefined'){
31871 formData.append('mimetype', file.mimetype);
31876 if(this.fireEvent('prepare', this, formData) != false){
31877 this.xhr.send(formData);
31887 * @class Roo.bootstrap.DocumentViewer
31888 * @extends Roo.bootstrap.Component
31889 * Bootstrap DocumentViewer class
31890 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31891 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31894 * Create a new DocumentViewer
31895 * @param {Object} config The config object
31898 Roo.bootstrap.DocumentViewer = function(config){
31899 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31904 * Fire after initEvent
31905 * @param {Roo.bootstrap.DocumentViewer} this
31911 * @param {Roo.bootstrap.DocumentViewer} this
31916 * Fire after download button
31917 * @param {Roo.bootstrap.DocumentViewer} this
31922 * Fire after trash button
31923 * @param {Roo.bootstrap.DocumentViewer} this
31930 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31932 showDownload : true,
31936 getAutoCreate : function()
31940 cls : 'roo-document-viewer',
31944 cls : 'roo-document-viewer-body',
31948 cls : 'roo-document-viewer-thumb',
31952 cls : 'roo-document-viewer-image'
31960 cls : 'roo-document-viewer-footer',
31963 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31967 cls : 'btn-group roo-document-viewer-download',
31971 cls : 'btn btn-default',
31972 html : '<i class="fa fa-download"></i>'
31978 cls : 'btn-group roo-document-viewer-trash',
31982 cls : 'btn btn-default',
31983 html : '<i class="fa fa-trash"></i>'
31996 initEvents : function()
31998 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31999 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32001 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32002 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32004 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32005 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32007 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32008 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32010 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32011 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32013 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32014 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32016 this.bodyEl.on('click', this.onClick, this);
32017 this.downloadBtn.on('click', this.onDownload, this);
32018 this.trashBtn.on('click', this.onTrash, this);
32020 this.downloadBtn.hide();
32021 this.trashBtn.hide();
32023 if(this.showDownload){
32024 this.downloadBtn.show();
32027 if(this.showTrash){
32028 this.trashBtn.show();
32031 if(!this.showDownload && !this.showTrash) {
32032 this.footerEl.hide();
32037 initial : function()
32039 this.fireEvent('initial', this);
32043 onClick : function(e)
32045 e.preventDefault();
32047 this.fireEvent('click', this);
32050 onDownload : function(e)
32052 e.preventDefault();
32054 this.fireEvent('download', this);
32057 onTrash : function(e)
32059 e.preventDefault();
32061 this.fireEvent('trash', this);
32073 * @class Roo.bootstrap.NavProgressBar
32074 * @extends Roo.bootstrap.Component
32075 * Bootstrap NavProgressBar class
32078 * Create a new nav progress bar
32079 * @param {Object} config The config object
32082 Roo.bootstrap.NavProgressBar = function(config){
32083 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32085 this.bullets = this.bullets || [];
32087 // Roo.bootstrap.NavProgressBar.register(this);
32091 * Fires when the active item changes
32092 * @param {Roo.bootstrap.NavProgressBar} this
32093 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32094 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32101 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32106 getAutoCreate : function()
32108 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32112 cls : 'roo-navigation-bar-group',
32116 cls : 'roo-navigation-top-bar'
32120 cls : 'roo-navigation-bullets-bar',
32124 cls : 'roo-navigation-bar'
32131 cls : 'roo-navigation-bottom-bar'
32141 initEvents: function()
32146 onRender : function(ct, position)
32148 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32150 if(this.bullets.length){
32151 Roo.each(this.bullets, function(b){
32160 addItem : function(cfg)
32162 var item = new Roo.bootstrap.NavProgressItem(cfg);
32164 item.parentId = this.id;
32165 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32168 var top = new Roo.bootstrap.Element({
32170 cls : 'roo-navigation-bar-text'
32173 var bottom = new Roo.bootstrap.Element({
32175 cls : 'roo-navigation-bar-text'
32178 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32179 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32181 var topText = new Roo.bootstrap.Element({
32183 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32186 var bottomText = new Roo.bootstrap.Element({
32188 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32191 topText.onRender(top.el, null);
32192 bottomText.onRender(bottom.el, null);
32195 item.bottomEl = bottom;
32198 this.barItems.push(item);
32203 getActive : function()
32205 var active = false;
32207 Roo.each(this.barItems, function(v){
32209 if (!v.isActive()) {
32221 setActiveItem : function(item)
32225 Roo.each(this.barItems, function(v){
32226 if (v.rid == item.rid) {
32230 if (v.isActive()) {
32231 v.setActive(false);
32236 item.setActive(true);
32238 this.fireEvent('changed', this, item, prev);
32241 getBarItem: function(rid)
32245 Roo.each(this.barItems, function(e) {
32246 if (e.rid != rid) {
32257 indexOfItem : function(item)
32261 Roo.each(this.barItems, function(v, i){
32263 if (v.rid != item.rid) {
32274 setActiveNext : function()
32276 var i = this.indexOfItem(this.getActive());
32278 if (i > this.barItems.length) {
32282 this.setActiveItem(this.barItems[i+1]);
32285 setActivePrev : function()
32287 var i = this.indexOfItem(this.getActive());
32293 this.setActiveItem(this.barItems[i-1]);
32296 format : function()
32298 if(!this.barItems.length){
32302 var width = 100 / this.barItems.length;
32304 Roo.each(this.barItems, function(i){
32305 i.el.setStyle('width', width + '%');
32306 i.topEl.el.setStyle('width', width + '%');
32307 i.bottomEl.el.setStyle('width', width + '%');
32316 * Nav Progress Item
32321 * @class Roo.bootstrap.NavProgressItem
32322 * @extends Roo.bootstrap.Component
32323 * Bootstrap NavProgressItem class
32324 * @cfg {String} rid the reference id
32325 * @cfg {Boolean} active (true|false) Is item active default false
32326 * @cfg {Boolean} disabled (true|false) Is item active default false
32327 * @cfg {String} html
32328 * @cfg {String} position (top|bottom) text position default bottom
32329 * @cfg {String} icon show icon instead of number
32332 * Create a new NavProgressItem
32333 * @param {Object} config The config object
32335 Roo.bootstrap.NavProgressItem = function(config){
32336 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32341 * The raw click event for the entire grid.
32342 * @param {Roo.bootstrap.NavProgressItem} this
32343 * @param {Roo.EventObject} e
32350 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32356 position : 'bottom',
32359 getAutoCreate : function()
32361 var iconCls = 'roo-navigation-bar-item-icon';
32363 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32367 cls: 'roo-navigation-bar-item',
32377 cfg.cls += ' active';
32380 cfg.cls += ' disabled';
32386 disable : function()
32388 this.setDisabled(true);
32391 enable : function()
32393 this.setDisabled(false);
32396 initEvents: function()
32398 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32400 this.iconEl.on('click', this.onClick, this);
32403 onClick : function(e)
32405 e.preventDefault();
32411 if(this.fireEvent('click', this, e) === false){
32415 this.parent().setActiveItem(this);
32418 isActive: function ()
32420 return this.active;
32423 setActive : function(state)
32425 if(this.active == state){
32429 this.active = state;
32432 this.el.addClass('active');
32436 this.el.removeClass('active');
32441 setDisabled : function(state)
32443 if(this.disabled == state){
32447 this.disabled = state;
32450 this.el.addClass('disabled');
32454 this.el.removeClass('disabled');
32457 tooltipEl : function()
32459 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32472 * @class Roo.bootstrap.FieldLabel
32473 * @extends Roo.bootstrap.Component
32474 * Bootstrap FieldLabel class
32475 * @cfg {String} html contents of the element
32476 * @cfg {String} tag tag of the element default label
32477 * @cfg {String} cls class of the element
32478 * @cfg {String} target label target
32479 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32480 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32481 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32482 * @cfg {String} iconTooltip default "This field is required"
32483 * @cfg {String} indicatorpos (left|right) default left
32486 * Create a new FieldLabel
32487 * @param {Object} config The config object
32490 Roo.bootstrap.FieldLabel = function(config){
32491 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32496 * Fires after the field has been marked as invalid.
32497 * @param {Roo.form.FieldLabel} this
32498 * @param {String} msg The validation message
32503 * Fires after the field has been validated with no errors.
32504 * @param {Roo.form.FieldLabel} this
32510 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32517 invalidClass : 'has-warning',
32518 validClass : 'has-success',
32519 iconTooltip : 'This field is required',
32520 indicatorpos : 'left',
32522 getAutoCreate : function(){
32525 if (!this.allowBlank) {
32531 cls : 'roo-bootstrap-field-label ' + this.cls,
32536 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32537 tooltip : this.iconTooltip
32546 if(this.indicatorpos == 'right'){
32549 cls : 'roo-bootstrap-field-label ' + this.cls,
32558 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32559 tooltip : this.iconTooltip
32568 initEvents: function()
32570 Roo.bootstrap.Element.superclass.initEvents.call(this);
32572 this.indicator = this.indicatorEl();
32574 if(this.indicator){
32575 this.indicator.removeClass('visible');
32576 this.indicator.addClass('invisible');
32579 Roo.bootstrap.FieldLabel.register(this);
32582 indicatorEl : function()
32584 var indicator = this.el.select('i.roo-required-indicator',true).first();
32595 * Mark this field as valid
32597 markValid : function()
32599 if(this.indicator){
32600 this.indicator.removeClass('visible');
32601 this.indicator.addClass('invisible');
32603 if (Roo.bootstrap.version == 3) {
32604 this.el.removeClass(this.invalidClass);
32605 this.el.addClass(this.validClass);
32607 this.el.removeClass('is-invalid');
32608 this.el.addClass('is-valid');
32612 this.fireEvent('valid', this);
32616 * Mark this field as invalid
32617 * @param {String} msg The validation message
32619 markInvalid : function(msg)
32621 if(this.indicator){
32622 this.indicator.removeClass('invisible');
32623 this.indicator.addClass('visible');
32625 if (Roo.bootstrap.version == 3) {
32626 this.el.removeClass(this.validClass);
32627 this.el.addClass(this.invalidClass);
32629 this.el.removeClass('is-valid');
32630 this.el.addClass('is-invalid');
32634 this.fireEvent('invalid', this, msg);
32640 Roo.apply(Roo.bootstrap.FieldLabel, {
32645 * register a FieldLabel Group
32646 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32648 register : function(label)
32650 if(this.groups.hasOwnProperty(label.target)){
32654 this.groups[label.target] = label;
32658 * fetch a FieldLabel Group based on the target
32659 * @param {string} target
32660 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32662 get: function(target) {
32663 if (typeof(this.groups[target]) == 'undefined') {
32667 return this.groups[target] ;
32676 * page DateSplitField.
32682 * @class Roo.bootstrap.DateSplitField
32683 * @extends Roo.bootstrap.Component
32684 * Bootstrap DateSplitField class
32685 * @cfg {string} fieldLabel - the label associated
32686 * @cfg {Number} labelWidth set the width of label (0-12)
32687 * @cfg {String} labelAlign (top|left)
32688 * @cfg {Boolean} dayAllowBlank (true|false) default false
32689 * @cfg {Boolean} monthAllowBlank (true|false) default false
32690 * @cfg {Boolean} yearAllowBlank (true|false) default false
32691 * @cfg {string} dayPlaceholder
32692 * @cfg {string} monthPlaceholder
32693 * @cfg {string} yearPlaceholder
32694 * @cfg {string} dayFormat default 'd'
32695 * @cfg {string} monthFormat default 'm'
32696 * @cfg {string} yearFormat default 'Y'
32697 * @cfg {Number} labellg set the width of label (1-12)
32698 * @cfg {Number} labelmd set the width of label (1-12)
32699 * @cfg {Number} labelsm set the width of label (1-12)
32700 * @cfg {Number} labelxs set the width of label (1-12)
32704 * Create a new DateSplitField
32705 * @param {Object} config The config object
32708 Roo.bootstrap.DateSplitField = function(config){
32709 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32715 * getting the data of years
32716 * @param {Roo.bootstrap.DateSplitField} this
32717 * @param {Object} years
32722 * getting the data of days
32723 * @param {Roo.bootstrap.DateSplitField} this
32724 * @param {Object} days
32729 * Fires after the field has been marked as invalid.
32730 * @param {Roo.form.Field} this
32731 * @param {String} msg The validation message
32736 * Fires after the field has been validated with no errors.
32737 * @param {Roo.form.Field} this
32743 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32746 labelAlign : 'top',
32748 dayAllowBlank : false,
32749 monthAllowBlank : false,
32750 yearAllowBlank : false,
32751 dayPlaceholder : '',
32752 monthPlaceholder : '',
32753 yearPlaceholder : '',
32757 isFormField : true,
32763 getAutoCreate : function()
32767 cls : 'row roo-date-split-field-group',
32772 cls : 'form-hidden-field roo-date-split-field-group-value',
32778 var labelCls = 'col-md-12';
32779 var contentCls = 'col-md-4';
32781 if(this.fieldLabel){
32785 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32789 html : this.fieldLabel
32794 if(this.labelAlign == 'left'){
32796 if(this.labelWidth > 12){
32797 label.style = "width: " + this.labelWidth + 'px';
32800 if(this.labelWidth < 13 && this.labelmd == 0){
32801 this.labelmd = this.labelWidth;
32804 if(this.labellg > 0){
32805 labelCls = ' col-lg-' + this.labellg;
32806 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32809 if(this.labelmd > 0){
32810 labelCls = ' col-md-' + this.labelmd;
32811 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32814 if(this.labelsm > 0){
32815 labelCls = ' col-sm-' + this.labelsm;
32816 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32819 if(this.labelxs > 0){
32820 labelCls = ' col-xs-' + this.labelxs;
32821 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32825 label.cls += ' ' + labelCls;
32827 cfg.cn.push(label);
32830 Roo.each(['day', 'month', 'year'], function(t){
32833 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32840 inputEl: function ()
32842 return this.el.select('.roo-date-split-field-group-value', true).first();
32845 onRender : function(ct, position)
32849 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32851 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32853 this.dayField = new Roo.bootstrap.ComboBox({
32854 allowBlank : this.dayAllowBlank,
32855 alwaysQuery : true,
32856 displayField : 'value',
32859 forceSelection : true,
32861 placeholder : this.dayPlaceholder,
32862 selectOnFocus : true,
32863 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32864 triggerAction : 'all',
32866 valueField : 'value',
32867 store : new Roo.data.SimpleStore({
32868 data : (function() {
32870 _this.fireEvent('days', _this, days);
32873 fields : [ 'value' ]
32876 select : function (_self, record, index)
32878 _this.setValue(_this.getValue());
32883 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32885 this.monthField = new Roo.bootstrap.MonthField({
32886 after : '<i class=\"fa fa-calendar\"></i>',
32887 allowBlank : this.monthAllowBlank,
32888 placeholder : this.monthPlaceholder,
32891 render : function (_self)
32893 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32894 e.preventDefault();
32898 select : function (_self, oldvalue, newvalue)
32900 _this.setValue(_this.getValue());
32905 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32907 this.yearField = new Roo.bootstrap.ComboBox({
32908 allowBlank : this.yearAllowBlank,
32909 alwaysQuery : true,
32910 displayField : 'value',
32913 forceSelection : true,
32915 placeholder : this.yearPlaceholder,
32916 selectOnFocus : true,
32917 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32918 triggerAction : 'all',
32920 valueField : 'value',
32921 store : new Roo.data.SimpleStore({
32922 data : (function() {
32924 _this.fireEvent('years', _this, years);
32927 fields : [ 'value' ]
32930 select : function (_self, record, index)
32932 _this.setValue(_this.getValue());
32937 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32940 setValue : function(v, format)
32942 this.inputEl.dom.value = v;
32944 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32946 var d = Date.parseDate(v, f);
32953 this.setDay(d.format(this.dayFormat));
32954 this.setMonth(d.format(this.monthFormat));
32955 this.setYear(d.format(this.yearFormat));
32962 setDay : function(v)
32964 this.dayField.setValue(v);
32965 this.inputEl.dom.value = this.getValue();
32970 setMonth : function(v)
32972 this.monthField.setValue(v, true);
32973 this.inputEl.dom.value = this.getValue();
32978 setYear : function(v)
32980 this.yearField.setValue(v);
32981 this.inputEl.dom.value = this.getValue();
32986 getDay : function()
32988 return this.dayField.getValue();
32991 getMonth : function()
32993 return this.monthField.getValue();
32996 getYear : function()
32998 return this.yearField.getValue();
33001 getValue : function()
33003 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33005 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33015 this.inputEl.dom.value = '';
33020 validate : function()
33022 var d = this.dayField.validate();
33023 var m = this.monthField.validate();
33024 var y = this.yearField.validate();
33029 (!this.dayAllowBlank && !d) ||
33030 (!this.monthAllowBlank && !m) ||
33031 (!this.yearAllowBlank && !y)
33036 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33045 this.markInvalid();
33050 markValid : function()
33053 var label = this.el.select('label', true).first();
33054 var icon = this.el.select('i.fa-star', true).first();
33060 this.fireEvent('valid', this);
33064 * Mark this field as invalid
33065 * @param {String} msg The validation message
33067 markInvalid : function(msg)
33070 var label = this.el.select('label', true).first();
33071 var icon = this.el.select('i.fa-star', true).first();
33073 if(label && !icon){
33074 this.el.select('.roo-date-split-field-label', true).createChild({
33076 cls : 'text-danger fa fa-lg fa-star',
33077 tooltip : 'This field is required',
33078 style : 'margin-right:5px;'
33082 this.fireEvent('invalid', this, msg);
33085 clearInvalid : function()
33087 var label = this.el.select('label', true).first();
33088 var icon = this.el.select('i.fa-star', true).first();
33094 this.fireEvent('valid', this);
33097 getName: function()
33107 * http://masonry.desandro.com
33109 * The idea is to render all the bricks based on vertical width...
33111 * The original code extends 'outlayer' - we might need to use that....
33117 * @class Roo.bootstrap.LayoutMasonry
33118 * @extends Roo.bootstrap.Component
33119 * Bootstrap Layout Masonry class
33122 * Create a new Element
33123 * @param {Object} config The config object
33126 Roo.bootstrap.LayoutMasonry = function(config){
33128 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33132 Roo.bootstrap.LayoutMasonry.register(this);
33138 * Fire after layout the items
33139 * @param {Roo.bootstrap.LayoutMasonry} this
33140 * @param {Roo.EventObject} e
33147 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33150 * @cfg {Boolean} isLayoutInstant = no animation?
33152 isLayoutInstant : false, // needed?
33155 * @cfg {Number} boxWidth width of the columns
33160 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33165 * @cfg {Number} padWidth padding below box..
33170 * @cfg {Number} gutter gutter width..
33175 * @cfg {Number} maxCols maximum number of columns
33181 * @cfg {Boolean} isAutoInitial defalut true
33183 isAutoInitial : true,
33188 * @cfg {Boolean} isHorizontal defalut false
33190 isHorizontal : false,
33192 currentSize : null,
33198 bricks: null, //CompositeElement
33202 _isLayoutInited : false,
33204 // isAlternative : false, // only use for vertical layout...
33207 * @cfg {Number} alternativePadWidth padding below box..
33209 alternativePadWidth : 50,
33211 selectedBrick : [],
33213 getAutoCreate : function(){
33215 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33219 cls: 'blog-masonary-wrapper ' + this.cls,
33221 cls : 'mas-boxes masonary'
33228 getChildContainer: function( )
33230 if (this.boxesEl) {
33231 return this.boxesEl;
33234 this.boxesEl = this.el.select('.mas-boxes').first();
33236 return this.boxesEl;
33240 initEvents : function()
33244 if(this.isAutoInitial){
33245 Roo.log('hook children rendered');
33246 this.on('childrenrendered', function() {
33247 Roo.log('children rendered');
33253 initial : function()
33255 this.selectedBrick = [];
33257 this.currentSize = this.el.getBox(true);
33259 Roo.EventManager.onWindowResize(this.resize, this);
33261 if(!this.isAutoInitial){
33269 //this.layout.defer(500,this);
33273 resize : function()
33275 var cs = this.el.getBox(true);
33278 this.currentSize.width == cs.width &&
33279 this.currentSize.x == cs.x &&
33280 this.currentSize.height == cs.height &&
33281 this.currentSize.y == cs.y
33283 Roo.log("no change in with or X or Y");
33287 this.currentSize = cs;
33293 layout : function()
33295 this._resetLayout();
33297 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33299 this.layoutItems( isInstant );
33301 this._isLayoutInited = true;
33303 this.fireEvent('layout', this);
33307 _resetLayout : function()
33309 if(this.isHorizontal){
33310 this.horizontalMeasureColumns();
33314 this.verticalMeasureColumns();
33318 verticalMeasureColumns : function()
33320 this.getContainerWidth();
33322 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33323 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33327 var boxWidth = this.boxWidth + this.padWidth;
33329 if(this.containerWidth < this.boxWidth){
33330 boxWidth = this.containerWidth
33333 var containerWidth = this.containerWidth;
33335 var cols = Math.floor(containerWidth / boxWidth);
33337 this.cols = Math.max( cols, 1 );
33339 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33341 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33343 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33345 this.colWidth = boxWidth + avail - this.padWidth;
33347 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33348 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33351 horizontalMeasureColumns : function()
33353 this.getContainerWidth();
33355 var boxWidth = this.boxWidth;
33357 if(this.containerWidth < boxWidth){
33358 boxWidth = this.containerWidth;
33361 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33363 this.el.setHeight(boxWidth);
33367 getContainerWidth : function()
33369 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33372 layoutItems : function( isInstant )
33374 Roo.log(this.bricks);
33376 var items = Roo.apply([], this.bricks);
33378 if(this.isHorizontal){
33379 this._horizontalLayoutItems( items , isInstant );
33383 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33384 // this._verticalAlternativeLayoutItems( items , isInstant );
33388 this._verticalLayoutItems( items , isInstant );
33392 _verticalLayoutItems : function ( items , isInstant)
33394 if ( !items || !items.length ) {
33399 ['xs', 'xs', 'xs', 'tall'],
33400 ['xs', 'xs', 'tall'],
33401 ['xs', 'xs', 'sm'],
33402 ['xs', 'xs', 'xs'],
33408 ['sm', 'xs', 'xs'],
33412 ['tall', 'xs', 'xs', 'xs'],
33413 ['tall', 'xs', 'xs'],
33425 Roo.each(items, function(item, k){
33427 switch (item.size) {
33428 // these layouts take up a full box,
33439 boxes.push([item]);
33462 var filterPattern = function(box, length)
33470 var pattern = box.slice(0, length);
33474 Roo.each(pattern, function(i){
33475 format.push(i.size);
33478 Roo.each(standard, function(s){
33480 if(String(s) != String(format)){
33489 if(!match && length == 1){
33494 filterPattern(box, length - 1);
33498 queue.push(pattern);
33500 box = box.slice(length, box.length);
33502 filterPattern(box, 4);
33508 Roo.each(boxes, function(box, k){
33514 if(box.length == 1){
33519 filterPattern(box, 4);
33523 this._processVerticalLayoutQueue( queue, isInstant );
33527 // _verticalAlternativeLayoutItems : function( items , isInstant )
33529 // if ( !items || !items.length ) {
33533 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33537 _horizontalLayoutItems : function ( items , isInstant)
33539 if ( !items || !items.length || items.length < 3) {
33545 var eItems = items.slice(0, 3);
33547 items = items.slice(3, items.length);
33550 ['xs', 'xs', 'xs', 'wide'],
33551 ['xs', 'xs', 'wide'],
33552 ['xs', 'xs', 'sm'],
33553 ['xs', 'xs', 'xs'],
33559 ['sm', 'xs', 'xs'],
33563 ['wide', 'xs', 'xs', 'xs'],
33564 ['wide', 'xs', 'xs'],
33577 Roo.each(items, function(item, k){
33579 switch (item.size) {
33590 boxes.push([item]);
33614 var filterPattern = function(box, length)
33622 var pattern = box.slice(0, length);
33626 Roo.each(pattern, function(i){
33627 format.push(i.size);
33630 Roo.each(standard, function(s){
33632 if(String(s) != String(format)){
33641 if(!match && length == 1){
33646 filterPattern(box, length - 1);
33650 queue.push(pattern);
33652 box = box.slice(length, box.length);
33654 filterPattern(box, 4);
33660 Roo.each(boxes, function(box, k){
33666 if(box.length == 1){
33671 filterPattern(box, 4);
33678 var pos = this.el.getBox(true);
33682 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33684 var hit_end = false;
33686 Roo.each(queue, function(box){
33690 Roo.each(box, function(b){
33692 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33702 Roo.each(box, function(b){
33704 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33707 mx = Math.max(mx, b.x);
33711 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33715 Roo.each(box, function(b){
33717 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33731 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33734 /** Sets position of item in DOM
33735 * @param {Element} item
33736 * @param {Number} x - horizontal position
33737 * @param {Number} y - vertical position
33738 * @param {Boolean} isInstant - disables transitions
33740 _processVerticalLayoutQueue : function( queue, isInstant )
33742 var pos = this.el.getBox(true);
33747 for (var i = 0; i < this.cols; i++){
33751 Roo.each(queue, function(box, k){
33753 var col = k % this.cols;
33755 Roo.each(box, function(b,kk){
33757 b.el.position('absolute');
33759 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33760 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33762 if(b.size == 'md-left' || b.size == 'md-right'){
33763 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33764 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33767 b.el.setWidth(width);
33768 b.el.setHeight(height);
33770 b.el.select('iframe',true).setSize(width,height);
33774 for (var i = 0; i < this.cols; i++){
33776 if(maxY[i] < maxY[col]){
33781 col = Math.min(col, i);
33785 x = pos.x + col * (this.colWidth + this.padWidth);
33789 var positions = [];
33791 switch (box.length){
33793 positions = this.getVerticalOneBoxColPositions(x, y, box);
33796 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33799 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33802 positions = this.getVerticalFourBoxColPositions(x, y, box);
33808 Roo.each(box, function(b,kk){
33810 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33812 var sz = b.el.getSize();
33814 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33822 for (var i = 0; i < this.cols; i++){
33823 mY = Math.max(mY, maxY[i]);
33826 this.el.setHeight(mY - pos.y);
33830 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33832 // var pos = this.el.getBox(true);
33835 // var maxX = pos.right;
33837 // var maxHeight = 0;
33839 // Roo.each(items, function(item, k){
33843 // item.el.position('absolute');
33845 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33847 // item.el.setWidth(width);
33849 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33851 // item.el.setHeight(height);
33854 // item.el.setXY([x, y], isInstant ? false : true);
33856 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33859 // y = y + height + this.alternativePadWidth;
33861 // maxHeight = maxHeight + height + this.alternativePadWidth;
33865 // this.el.setHeight(maxHeight);
33869 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33871 var pos = this.el.getBox(true);
33876 var maxX = pos.right;
33878 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33880 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33882 Roo.each(queue, function(box, k){
33884 Roo.each(box, function(b, kk){
33886 b.el.position('absolute');
33888 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33889 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33891 if(b.size == 'md-left' || b.size == 'md-right'){
33892 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33893 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33896 b.el.setWidth(width);
33897 b.el.setHeight(height);
33905 var positions = [];
33907 switch (box.length){
33909 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33912 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33915 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33918 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33924 Roo.each(box, function(b,kk){
33926 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33928 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33936 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33938 Roo.each(eItems, function(b,k){
33940 b.size = (k == 0) ? 'sm' : 'xs';
33941 b.x = (k == 0) ? 2 : 1;
33942 b.y = (k == 0) ? 2 : 1;
33944 b.el.position('absolute');
33946 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33948 b.el.setWidth(width);
33950 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33952 b.el.setHeight(height);
33956 var positions = [];
33959 x : maxX - this.unitWidth * 2 - this.gutter,
33964 x : maxX - this.unitWidth,
33965 y : minY + (this.unitWidth + this.gutter) * 2
33969 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33973 Roo.each(eItems, function(b,k){
33975 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33981 getVerticalOneBoxColPositions : function(x, y, box)
33985 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33987 if(box[0].size == 'md-left'){
33991 if(box[0].size == 'md-right'){
33996 x : x + (this.unitWidth + this.gutter) * rand,
34003 getVerticalTwoBoxColPositions : function(x, y, box)
34007 if(box[0].size == 'xs'){
34011 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34015 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34029 x : x + (this.unitWidth + this.gutter) * 2,
34030 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34037 getVerticalThreeBoxColPositions : function(x, y, box)
34041 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34049 x : x + (this.unitWidth + this.gutter) * 1,
34054 x : x + (this.unitWidth + this.gutter) * 2,
34062 if(box[0].size == 'xs' && box[1].size == 'xs'){
34071 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34075 x : x + (this.unitWidth + this.gutter) * 1,
34089 x : x + (this.unitWidth + this.gutter) * 2,
34094 x : x + (this.unitWidth + this.gutter) * 2,
34095 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34102 getVerticalFourBoxColPositions : function(x, y, box)
34106 if(box[0].size == 'xs'){
34115 y : y + (this.unitHeight + this.gutter) * 1
34120 y : y + (this.unitHeight + this.gutter) * 2
34124 x : x + (this.unitWidth + this.gutter) * 1,
34138 x : x + (this.unitWidth + this.gutter) * 2,
34143 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34144 y : y + (this.unitHeight + this.gutter) * 1
34148 x : x + (this.unitWidth + this.gutter) * 2,
34149 y : y + (this.unitWidth + this.gutter) * 2
34156 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34160 if(box[0].size == 'md-left'){
34162 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34169 if(box[0].size == 'md-right'){
34171 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34172 y : minY + (this.unitWidth + this.gutter) * 1
34178 var rand = Math.floor(Math.random() * (4 - box[0].y));
34181 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34182 y : minY + (this.unitWidth + this.gutter) * rand
34189 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34193 if(box[0].size == 'xs'){
34196 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34201 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34202 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34210 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34215 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34216 y : minY + (this.unitWidth + this.gutter) * 2
34223 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34227 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34230 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34235 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34236 y : minY + (this.unitWidth + this.gutter) * 1
34240 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34241 y : minY + (this.unitWidth + this.gutter) * 2
34248 if(box[0].size == 'xs' && box[1].size == 'xs'){
34251 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34256 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34261 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34262 y : minY + (this.unitWidth + this.gutter) * 1
34270 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34275 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34276 y : minY + (this.unitWidth + this.gutter) * 2
34280 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34281 y : minY + (this.unitWidth + this.gutter) * 2
34288 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34292 if(box[0].size == 'xs'){
34295 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34300 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34305 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),
34310 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34311 y : minY + (this.unitWidth + this.gutter) * 1
34319 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34324 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34325 y : minY + (this.unitWidth + this.gutter) * 2
34329 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34330 y : minY + (this.unitWidth + this.gutter) * 2
34334 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),
34335 y : minY + (this.unitWidth + this.gutter) * 2
34343 * remove a Masonry Brick
34344 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34346 removeBrick : function(brick_id)
34352 for (var i = 0; i<this.bricks.length; i++) {
34353 if (this.bricks[i].id == brick_id) {
34354 this.bricks.splice(i,1);
34355 this.el.dom.removeChild(Roo.get(brick_id).dom);
34362 * adds a Masonry Brick
34363 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34365 addBrick : function(cfg)
34367 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34368 //this.register(cn);
34369 cn.parentId = this.id;
34370 cn.render(this.el);
34375 * register a Masonry Brick
34376 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34379 register : function(brick)
34381 this.bricks.push(brick);
34382 brick.masonryId = this.id;
34386 * clear all the Masonry Brick
34388 clearAll : function()
34391 //this.getChildContainer().dom.innerHTML = "";
34392 this.el.dom.innerHTML = '';
34395 getSelected : function()
34397 if (!this.selectedBrick) {
34401 return this.selectedBrick;
34405 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34409 * register a Masonry Layout
34410 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34413 register : function(layout)
34415 this.groups[layout.id] = layout;
34418 * fetch a Masonry Layout based on the masonry layout ID
34419 * @param {string} the masonry layout to add
34420 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34423 get: function(layout_id) {
34424 if (typeof(this.groups[layout_id]) == 'undefined') {
34427 return this.groups[layout_id] ;
34439 * http://masonry.desandro.com
34441 * The idea is to render all the bricks based on vertical width...
34443 * The original code extends 'outlayer' - we might need to use that....
34449 * @class Roo.bootstrap.LayoutMasonryAuto
34450 * @extends Roo.bootstrap.Component
34451 * Bootstrap Layout Masonry class
34454 * Create a new Element
34455 * @param {Object} config The config object
34458 Roo.bootstrap.LayoutMasonryAuto = function(config){
34459 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34462 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34465 * @cfg {Boolean} isFitWidth - resize the width..
34467 isFitWidth : false, // options..
34469 * @cfg {Boolean} isOriginLeft = left align?
34471 isOriginLeft : true,
34473 * @cfg {Boolean} isOriginTop = top align?
34475 isOriginTop : false,
34477 * @cfg {Boolean} isLayoutInstant = no animation?
34479 isLayoutInstant : false, // needed?
34481 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34483 isResizingContainer : true,
34485 * @cfg {Number} columnWidth width of the columns
34491 * @cfg {Number} maxCols maximum number of columns
34496 * @cfg {Number} padHeight padding below box..
34502 * @cfg {Boolean} isAutoInitial defalut true
34505 isAutoInitial : true,
34511 initialColumnWidth : 0,
34512 currentSize : null,
34514 colYs : null, // array.
34521 bricks: null, //CompositeElement
34522 cols : 0, // array?
34523 // element : null, // wrapped now this.el
34524 _isLayoutInited : null,
34527 getAutoCreate : function(){
34531 cls: 'blog-masonary-wrapper ' + this.cls,
34533 cls : 'mas-boxes masonary'
34540 getChildContainer: function( )
34542 if (this.boxesEl) {
34543 return this.boxesEl;
34546 this.boxesEl = this.el.select('.mas-boxes').first();
34548 return this.boxesEl;
34552 initEvents : function()
34556 if(this.isAutoInitial){
34557 Roo.log('hook children rendered');
34558 this.on('childrenrendered', function() {
34559 Roo.log('children rendered');
34566 initial : function()
34568 this.reloadItems();
34570 this.currentSize = this.el.getBox(true);
34572 /// was window resize... - let's see if this works..
34573 Roo.EventManager.onWindowResize(this.resize, this);
34575 if(!this.isAutoInitial){
34580 this.layout.defer(500,this);
34583 reloadItems: function()
34585 this.bricks = this.el.select('.masonry-brick', true);
34587 this.bricks.each(function(b) {
34588 //Roo.log(b.getSize());
34589 if (!b.attr('originalwidth')) {
34590 b.attr('originalwidth', b.getSize().width);
34595 Roo.log(this.bricks.elements.length);
34598 resize : function()
34601 var cs = this.el.getBox(true);
34603 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34604 Roo.log("no change in with or X");
34607 this.currentSize = cs;
34611 layout : function()
34614 this._resetLayout();
34615 //this._manageStamps();
34617 // don't animate first layout
34618 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34619 this.layoutItems( isInstant );
34621 // flag for initalized
34622 this._isLayoutInited = true;
34625 layoutItems : function( isInstant )
34627 //var items = this._getItemsForLayout( this.items );
34628 // original code supports filtering layout items.. we just ignore it..
34630 this._layoutItems( this.bricks , isInstant );
34632 this._postLayout();
34634 _layoutItems : function ( items , isInstant)
34636 //this.fireEvent( 'layout', this, items );
34639 if ( !items || !items.elements.length ) {
34640 // no items, emit event with empty array
34645 items.each(function(item) {
34646 Roo.log("layout item");
34648 // get x/y object from method
34649 var position = this._getItemLayoutPosition( item );
34651 position.item = item;
34652 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34653 queue.push( position );
34656 this._processLayoutQueue( queue );
34658 /** Sets position of item in DOM
34659 * @param {Element} item
34660 * @param {Number} x - horizontal position
34661 * @param {Number} y - vertical position
34662 * @param {Boolean} isInstant - disables transitions
34664 _processLayoutQueue : function( queue )
34666 for ( var i=0, len = queue.length; i < len; i++ ) {
34667 var obj = queue[i];
34668 obj.item.position('absolute');
34669 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34675 * Any logic you want to do after each layout,
34676 * i.e. size the container
34678 _postLayout : function()
34680 this.resizeContainer();
34683 resizeContainer : function()
34685 if ( !this.isResizingContainer ) {
34688 var size = this._getContainerSize();
34690 this.el.setSize(size.width,size.height);
34691 this.boxesEl.setSize(size.width,size.height);
34697 _resetLayout : function()
34699 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34700 this.colWidth = this.el.getWidth();
34701 //this.gutter = this.el.getWidth();
34703 this.measureColumns();
34709 this.colYs.push( 0 );
34715 measureColumns : function()
34717 this.getContainerWidth();
34718 // if columnWidth is 0, default to outerWidth of first item
34719 if ( !this.columnWidth ) {
34720 var firstItem = this.bricks.first();
34721 Roo.log(firstItem);
34722 this.columnWidth = this.containerWidth;
34723 if (firstItem && firstItem.attr('originalwidth') ) {
34724 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34726 // columnWidth fall back to item of first element
34727 Roo.log("set column width?");
34728 this.initialColumnWidth = this.columnWidth ;
34730 // if first elem has no width, default to size of container
34735 if (this.initialColumnWidth) {
34736 this.columnWidth = this.initialColumnWidth;
34741 // column width is fixed at the top - however if container width get's smaller we should
34744 // this bit calcs how man columns..
34746 var columnWidth = this.columnWidth += this.gutter;
34748 // calculate columns
34749 var containerWidth = this.containerWidth + this.gutter;
34751 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34752 // fix rounding errors, typically with gutters
34753 var excess = columnWidth - containerWidth % columnWidth;
34756 // if overshoot is less than a pixel, round up, otherwise floor it
34757 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34758 cols = Math[ mathMethod ]( cols );
34759 this.cols = Math.max( cols, 1 );
34760 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34762 // padding positioning..
34763 var totalColWidth = this.cols * this.columnWidth;
34764 var padavail = this.containerWidth - totalColWidth;
34765 // so for 2 columns - we need 3 'pads'
34767 var padNeeded = (1+this.cols) * this.padWidth;
34769 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34771 this.columnWidth += padExtra
34772 //this.padWidth = Math.floor(padavail / ( this.cols));
34774 // adjust colum width so that padding is fixed??
34776 // we have 3 columns ... total = width * 3
34777 // we have X left over... that should be used by
34779 //if (this.expandC) {
34787 getContainerWidth : function()
34789 /* // container is parent if fit width
34790 var container = this.isFitWidth ? this.element.parentNode : this.element;
34791 // check that this.size and size are there
34792 // IE8 triggers resize on body size change, so they might not be
34794 var size = getSize( container ); //FIXME
34795 this.containerWidth = size && size.innerWidth; //FIXME
34798 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34802 _getItemLayoutPosition : function( item ) // what is item?
34804 // we resize the item to our columnWidth..
34806 item.setWidth(this.columnWidth);
34807 item.autoBoxAdjust = false;
34809 var sz = item.getSize();
34811 // how many columns does this brick span
34812 var remainder = this.containerWidth % this.columnWidth;
34814 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34815 // round if off by 1 pixel, otherwise use ceil
34816 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34817 colSpan = Math.min( colSpan, this.cols );
34819 // normally this should be '1' as we dont' currently allow multi width columns..
34821 var colGroup = this._getColGroup( colSpan );
34822 // get the minimum Y value from the columns
34823 var minimumY = Math.min.apply( Math, colGroup );
34824 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34826 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34828 // position the brick
34830 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34831 y: this.currentSize.y + minimumY + this.padHeight
34835 // apply setHeight to necessary columns
34836 var setHeight = minimumY + sz.height + this.padHeight;
34837 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34839 var setSpan = this.cols + 1 - colGroup.length;
34840 for ( var i = 0; i < setSpan; i++ ) {
34841 this.colYs[ shortColIndex + i ] = setHeight ;
34848 * @param {Number} colSpan - number of columns the element spans
34849 * @returns {Array} colGroup
34851 _getColGroup : function( colSpan )
34853 if ( colSpan < 2 ) {
34854 // if brick spans only one column, use all the column Ys
34859 // how many different places could this brick fit horizontally
34860 var groupCount = this.cols + 1 - colSpan;
34861 // for each group potential horizontal position
34862 for ( var i = 0; i < groupCount; i++ ) {
34863 // make an array of colY values for that one group
34864 var groupColYs = this.colYs.slice( i, i + colSpan );
34865 // and get the max value of the array
34866 colGroup[i] = Math.max.apply( Math, groupColYs );
34871 _manageStamp : function( stamp )
34873 var stampSize = stamp.getSize();
34874 var offset = stamp.getBox();
34875 // get the columns that this stamp affects
34876 var firstX = this.isOriginLeft ? offset.x : offset.right;
34877 var lastX = firstX + stampSize.width;
34878 var firstCol = Math.floor( firstX / this.columnWidth );
34879 firstCol = Math.max( 0, firstCol );
34881 var lastCol = Math.floor( lastX / this.columnWidth );
34882 // lastCol should not go over if multiple of columnWidth #425
34883 lastCol -= lastX % this.columnWidth ? 0 : 1;
34884 lastCol = Math.min( this.cols - 1, lastCol );
34886 // set colYs to bottom of the stamp
34887 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34890 for ( var i = firstCol; i <= lastCol; i++ ) {
34891 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34896 _getContainerSize : function()
34898 this.maxY = Math.max.apply( Math, this.colYs );
34903 if ( this.isFitWidth ) {
34904 size.width = this._getContainerFitWidth();
34910 _getContainerFitWidth : function()
34912 var unusedCols = 0;
34913 // count unused columns
34916 if ( this.colYs[i] !== 0 ) {
34921 // fit container to columns that have been used
34922 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34925 needsResizeLayout : function()
34927 var previousWidth = this.containerWidth;
34928 this.getContainerWidth();
34929 return previousWidth !== this.containerWidth;
34944 * @class Roo.bootstrap.MasonryBrick
34945 * @extends Roo.bootstrap.Component
34946 * Bootstrap MasonryBrick class
34949 * Create a new MasonryBrick
34950 * @param {Object} config The config object
34953 Roo.bootstrap.MasonryBrick = function(config){
34955 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34957 Roo.bootstrap.MasonryBrick.register(this);
34963 * When a MasonryBrick is clcik
34964 * @param {Roo.bootstrap.MasonryBrick} this
34965 * @param {Roo.EventObject} e
34971 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34974 * @cfg {String} title
34978 * @cfg {String} html
34982 * @cfg {String} bgimage
34986 * @cfg {String} videourl
34990 * @cfg {String} cls
34994 * @cfg {String} href
34998 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35003 * @cfg {String} placetitle (center|bottom)
35008 * @cfg {Boolean} isFitContainer defalut true
35010 isFitContainer : true,
35013 * @cfg {Boolean} preventDefault defalut false
35015 preventDefault : false,
35018 * @cfg {Boolean} inverse defalut false
35020 maskInverse : false,
35022 getAutoCreate : function()
35024 if(!this.isFitContainer){
35025 return this.getSplitAutoCreate();
35028 var cls = 'masonry-brick masonry-brick-full';
35030 if(this.href.length){
35031 cls += ' masonry-brick-link';
35034 if(this.bgimage.length){
35035 cls += ' masonry-brick-image';
35038 if(this.maskInverse){
35039 cls += ' mask-inverse';
35042 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35043 cls += ' enable-mask';
35047 cls += ' masonry-' + this.size + '-brick';
35050 if(this.placetitle.length){
35052 switch (this.placetitle) {
35054 cls += ' masonry-center-title';
35057 cls += ' masonry-bottom-title';
35064 if(!this.html.length && !this.bgimage.length){
35065 cls += ' masonry-center-title';
35068 if(!this.html.length && this.bgimage.length){
35069 cls += ' masonry-bottom-title';
35074 cls += ' ' + this.cls;
35078 tag: (this.href.length) ? 'a' : 'div',
35083 cls: 'masonry-brick-mask'
35087 cls: 'masonry-brick-paragraph',
35093 if(this.href.length){
35094 cfg.href = this.href;
35097 var cn = cfg.cn[1].cn;
35099 if(this.title.length){
35102 cls: 'masonry-brick-title',
35107 if(this.html.length){
35110 cls: 'masonry-brick-text',
35115 if (!this.title.length && !this.html.length) {
35116 cfg.cn[1].cls += ' hide';
35119 if(this.bgimage.length){
35122 cls: 'masonry-brick-image-view',
35127 if(this.videourl.length){
35128 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35129 // youtube support only?
35132 cls: 'masonry-brick-image-view',
35135 allowfullscreen : true
35143 getSplitAutoCreate : function()
35145 var cls = 'masonry-brick masonry-brick-split';
35147 if(this.href.length){
35148 cls += ' masonry-brick-link';
35151 if(this.bgimage.length){
35152 cls += ' masonry-brick-image';
35156 cls += ' masonry-' + this.size + '-brick';
35159 switch (this.placetitle) {
35161 cls += ' masonry-center-title';
35164 cls += ' masonry-bottom-title';
35167 if(!this.bgimage.length){
35168 cls += ' masonry-center-title';
35171 if(this.bgimage.length){
35172 cls += ' masonry-bottom-title';
35178 cls += ' ' + this.cls;
35182 tag: (this.href.length) ? 'a' : 'div',
35187 cls: 'masonry-brick-split-head',
35191 cls: 'masonry-brick-paragraph',
35198 cls: 'masonry-brick-split-body',
35204 if(this.href.length){
35205 cfg.href = this.href;
35208 if(this.title.length){
35209 cfg.cn[0].cn[0].cn.push({
35211 cls: 'masonry-brick-title',
35216 if(this.html.length){
35217 cfg.cn[1].cn.push({
35219 cls: 'masonry-brick-text',
35224 if(this.bgimage.length){
35225 cfg.cn[0].cn.push({
35227 cls: 'masonry-brick-image-view',
35232 if(this.videourl.length){
35233 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35234 // youtube support only?
35235 cfg.cn[0].cn.cn.push({
35237 cls: 'masonry-brick-image-view',
35240 allowfullscreen : true
35247 initEvents: function()
35249 switch (this.size) {
35282 this.el.on('touchstart', this.onTouchStart, this);
35283 this.el.on('touchmove', this.onTouchMove, this);
35284 this.el.on('touchend', this.onTouchEnd, this);
35285 this.el.on('contextmenu', this.onContextMenu, this);
35287 this.el.on('mouseenter' ,this.enter, this);
35288 this.el.on('mouseleave', this.leave, this);
35289 this.el.on('click', this.onClick, this);
35292 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35293 this.parent().bricks.push(this);
35298 onClick: function(e, el)
35300 var time = this.endTimer - this.startTimer;
35301 // Roo.log(e.preventDefault());
35304 e.preventDefault();
35309 if(!this.preventDefault){
35313 e.preventDefault();
35315 if (this.activeClass != '') {
35316 this.selectBrick();
35319 this.fireEvent('click', this, e);
35322 enter: function(e, el)
35324 e.preventDefault();
35326 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35330 if(this.bgimage.length && this.html.length){
35331 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35335 leave: function(e, el)
35337 e.preventDefault();
35339 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35343 if(this.bgimage.length && this.html.length){
35344 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35348 onTouchStart: function(e, el)
35350 // e.preventDefault();
35352 this.touchmoved = false;
35354 if(!this.isFitContainer){
35358 if(!this.bgimage.length || !this.html.length){
35362 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35364 this.timer = new Date().getTime();
35368 onTouchMove: function(e, el)
35370 this.touchmoved = true;
35373 onContextMenu : function(e,el)
35375 e.preventDefault();
35376 e.stopPropagation();
35380 onTouchEnd: function(e, el)
35382 // e.preventDefault();
35384 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35391 if(!this.bgimage.length || !this.html.length){
35393 if(this.href.length){
35394 window.location.href = this.href;
35400 if(!this.isFitContainer){
35404 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35406 window.location.href = this.href;
35409 //selection on single brick only
35410 selectBrick : function() {
35412 if (!this.parentId) {
35416 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35417 var index = m.selectedBrick.indexOf(this.id);
35420 m.selectedBrick.splice(index,1);
35421 this.el.removeClass(this.activeClass);
35425 for(var i = 0; i < m.selectedBrick.length; i++) {
35426 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35427 b.el.removeClass(b.activeClass);
35430 m.selectedBrick = [];
35432 m.selectedBrick.push(this.id);
35433 this.el.addClass(this.activeClass);
35437 isSelected : function(){
35438 return this.el.hasClass(this.activeClass);
35443 Roo.apply(Roo.bootstrap.MasonryBrick, {
35446 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35448 * register a Masonry Brick
35449 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35452 register : function(brick)
35454 //this.groups[brick.id] = brick;
35455 this.groups.add(brick.id, brick);
35458 * fetch a masonry brick based on the masonry brick ID
35459 * @param {string} the masonry brick to add
35460 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35463 get: function(brick_id)
35465 // if (typeof(this.groups[brick_id]) == 'undefined') {
35468 // return this.groups[brick_id] ;
35470 if(this.groups.key(brick_id)) {
35471 return this.groups.key(brick_id);
35489 * @class Roo.bootstrap.Brick
35490 * @extends Roo.bootstrap.Component
35491 * Bootstrap Brick class
35494 * Create a new Brick
35495 * @param {Object} config The config object
35498 Roo.bootstrap.Brick = function(config){
35499 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35505 * When a Brick is click
35506 * @param {Roo.bootstrap.Brick} this
35507 * @param {Roo.EventObject} e
35513 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35516 * @cfg {String} title
35520 * @cfg {String} html
35524 * @cfg {String} bgimage
35528 * @cfg {String} cls
35532 * @cfg {String} href
35536 * @cfg {String} video
35540 * @cfg {Boolean} square
35544 getAutoCreate : function()
35546 var cls = 'roo-brick';
35548 if(this.href.length){
35549 cls += ' roo-brick-link';
35552 if(this.bgimage.length){
35553 cls += ' roo-brick-image';
35556 if(!this.html.length && !this.bgimage.length){
35557 cls += ' roo-brick-center-title';
35560 if(!this.html.length && this.bgimage.length){
35561 cls += ' roo-brick-bottom-title';
35565 cls += ' ' + this.cls;
35569 tag: (this.href.length) ? 'a' : 'div',
35574 cls: 'roo-brick-paragraph',
35580 if(this.href.length){
35581 cfg.href = this.href;
35584 var cn = cfg.cn[0].cn;
35586 if(this.title.length){
35589 cls: 'roo-brick-title',
35594 if(this.html.length){
35597 cls: 'roo-brick-text',
35604 if(this.bgimage.length){
35607 cls: 'roo-brick-image-view',
35615 initEvents: function()
35617 if(this.title.length || this.html.length){
35618 this.el.on('mouseenter' ,this.enter, this);
35619 this.el.on('mouseleave', this.leave, this);
35622 Roo.EventManager.onWindowResize(this.resize, this);
35624 if(this.bgimage.length){
35625 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35626 this.imageEl.on('load', this.onImageLoad, this);
35633 onImageLoad : function()
35638 resize : function()
35640 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35642 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35644 if(this.bgimage.length){
35645 var image = this.el.select('.roo-brick-image-view', true).first();
35647 image.setWidth(paragraph.getWidth());
35650 image.setHeight(paragraph.getWidth());
35653 this.el.setHeight(image.getHeight());
35654 paragraph.setHeight(image.getHeight());
35660 enter: function(e, el)
35662 e.preventDefault();
35664 if(this.bgimage.length){
35665 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35666 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35670 leave: function(e, el)
35672 e.preventDefault();
35674 if(this.bgimage.length){
35675 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35676 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35691 * @class Roo.bootstrap.NumberField
35692 * @extends Roo.bootstrap.Input
35693 * Bootstrap NumberField class
35699 * Create a new NumberField
35700 * @param {Object} config The config object
35703 Roo.bootstrap.NumberField = function(config){
35704 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35707 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35710 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35712 allowDecimals : true,
35714 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35716 decimalSeparator : ".",
35718 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35720 decimalPrecision : 2,
35722 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35724 allowNegative : true,
35727 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35731 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35733 minValue : Number.NEGATIVE_INFINITY,
35735 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35737 maxValue : Number.MAX_VALUE,
35739 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35741 minText : "The minimum value for this field is {0}",
35743 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35745 maxText : "The maximum value for this field is {0}",
35747 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35748 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35750 nanText : "{0} is not a valid number",
35752 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35754 thousandsDelimiter : false,
35756 * @cfg {String} valueAlign alignment of value
35758 valueAlign : "left",
35760 getAutoCreate : function()
35762 var hiddenInput = {
35766 cls: 'hidden-number-input'
35770 hiddenInput.name = this.name;
35775 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35777 this.name = hiddenInput.name;
35779 if(cfg.cn.length > 0) {
35780 cfg.cn.push(hiddenInput);
35787 initEvents : function()
35789 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35791 var allowed = "0123456789";
35793 if(this.allowDecimals){
35794 allowed += this.decimalSeparator;
35797 if(this.allowNegative){
35801 if(this.thousandsDelimiter) {
35805 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35807 var keyPress = function(e){
35809 var k = e.getKey();
35811 var c = e.getCharCode();
35814 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35815 allowed.indexOf(String.fromCharCode(c)) === -1
35821 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35825 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35830 this.el.on("keypress", keyPress, this);
35833 validateValue : function(value)
35836 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35840 var num = this.parseValue(value);
35843 this.markInvalid(String.format(this.nanText, value));
35847 if(num < this.minValue){
35848 this.markInvalid(String.format(this.minText, this.minValue));
35852 if(num > this.maxValue){
35853 this.markInvalid(String.format(this.maxText, this.maxValue));
35860 getValue : function()
35862 var v = this.hiddenEl().getValue();
35864 return this.fixPrecision(this.parseValue(v));
35867 parseValue : function(value)
35869 if(this.thousandsDelimiter) {
35871 r = new RegExp(",", "g");
35872 value = value.replace(r, "");
35875 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35876 return isNaN(value) ? '' : value;
35879 fixPrecision : function(value)
35881 if(this.thousandsDelimiter) {
35883 r = new RegExp(",", "g");
35884 value = value.replace(r, "");
35887 var nan = isNaN(value);
35889 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35890 return nan ? '' : value;
35892 return parseFloat(value).toFixed(this.decimalPrecision);
35895 setValue : function(v)
35897 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35903 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35905 this.inputEl().dom.value = (v == '') ? '' :
35906 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35908 if(!this.allowZero && v === '0') {
35909 this.hiddenEl().dom.value = '';
35910 this.inputEl().dom.value = '';
35917 decimalPrecisionFcn : function(v)
35919 return Math.floor(v);
35922 beforeBlur : function()
35924 var v = this.parseValue(this.getRawValue());
35926 if(v || v === 0 || v === ''){
35931 hiddenEl : function()
35933 return this.el.select('input.hidden-number-input',true).first();
35945 * @class Roo.bootstrap.DocumentSlider
35946 * @extends Roo.bootstrap.Component
35947 * Bootstrap DocumentSlider class
35950 * Create a new DocumentViewer
35951 * @param {Object} config The config object
35954 Roo.bootstrap.DocumentSlider = function(config){
35955 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35962 * Fire after initEvent
35963 * @param {Roo.bootstrap.DocumentSlider} this
35968 * Fire after update
35969 * @param {Roo.bootstrap.DocumentSlider} this
35975 * @param {Roo.bootstrap.DocumentSlider} this
35981 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35987 getAutoCreate : function()
35991 cls : 'roo-document-slider',
35995 cls : 'roo-document-slider-header',
35999 cls : 'roo-document-slider-header-title'
36005 cls : 'roo-document-slider-body',
36009 cls : 'roo-document-slider-prev',
36013 cls : 'fa fa-chevron-left'
36019 cls : 'roo-document-slider-thumb',
36023 cls : 'roo-document-slider-image'
36029 cls : 'roo-document-slider-next',
36033 cls : 'fa fa-chevron-right'
36045 initEvents : function()
36047 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36048 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36050 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36051 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36053 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36054 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36056 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36057 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36059 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36060 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36062 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36063 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36065 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36066 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36068 this.thumbEl.on('click', this.onClick, this);
36070 this.prevIndicator.on('click', this.prev, this);
36072 this.nextIndicator.on('click', this.next, this);
36076 initial : function()
36078 if(this.files.length){
36079 this.indicator = 1;
36083 this.fireEvent('initial', this);
36086 update : function()
36088 this.imageEl.attr('src', this.files[this.indicator - 1]);
36090 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36092 this.prevIndicator.show();
36094 if(this.indicator == 1){
36095 this.prevIndicator.hide();
36098 this.nextIndicator.show();
36100 if(this.indicator == this.files.length){
36101 this.nextIndicator.hide();
36104 this.thumbEl.scrollTo('top');
36106 this.fireEvent('update', this);
36109 onClick : function(e)
36111 e.preventDefault();
36113 this.fireEvent('click', this);
36118 e.preventDefault();
36120 this.indicator = Math.max(1, this.indicator - 1);
36127 e.preventDefault();
36129 this.indicator = Math.min(this.files.length, this.indicator + 1);
36143 * @class Roo.bootstrap.RadioSet
36144 * @extends Roo.bootstrap.Input
36145 * Bootstrap RadioSet class
36146 * @cfg {String} indicatorpos (left|right) default left
36147 * @cfg {Boolean} inline (true|false) inline the element (default true)
36148 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36150 * Create a new RadioSet
36151 * @param {Object} config The config object
36154 Roo.bootstrap.RadioSet = function(config){
36156 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36160 Roo.bootstrap.RadioSet.register(this);
36165 * Fires when the element is checked or unchecked.
36166 * @param {Roo.bootstrap.RadioSet} this This radio
36167 * @param {Roo.bootstrap.Radio} item The checked item
36172 * Fires when the element is click.
36173 * @param {Roo.bootstrap.RadioSet} this This radio set
36174 * @param {Roo.bootstrap.Radio} item The checked item
36175 * @param {Roo.EventObject} e The event object
36182 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36190 indicatorpos : 'left',
36192 getAutoCreate : function()
36196 cls : 'roo-radio-set-label',
36200 html : this.fieldLabel
36204 if (Roo.bootstrap.version == 3) {
36207 if(this.indicatorpos == 'left'){
36210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36211 tooltip : 'This field is required'
36216 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36217 tooltip : 'This field is required'
36223 cls : 'roo-radio-set-items'
36226 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36228 if (align === 'left' && this.fieldLabel.length) {
36231 cls : "roo-radio-set-right",
36237 if(this.labelWidth > 12){
36238 label.style = "width: " + this.labelWidth + 'px';
36241 if(this.labelWidth < 13 && this.labelmd == 0){
36242 this.labelmd = this.labelWidth;
36245 if(this.labellg > 0){
36246 label.cls += ' col-lg-' + this.labellg;
36247 items.cls += ' col-lg-' + (12 - this.labellg);
36250 if(this.labelmd > 0){
36251 label.cls += ' col-md-' + this.labelmd;
36252 items.cls += ' col-md-' + (12 - this.labelmd);
36255 if(this.labelsm > 0){
36256 label.cls += ' col-sm-' + this.labelsm;
36257 items.cls += ' col-sm-' + (12 - this.labelsm);
36260 if(this.labelxs > 0){
36261 label.cls += ' col-xs-' + this.labelxs;
36262 items.cls += ' col-xs-' + (12 - this.labelxs);
36268 cls : 'roo-radio-set',
36272 cls : 'roo-radio-set-input',
36275 value : this.value ? this.value : ''
36282 if(this.weight.length){
36283 cfg.cls += ' roo-radio-' + this.weight;
36287 cfg.cls += ' roo-radio-set-inline';
36291 ['xs','sm','md','lg'].map(function(size){
36292 if (settings[size]) {
36293 cfg.cls += ' col-' + size + '-' + settings[size];
36301 initEvents : function()
36303 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36304 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36306 if(!this.fieldLabel.length){
36307 this.labelEl.hide();
36310 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36311 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36313 this.indicator = this.indicatorEl();
36315 if(this.indicator){
36316 this.indicator.addClass('invisible');
36319 this.originalValue = this.getValue();
36323 inputEl: function ()
36325 return this.el.select('.roo-radio-set-input', true).first();
36328 getChildContainer : function()
36330 return this.itemsEl;
36333 register : function(item)
36335 this.radioes.push(item);
36339 validate : function()
36341 if(this.getVisibilityEl().hasClass('hidden')){
36347 Roo.each(this.radioes, function(i){
36356 if(this.allowBlank) {
36360 if(this.disabled || valid){
36365 this.markInvalid();
36370 markValid : function()
36372 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36373 this.indicatorEl().removeClass('visible');
36374 this.indicatorEl().addClass('invisible');
36378 if (Roo.bootstrap.version == 3) {
36379 this.el.removeClass([this.invalidClass, this.validClass]);
36380 this.el.addClass(this.validClass);
36382 this.el.removeClass(['is-invalid','is-valid']);
36383 this.el.addClass(['is-valid']);
36385 this.fireEvent('valid', this);
36388 markInvalid : function(msg)
36390 if(this.allowBlank || this.disabled){
36394 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36395 this.indicatorEl().removeClass('invisible');
36396 this.indicatorEl().addClass('visible');
36398 if (Roo.bootstrap.version == 3) {
36399 this.el.removeClass([this.invalidClass, this.validClass]);
36400 this.el.addClass(this.invalidClass);
36402 this.el.removeClass(['is-invalid','is-valid']);
36403 this.el.addClass(['is-invalid']);
36406 this.fireEvent('invalid', this, msg);
36410 setValue : function(v, suppressEvent)
36412 if(this.value === v){
36419 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36422 Roo.each(this.radioes, function(i){
36424 i.el.removeClass('checked');
36427 Roo.each(this.radioes, function(i){
36429 if(i.value === v || i.value.toString() === v.toString()){
36431 i.el.addClass('checked');
36433 if(suppressEvent !== true){
36434 this.fireEvent('check', this, i);
36445 clearInvalid : function(){
36447 if(!this.el || this.preventMark){
36451 this.el.removeClass([this.invalidClass]);
36453 this.fireEvent('valid', this);
36458 Roo.apply(Roo.bootstrap.RadioSet, {
36462 register : function(set)
36464 this.groups[set.name] = set;
36467 get: function(name)
36469 if (typeof(this.groups[name]) == 'undefined') {
36473 return this.groups[name] ;
36479 * Ext JS Library 1.1.1
36480 * Copyright(c) 2006-2007, Ext JS, LLC.
36482 * Originally Released Under LGPL - original licence link has changed is not relivant.
36485 * <script type="text/javascript">
36490 * @class Roo.bootstrap.SplitBar
36491 * @extends Roo.util.Observable
36492 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36496 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36497 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36498 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36499 split.minSize = 100;
36500 split.maxSize = 600;
36501 split.animate = true;
36502 split.on('moved', splitterMoved);
36505 * Create a new SplitBar
36506 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36507 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36508 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36509 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36510 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36511 position of the SplitBar).
36513 Roo.bootstrap.SplitBar = function(cfg){
36518 // dragElement : elm
36519 // resizingElement: el,
36521 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36522 // placement : Roo.bootstrap.SplitBar.LEFT ,
36523 // existingProxy ???
36526 this.el = Roo.get(cfg.dragElement, true);
36527 this.el.dom.unselectable = "on";
36529 this.resizingEl = Roo.get(cfg.resizingElement, true);
36533 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36534 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36537 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36540 * The minimum size of the resizing element. (Defaults to 0)
36546 * The maximum size of the resizing element. (Defaults to 2000)
36549 this.maxSize = 2000;
36552 * Whether to animate the transition to the new size
36555 this.animate = false;
36558 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36561 this.useShim = false;
36566 if(!cfg.existingProxy){
36568 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36570 this.proxy = Roo.get(cfg.existingProxy).dom;
36573 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36576 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36579 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36582 this.dragSpecs = {};
36585 * @private The adapter to use to positon and resize elements
36587 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36588 this.adapter.init(this);
36590 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36592 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36593 this.el.addClass("roo-splitbar-h");
36596 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36597 this.el.addClass("roo-splitbar-v");
36603 * Fires when the splitter is moved (alias for {@link #event-moved})
36604 * @param {Roo.bootstrap.SplitBar} this
36605 * @param {Number} newSize the new width or height
36610 * Fires when the splitter is moved
36611 * @param {Roo.bootstrap.SplitBar} this
36612 * @param {Number} newSize the new width or height
36616 * @event beforeresize
36617 * Fires before the splitter is dragged
36618 * @param {Roo.bootstrap.SplitBar} this
36620 "beforeresize" : true,
36622 "beforeapply" : true
36625 Roo.util.Observable.call(this);
36628 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36629 onStartProxyDrag : function(x, y){
36630 this.fireEvent("beforeresize", this);
36632 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36634 o.enableDisplayMode("block");
36635 // all splitbars share the same overlay
36636 Roo.bootstrap.SplitBar.prototype.overlay = o;
36638 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36639 this.overlay.show();
36640 Roo.get(this.proxy).setDisplayed("block");
36641 var size = this.adapter.getElementSize(this);
36642 this.activeMinSize = this.getMinimumSize();;
36643 this.activeMaxSize = this.getMaximumSize();;
36644 var c1 = size - this.activeMinSize;
36645 var c2 = Math.max(this.activeMaxSize - size, 0);
36646 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36647 this.dd.resetConstraints();
36648 this.dd.setXConstraint(
36649 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36650 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36652 this.dd.setYConstraint(0, 0);
36654 this.dd.resetConstraints();
36655 this.dd.setXConstraint(0, 0);
36656 this.dd.setYConstraint(
36657 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36658 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36661 this.dragSpecs.startSize = size;
36662 this.dragSpecs.startPoint = [x, y];
36663 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36667 * @private Called after the drag operation by the DDProxy
36669 onEndProxyDrag : function(e){
36670 Roo.get(this.proxy).setDisplayed(false);
36671 var endPoint = Roo.lib.Event.getXY(e);
36673 this.overlay.hide();
36676 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36677 newSize = this.dragSpecs.startSize +
36678 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36679 endPoint[0] - this.dragSpecs.startPoint[0] :
36680 this.dragSpecs.startPoint[0] - endPoint[0]
36683 newSize = this.dragSpecs.startSize +
36684 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36685 endPoint[1] - this.dragSpecs.startPoint[1] :
36686 this.dragSpecs.startPoint[1] - endPoint[1]
36689 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36690 if(newSize != this.dragSpecs.startSize){
36691 if(this.fireEvent('beforeapply', this, newSize) !== false){
36692 this.adapter.setElementSize(this, newSize);
36693 this.fireEvent("moved", this, newSize);
36694 this.fireEvent("resize", this, newSize);
36700 * Get the adapter this SplitBar uses
36701 * @return The adapter object
36703 getAdapter : function(){
36704 return this.adapter;
36708 * Set the adapter this SplitBar uses
36709 * @param {Object} adapter A SplitBar adapter object
36711 setAdapter : function(adapter){
36712 this.adapter = adapter;
36713 this.adapter.init(this);
36717 * Gets the minimum size for the resizing element
36718 * @return {Number} The minimum size
36720 getMinimumSize : function(){
36721 return this.minSize;
36725 * Sets the minimum size for the resizing element
36726 * @param {Number} minSize The minimum size
36728 setMinimumSize : function(minSize){
36729 this.minSize = minSize;
36733 * Gets the maximum size for the resizing element
36734 * @return {Number} The maximum size
36736 getMaximumSize : function(){
36737 return this.maxSize;
36741 * Sets the maximum size for the resizing element
36742 * @param {Number} maxSize The maximum size
36744 setMaximumSize : function(maxSize){
36745 this.maxSize = maxSize;
36749 * Sets the initialize size for the resizing element
36750 * @param {Number} size The initial size
36752 setCurrentSize : function(size){
36753 var oldAnimate = this.animate;
36754 this.animate = false;
36755 this.adapter.setElementSize(this, size);
36756 this.animate = oldAnimate;
36760 * Destroy this splitbar.
36761 * @param {Boolean} removeEl True to remove the element
36763 destroy : function(removeEl){
36765 this.shim.remove();
36768 this.proxy.parentNode.removeChild(this.proxy);
36776 * @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.
36778 Roo.bootstrap.SplitBar.createProxy = function(dir){
36779 var proxy = new Roo.Element(document.createElement("div"));
36780 proxy.unselectable();
36781 var cls = 'roo-splitbar-proxy';
36782 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36783 document.body.appendChild(proxy.dom);
36788 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36789 * Default Adapter. It assumes the splitter and resizing element are not positioned
36790 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36792 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36795 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36796 // do nothing for now
36797 init : function(s){
36801 * Called before drag operations to get the current size of the resizing element.
36802 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36804 getElementSize : function(s){
36805 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36806 return s.resizingEl.getWidth();
36808 return s.resizingEl.getHeight();
36813 * Called after drag operations to set the size of the resizing element.
36814 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36815 * @param {Number} newSize The new size to set
36816 * @param {Function} onComplete A function to be invoked when resizing is complete
36818 setElementSize : function(s, newSize, onComplete){
36819 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36821 s.resizingEl.setWidth(newSize);
36823 onComplete(s, newSize);
36826 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36831 s.resizingEl.setHeight(newSize);
36833 onComplete(s, newSize);
36836 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36843 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36844 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36845 * Adapter that moves the splitter element to align with the resized sizing element.
36846 * Used with an absolute positioned SplitBar.
36847 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36848 * document.body, make sure you assign an id to the body element.
36850 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36851 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36852 this.container = Roo.get(container);
36855 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36856 init : function(s){
36857 this.basic.init(s);
36860 getElementSize : function(s){
36861 return this.basic.getElementSize(s);
36864 setElementSize : function(s, newSize, onComplete){
36865 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36868 moveSplitter : function(s){
36869 var yes = Roo.bootstrap.SplitBar;
36870 switch(s.placement){
36872 s.el.setX(s.resizingEl.getRight());
36875 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36878 s.el.setY(s.resizingEl.getBottom());
36881 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36888 * Orientation constant - Create a vertical SplitBar
36892 Roo.bootstrap.SplitBar.VERTICAL = 1;
36895 * Orientation constant - Create a horizontal SplitBar
36899 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36902 * Placement constant - The resizing element is to the left of the splitter element
36906 Roo.bootstrap.SplitBar.LEFT = 1;
36909 * Placement constant - The resizing element is to the right of the splitter element
36913 Roo.bootstrap.SplitBar.RIGHT = 2;
36916 * Placement constant - The resizing element is positioned above the splitter element
36920 Roo.bootstrap.SplitBar.TOP = 3;
36923 * Placement constant - The resizing element is positioned under splitter element
36927 Roo.bootstrap.SplitBar.BOTTOM = 4;
36928 Roo.namespace("Roo.bootstrap.layout");/*
36930 * Ext JS Library 1.1.1
36931 * Copyright(c) 2006-2007, Ext JS, LLC.
36933 * Originally Released Under LGPL - original licence link has changed is not relivant.
36936 * <script type="text/javascript">
36940 * @class Roo.bootstrap.layout.Manager
36941 * @extends Roo.bootstrap.Component
36942 * Base class for layout managers.
36944 Roo.bootstrap.layout.Manager = function(config)
36946 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36952 /** false to disable window resize monitoring @type Boolean */
36953 this.monitorWindowResize = true;
36958 * Fires when a layout is performed.
36959 * @param {Roo.LayoutManager} this
36963 * @event regionresized
36964 * Fires when the user resizes a region.
36965 * @param {Roo.LayoutRegion} region The resized region
36966 * @param {Number} newSize The new size (width for east/west, height for north/south)
36968 "regionresized" : true,
36970 * @event regioncollapsed
36971 * Fires when a region is collapsed.
36972 * @param {Roo.LayoutRegion} region The collapsed region
36974 "regioncollapsed" : true,
36976 * @event regionexpanded
36977 * Fires when a region is expanded.
36978 * @param {Roo.LayoutRegion} region The expanded region
36980 "regionexpanded" : true
36982 this.updating = false;
36985 this.el = Roo.get(config.el);
36991 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36996 monitorWindowResize : true,
37002 onRender : function(ct, position)
37005 this.el = Roo.get(ct);
37008 //this.fireEvent('render',this);
37012 initEvents: function()
37016 // ie scrollbar fix
37017 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37018 document.body.scroll = "no";
37019 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37020 this.el.position('relative');
37022 this.id = this.el.id;
37023 this.el.addClass("roo-layout-container");
37024 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37025 if(this.el.dom != document.body ) {
37026 this.el.on('resize', this.layout,this);
37027 this.el.on('show', this.layout,this);
37033 * Returns true if this layout is currently being updated
37034 * @return {Boolean}
37036 isUpdating : function(){
37037 return this.updating;
37041 * Suspend the LayoutManager from doing auto-layouts while
37042 * making multiple add or remove calls
37044 beginUpdate : function(){
37045 this.updating = true;
37049 * Restore auto-layouts and optionally disable the manager from performing a layout
37050 * @param {Boolean} noLayout true to disable a layout update
37052 endUpdate : function(noLayout){
37053 this.updating = false;
37059 layout: function(){
37063 onRegionResized : function(region, newSize){
37064 this.fireEvent("regionresized", region, newSize);
37068 onRegionCollapsed : function(region){
37069 this.fireEvent("regioncollapsed", region);
37072 onRegionExpanded : function(region){
37073 this.fireEvent("regionexpanded", region);
37077 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37078 * performs box-model adjustments.
37079 * @return {Object} The size as an object {width: (the width), height: (the height)}
37081 getViewSize : function()
37084 if(this.el.dom != document.body){
37085 size = this.el.getSize();
37087 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37089 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37090 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37095 * Returns the Element this layout is bound to.
37096 * @return {Roo.Element}
37098 getEl : function(){
37103 * Returns the specified region.
37104 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37105 * @return {Roo.LayoutRegion}
37107 getRegion : function(target){
37108 return this.regions[target.toLowerCase()];
37111 onWindowResize : function(){
37112 if(this.monitorWindowResize){
37119 * Ext JS Library 1.1.1
37120 * Copyright(c) 2006-2007, Ext JS, LLC.
37122 * Originally Released Under LGPL - original licence link has changed is not relivant.
37125 * <script type="text/javascript">
37128 * @class Roo.bootstrap.layout.Border
37129 * @extends Roo.bootstrap.layout.Manager
37130 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37131 * please see: examples/bootstrap/nested.html<br><br>
37133 <b>The container the layout is rendered into can be either the body element or any other element.
37134 If it is not the body element, the container needs to either be an absolute positioned element,
37135 or you will need to add "position:relative" to the css of the container. You will also need to specify
37136 the container size if it is not the body element.</b>
37139 * Create a new Border
37140 * @param {Object} config Configuration options
37142 Roo.bootstrap.layout.Border = function(config){
37143 config = config || {};
37144 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37148 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37149 if(config[region]){
37150 config[region].region = region;
37151 this.addRegion(config[region]);
37157 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37159 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37161 parent : false, // this might point to a 'nest' or a ???
37164 * Creates and adds a new region if it doesn't already exist.
37165 * @param {String} target The target region key (north, south, east, west or center).
37166 * @param {Object} config The regions config object
37167 * @return {BorderLayoutRegion} The new region
37169 addRegion : function(config)
37171 if(!this.regions[config.region]){
37172 var r = this.factory(config);
37173 this.bindRegion(r);
37175 return this.regions[config.region];
37179 bindRegion : function(r){
37180 this.regions[r.config.region] = r;
37182 r.on("visibilitychange", this.layout, this);
37183 r.on("paneladded", this.layout, this);
37184 r.on("panelremoved", this.layout, this);
37185 r.on("invalidated", this.layout, this);
37186 r.on("resized", this.onRegionResized, this);
37187 r.on("collapsed", this.onRegionCollapsed, this);
37188 r.on("expanded", this.onRegionExpanded, this);
37192 * Performs a layout update.
37194 layout : function()
37196 if(this.updating) {
37200 // render all the rebions if they have not been done alreayd?
37201 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37202 if(this.regions[region] && !this.regions[region].bodyEl){
37203 this.regions[region].onRender(this.el)
37207 var size = this.getViewSize();
37208 var w = size.width;
37209 var h = size.height;
37214 //var x = 0, y = 0;
37216 var rs = this.regions;
37217 var north = rs["north"];
37218 var south = rs["south"];
37219 var west = rs["west"];
37220 var east = rs["east"];
37221 var center = rs["center"];
37222 //if(this.hideOnLayout){ // not supported anymore
37223 //c.el.setStyle("display", "none");
37225 if(north && north.isVisible()){
37226 var b = north.getBox();
37227 var m = north.getMargins();
37228 b.width = w - (m.left+m.right);
37231 centerY = b.height + b.y + m.bottom;
37232 centerH -= centerY;
37233 north.updateBox(this.safeBox(b));
37235 if(south && south.isVisible()){
37236 var b = south.getBox();
37237 var m = south.getMargins();
37238 b.width = w - (m.left+m.right);
37240 var totalHeight = (b.height + m.top + m.bottom);
37241 b.y = h - totalHeight + m.top;
37242 centerH -= totalHeight;
37243 south.updateBox(this.safeBox(b));
37245 if(west && west.isVisible()){
37246 var b = west.getBox();
37247 var m = west.getMargins();
37248 b.height = centerH - (m.top+m.bottom);
37250 b.y = centerY + m.top;
37251 var totalWidth = (b.width + m.left + m.right);
37252 centerX += totalWidth;
37253 centerW -= totalWidth;
37254 west.updateBox(this.safeBox(b));
37256 if(east && east.isVisible()){
37257 var b = east.getBox();
37258 var m = east.getMargins();
37259 b.height = centerH - (m.top+m.bottom);
37260 var totalWidth = (b.width + m.left + m.right);
37261 b.x = w - totalWidth + m.left;
37262 b.y = centerY + m.top;
37263 centerW -= totalWidth;
37264 east.updateBox(this.safeBox(b));
37267 var m = center.getMargins();
37269 x: centerX + m.left,
37270 y: centerY + m.top,
37271 width: centerW - (m.left+m.right),
37272 height: centerH - (m.top+m.bottom)
37274 //if(this.hideOnLayout){
37275 //center.el.setStyle("display", "block");
37277 center.updateBox(this.safeBox(centerBox));
37280 this.fireEvent("layout", this);
37284 safeBox : function(box){
37285 box.width = Math.max(0, box.width);
37286 box.height = Math.max(0, box.height);
37291 * Adds a ContentPanel (or subclass) to this layout.
37292 * @param {String} target The target region key (north, south, east, west or center).
37293 * @param {Roo.ContentPanel} panel The panel to add
37294 * @return {Roo.ContentPanel} The added panel
37296 add : function(target, panel){
37298 target = target.toLowerCase();
37299 return this.regions[target].add(panel);
37303 * Remove a ContentPanel (or subclass) to this layout.
37304 * @param {String} target The target region key (north, south, east, west or center).
37305 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37306 * @return {Roo.ContentPanel} The removed panel
37308 remove : function(target, panel){
37309 target = target.toLowerCase();
37310 return this.regions[target].remove(panel);
37314 * Searches all regions for a panel with the specified id
37315 * @param {String} panelId
37316 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37318 findPanel : function(panelId){
37319 var rs = this.regions;
37320 for(var target in rs){
37321 if(typeof rs[target] != "function"){
37322 var p = rs[target].getPanel(panelId);
37332 * Searches all regions for a panel with the specified id and activates (shows) it.
37333 * @param {String/ContentPanel} panelId The panels id or the panel itself
37334 * @return {Roo.ContentPanel} The shown panel or null
37336 showPanel : function(panelId) {
37337 var rs = this.regions;
37338 for(var target in rs){
37339 var r = rs[target];
37340 if(typeof r != "function"){
37341 if(r.hasPanel(panelId)){
37342 return r.showPanel(panelId);
37350 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37351 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37354 restoreState : function(provider){
37356 provider = Roo.state.Manager;
37358 var sm = new Roo.LayoutStateManager();
37359 sm.init(this, provider);
37365 * Adds a xtype elements to the layout.
37369 xtype : 'ContentPanel',
37376 xtype : 'NestedLayoutPanel',
37382 items : [ ... list of content panels or nested layout panels.. ]
37386 * @param {Object} cfg Xtype definition of item to add.
37388 addxtype : function(cfg)
37390 // basically accepts a pannel...
37391 // can accept a layout region..!?!?
37392 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37395 // theory? children can only be panels??
37397 //if (!cfg.xtype.match(/Panel$/)) {
37402 if (typeof(cfg.region) == 'undefined') {
37403 Roo.log("Failed to add Panel, region was not set");
37407 var region = cfg.region;
37413 xitems = cfg.items;
37418 if ( region == 'center') {
37419 Roo.log("Center: " + cfg.title);
37425 case 'Content': // ContentPanel (el, cfg)
37426 case 'Scroll': // ContentPanel (el, cfg)
37428 cfg.autoCreate = cfg.autoCreate || true;
37429 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37431 // var el = this.el.createChild();
37432 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37435 this.add(region, ret);
37439 case 'TreePanel': // our new panel!
37440 cfg.el = this.el.createChild();
37441 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37442 this.add(region, ret);
37447 // create a new Layout (which is a Border Layout...
37449 var clayout = cfg.layout;
37450 clayout.el = this.el.createChild();
37451 clayout.items = clayout.items || [];
37455 // replace this exitems with the clayout ones..
37456 xitems = clayout.items;
37458 // force background off if it's in center...
37459 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37460 cfg.background = false;
37462 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37465 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37466 //console.log('adding nested layout panel ' + cfg.toSource());
37467 this.add(region, ret);
37468 nb = {}; /// find first...
37473 // needs grid and region
37475 //var el = this.getRegion(region).el.createChild();
37477 *var el = this.el.createChild();
37478 // create the grid first...
37479 cfg.grid.container = el;
37480 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37483 if (region == 'center' && this.active ) {
37484 cfg.background = false;
37487 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37489 this.add(region, ret);
37491 if (cfg.background) {
37492 // render grid on panel activation (if panel background)
37493 ret.on('activate', function(gp) {
37494 if (!gp.grid.rendered) {
37495 // gp.grid.render(el);
37499 // cfg.grid.render(el);
37505 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37506 // it was the old xcomponent building that caused this before.
37507 // espeically if border is the top element in the tree.
37517 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37519 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37520 this.add(region, ret);
37524 throw "Can not add '" + cfg.xtype + "' to Border";
37530 this.beginUpdate();
37534 Roo.each(xitems, function(i) {
37535 region = nb && i.region ? i.region : false;
37537 var add = ret.addxtype(i);
37540 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37541 if (!i.background) {
37542 abn[region] = nb[region] ;
37549 // make the last non-background panel active..
37550 //if (nb) { Roo.log(abn); }
37553 for(var r in abn) {
37554 region = this.getRegion(r);
37556 // tried using nb[r], but it does not work..
37558 region.showPanel(abn[r]);
37569 factory : function(cfg)
37572 var validRegions = Roo.bootstrap.layout.Border.regions;
37574 var target = cfg.region;
37577 var r = Roo.bootstrap.layout;
37581 return new r.North(cfg);
37583 return new r.South(cfg);
37585 return new r.East(cfg);
37587 return new r.West(cfg);
37589 return new r.Center(cfg);
37591 throw 'Layout region "'+target+'" not supported.';
37598 * Ext JS Library 1.1.1
37599 * Copyright(c) 2006-2007, Ext JS, LLC.
37601 * Originally Released Under LGPL - original licence link has changed is not relivant.
37604 * <script type="text/javascript">
37608 * @class Roo.bootstrap.layout.Basic
37609 * @extends Roo.util.Observable
37610 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37611 * and does not have a titlebar, tabs or any other features. All it does is size and position
37612 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37613 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37614 * @cfg {string} region the region that it inhabits..
37615 * @cfg {bool} skipConfig skip config?
37619 Roo.bootstrap.layout.Basic = function(config){
37621 this.mgr = config.mgr;
37623 this.position = config.region;
37625 var skipConfig = config.skipConfig;
37629 * @scope Roo.BasicLayoutRegion
37633 * @event beforeremove
37634 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37635 * @param {Roo.LayoutRegion} this
37636 * @param {Roo.ContentPanel} panel The panel
37637 * @param {Object} e The cancel event object
37639 "beforeremove" : true,
37641 * @event invalidated
37642 * Fires when the layout for this region is changed.
37643 * @param {Roo.LayoutRegion} this
37645 "invalidated" : true,
37647 * @event visibilitychange
37648 * Fires when this region is shown or hidden
37649 * @param {Roo.LayoutRegion} this
37650 * @param {Boolean} visibility true or false
37652 "visibilitychange" : true,
37654 * @event paneladded
37655 * Fires when a panel is added.
37656 * @param {Roo.LayoutRegion} this
37657 * @param {Roo.ContentPanel} panel The panel
37659 "paneladded" : true,
37661 * @event panelremoved
37662 * Fires when a panel is removed.
37663 * @param {Roo.LayoutRegion} this
37664 * @param {Roo.ContentPanel} panel The panel
37666 "panelremoved" : true,
37668 * @event beforecollapse
37669 * Fires when this region before collapse.
37670 * @param {Roo.LayoutRegion} this
37672 "beforecollapse" : true,
37675 * Fires when this region is collapsed.
37676 * @param {Roo.LayoutRegion} this
37678 "collapsed" : true,
37681 * Fires when this region is expanded.
37682 * @param {Roo.LayoutRegion} this
37687 * Fires when this region is slid into view.
37688 * @param {Roo.LayoutRegion} this
37690 "slideshow" : true,
37693 * Fires when this region slides out of view.
37694 * @param {Roo.LayoutRegion} this
37696 "slidehide" : true,
37698 * @event panelactivated
37699 * Fires when a panel is activated.
37700 * @param {Roo.LayoutRegion} this
37701 * @param {Roo.ContentPanel} panel The activated panel
37703 "panelactivated" : true,
37706 * Fires when the user resizes this region.
37707 * @param {Roo.LayoutRegion} this
37708 * @param {Number} newSize The new size (width for east/west, height for north/south)
37712 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37713 this.panels = new Roo.util.MixedCollection();
37714 this.panels.getKey = this.getPanelId.createDelegate(this);
37716 this.activePanel = null;
37717 // ensure listeners are added...
37719 if (config.listeners || config.events) {
37720 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37721 listeners : config.listeners || {},
37722 events : config.events || {}
37726 if(skipConfig !== true){
37727 this.applyConfig(config);
37731 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37733 getPanelId : function(p){
37737 applyConfig : function(config){
37738 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37739 this.config = config;
37744 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37745 * the width, for horizontal (north, south) the height.
37746 * @param {Number} newSize The new width or height
37748 resizeTo : function(newSize){
37749 var el = this.el ? this.el :
37750 (this.activePanel ? this.activePanel.getEl() : null);
37752 switch(this.position){
37755 el.setWidth(newSize);
37756 this.fireEvent("resized", this, newSize);
37760 el.setHeight(newSize);
37761 this.fireEvent("resized", this, newSize);
37767 getBox : function(){
37768 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37771 getMargins : function(){
37772 return this.margins;
37775 updateBox : function(box){
37777 var el = this.activePanel.getEl();
37778 el.dom.style.left = box.x + "px";
37779 el.dom.style.top = box.y + "px";
37780 this.activePanel.setSize(box.width, box.height);
37784 * Returns the container element for this region.
37785 * @return {Roo.Element}
37787 getEl : function(){
37788 return this.activePanel;
37792 * Returns true if this region is currently visible.
37793 * @return {Boolean}
37795 isVisible : function(){
37796 return this.activePanel ? true : false;
37799 setActivePanel : function(panel){
37800 panel = this.getPanel(panel);
37801 if(this.activePanel && this.activePanel != panel){
37802 this.activePanel.setActiveState(false);
37803 this.activePanel.getEl().setLeftTop(-10000,-10000);
37805 this.activePanel = panel;
37806 panel.setActiveState(true);
37808 panel.setSize(this.box.width, this.box.height);
37810 this.fireEvent("panelactivated", this, panel);
37811 this.fireEvent("invalidated");
37815 * Show the specified panel.
37816 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37817 * @return {Roo.ContentPanel} The shown panel or null
37819 showPanel : function(panel){
37820 panel = this.getPanel(panel);
37822 this.setActivePanel(panel);
37828 * Get the active panel for this region.
37829 * @return {Roo.ContentPanel} The active panel or null
37831 getActivePanel : function(){
37832 return this.activePanel;
37836 * Add the passed ContentPanel(s)
37837 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37838 * @return {Roo.ContentPanel} The panel added (if only one was added)
37840 add : function(panel){
37841 if(arguments.length > 1){
37842 for(var i = 0, len = arguments.length; i < len; i++) {
37843 this.add(arguments[i]);
37847 if(this.hasPanel(panel)){
37848 this.showPanel(panel);
37851 var el = panel.getEl();
37852 if(el.dom.parentNode != this.mgr.el.dom){
37853 this.mgr.el.dom.appendChild(el.dom);
37855 if(panel.setRegion){
37856 panel.setRegion(this);
37858 this.panels.add(panel);
37859 el.setStyle("position", "absolute");
37860 if(!panel.background){
37861 this.setActivePanel(panel);
37862 if(this.config.initialSize && this.panels.getCount()==1){
37863 this.resizeTo(this.config.initialSize);
37866 this.fireEvent("paneladded", this, panel);
37871 * Returns true if the panel is in this region.
37872 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37873 * @return {Boolean}
37875 hasPanel : function(panel){
37876 if(typeof panel == "object"){ // must be panel obj
37877 panel = panel.getId();
37879 return this.getPanel(panel) ? true : false;
37883 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37884 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37885 * @param {Boolean} preservePanel Overrides the config preservePanel option
37886 * @return {Roo.ContentPanel} The panel that was removed
37888 remove : function(panel, preservePanel){
37889 panel = this.getPanel(panel);
37894 this.fireEvent("beforeremove", this, panel, e);
37895 if(e.cancel === true){
37898 var panelId = panel.getId();
37899 this.panels.removeKey(panelId);
37904 * Returns the panel specified or null if it's not in this region.
37905 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37906 * @return {Roo.ContentPanel}
37908 getPanel : function(id){
37909 if(typeof id == "object"){ // must be panel obj
37912 return this.panels.get(id);
37916 * Returns this regions position (north/south/east/west/center).
37919 getPosition: function(){
37920 return this.position;
37924 * Ext JS Library 1.1.1
37925 * Copyright(c) 2006-2007, Ext JS, LLC.
37927 * Originally Released Under LGPL - original licence link has changed is not relivant.
37930 * <script type="text/javascript">
37934 * @class Roo.bootstrap.layout.Region
37935 * @extends Roo.bootstrap.layout.Basic
37936 * This class represents a region in a layout manager.
37938 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37939 * @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})
37940 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37941 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37942 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37943 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37944 * @cfg {String} title The title for the region (overrides panel titles)
37945 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37946 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37947 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37948 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37949 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37950 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37951 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37952 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37953 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37954 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37956 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37957 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37958 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37959 * @cfg {Number} width For East/West panels
37960 * @cfg {Number} height For North/South panels
37961 * @cfg {Boolean} split To show the splitter
37962 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37964 * @cfg {string} cls Extra CSS classes to add to region
37966 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37967 * @cfg {string} region the region that it inhabits..
37970 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37971 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37973 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37974 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37975 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37977 Roo.bootstrap.layout.Region = function(config)
37979 this.applyConfig(config);
37981 var mgr = config.mgr;
37982 var pos = config.region;
37983 config.skipConfig = true;
37984 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37987 this.onRender(mgr.el);
37990 this.visible = true;
37991 this.collapsed = false;
37992 this.unrendered_panels = [];
37995 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37997 position: '', // set by wrapper (eg. north/south etc..)
37998 unrendered_panels : null, // unrendered panels.
38000 tabPosition : false,
38002 mgr: false, // points to 'Border'
38005 createBody : function(){
38006 /** This region's body element
38007 * @type Roo.Element */
38008 this.bodyEl = this.el.createChild({
38010 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38014 onRender: function(ctr, pos)
38016 var dh = Roo.DomHelper;
38017 /** This region's container element
38018 * @type Roo.Element */
38019 this.el = dh.append(ctr.dom, {
38021 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38023 /** This region's title element
38024 * @type Roo.Element */
38026 this.titleEl = dh.append(this.el.dom, {
38028 unselectable: "on",
38029 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38031 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38032 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38036 this.titleEl.enableDisplayMode();
38037 /** This region's title text element
38038 * @type HTMLElement */
38039 this.titleTextEl = this.titleEl.dom.firstChild;
38040 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38042 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38043 this.closeBtn.enableDisplayMode();
38044 this.closeBtn.on("click", this.closeClicked, this);
38045 this.closeBtn.hide();
38047 this.createBody(this.config);
38048 if(this.config.hideWhenEmpty){
38050 this.on("paneladded", this.validateVisibility, this);
38051 this.on("panelremoved", this.validateVisibility, this);
38053 if(this.autoScroll){
38054 this.bodyEl.setStyle("overflow", "auto");
38056 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38058 //if(c.titlebar !== false){
38059 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38060 this.titleEl.hide();
38062 this.titleEl.show();
38063 if(this.config.title){
38064 this.titleTextEl.innerHTML = this.config.title;
38068 if(this.config.collapsed){
38069 this.collapse(true);
38071 if(this.config.hidden){
38075 if (this.unrendered_panels && this.unrendered_panels.length) {
38076 for (var i =0;i< this.unrendered_panels.length; i++) {
38077 this.add(this.unrendered_panels[i]);
38079 this.unrendered_panels = null;
38085 applyConfig : function(c)
38088 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38089 var dh = Roo.DomHelper;
38090 if(c.titlebar !== false){
38091 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38092 this.collapseBtn.on("click", this.collapse, this);
38093 this.collapseBtn.enableDisplayMode();
38095 if(c.showPin === true || this.showPin){
38096 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38097 this.stickBtn.enableDisplayMode();
38098 this.stickBtn.on("click", this.expand, this);
38099 this.stickBtn.hide();
38104 /** This region's collapsed element
38105 * @type Roo.Element */
38108 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38109 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38112 if(c.floatable !== false){
38113 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38114 this.collapsedEl.on("click", this.collapseClick, this);
38117 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38118 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38119 id: "message", unselectable: "on", style:{"float":"left"}});
38120 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38122 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38123 this.expandBtn.on("click", this.expand, this);
38127 if(this.collapseBtn){
38128 this.collapseBtn.setVisible(c.collapsible == true);
38131 this.cmargins = c.cmargins || this.cmargins ||
38132 (this.position == "west" || this.position == "east" ?
38133 {top: 0, left: 2, right:2, bottom: 0} :
38134 {top: 2, left: 0, right:0, bottom: 2});
38136 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38139 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38141 this.autoScroll = c.autoScroll || false;
38146 this.duration = c.duration || .30;
38147 this.slideDuration = c.slideDuration || .45;
38152 * Returns true if this region is currently visible.
38153 * @return {Boolean}
38155 isVisible : function(){
38156 return this.visible;
38160 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38161 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38163 //setCollapsedTitle : function(title){
38164 // title = title || " ";
38165 // if(this.collapsedTitleTextEl){
38166 // this.collapsedTitleTextEl.innerHTML = title;
38170 getBox : function(){
38172 // if(!this.collapsed){
38173 b = this.el.getBox(false, true);
38175 // b = this.collapsedEl.getBox(false, true);
38180 getMargins : function(){
38181 return this.margins;
38182 //return this.collapsed ? this.cmargins : this.margins;
38185 highlight : function(){
38186 this.el.addClass("x-layout-panel-dragover");
38189 unhighlight : function(){
38190 this.el.removeClass("x-layout-panel-dragover");
38193 updateBox : function(box)
38195 if (!this.bodyEl) {
38196 return; // not rendered yet..
38200 if(!this.collapsed){
38201 this.el.dom.style.left = box.x + "px";
38202 this.el.dom.style.top = box.y + "px";
38203 this.updateBody(box.width, box.height);
38205 this.collapsedEl.dom.style.left = box.x + "px";
38206 this.collapsedEl.dom.style.top = box.y + "px";
38207 this.collapsedEl.setSize(box.width, box.height);
38210 this.tabs.autoSizeTabs();
38214 updateBody : function(w, h)
38217 this.el.setWidth(w);
38218 w -= this.el.getBorderWidth("rl");
38219 if(this.config.adjustments){
38220 w += this.config.adjustments[0];
38223 if(h !== null && h > 0){
38224 this.el.setHeight(h);
38225 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38226 h -= this.el.getBorderWidth("tb");
38227 if(this.config.adjustments){
38228 h += this.config.adjustments[1];
38230 this.bodyEl.setHeight(h);
38232 h = this.tabs.syncHeight(h);
38235 if(this.panelSize){
38236 w = w !== null ? w : this.panelSize.width;
38237 h = h !== null ? h : this.panelSize.height;
38239 if(this.activePanel){
38240 var el = this.activePanel.getEl();
38241 w = w !== null ? w : el.getWidth();
38242 h = h !== null ? h : el.getHeight();
38243 this.panelSize = {width: w, height: h};
38244 this.activePanel.setSize(w, h);
38246 if(Roo.isIE && this.tabs){
38247 this.tabs.el.repaint();
38252 * Returns the container element for this region.
38253 * @return {Roo.Element}
38255 getEl : function(){
38260 * Hides this region.
38263 //if(!this.collapsed){
38264 this.el.dom.style.left = "-2000px";
38267 // this.collapsedEl.dom.style.left = "-2000px";
38268 // this.collapsedEl.hide();
38270 this.visible = false;
38271 this.fireEvent("visibilitychange", this, false);
38275 * Shows this region if it was previously hidden.
38278 //if(!this.collapsed){
38281 // this.collapsedEl.show();
38283 this.visible = true;
38284 this.fireEvent("visibilitychange", this, true);
38287 closeClicked : function(){
38288 if(this.activePanel){
38289 this.remove(this.activePanel);
38293 collapseClick : function(e){
38295 e.stopPropagation();
38298 e.stopPropagation();
38304 * Collapses this region.
38305 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38308 collapse : function(skipAnim, skipCheck = false){
38309 if(this.collapsed) {
38313 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38315 this.collapsed = true;
38317 this.split.el.hide();
38319 if(this.config.animate && skipAnim !== true){
38320 this.fireEvent("invalidated", this);
38321 this.animateCollapse();
38323 this.el.setLocation(-20000,-20000);
38325 this.collapsedEl.show();
38326 this.fireEvent("collapsed", this);
38327 this.fireEvent("invalidated", this);
38333 animateCollapse : function(){
38338 * Expands this region if it was previously collapsed.
38339 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38340 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38343 expand : function(e, skipAnim){
38345 e.stopPropagation();
38347 if(!this.collapsed || this.el.hasActiveFx()) {
38351 this.afterSlideIn();
38354 this.collapsed = false;
38355 if(this.config.animate && skipAnim !== true){
38356 this.animateExpand();
38360 this.split.el.show();
38362 this.collapsedEl.setLocation(-2000,-2000);
38363 this.collapsedEl.hide();
38364 this.fireEvent("invalidated", this);
38365 this.fireEvent("expanded", this);
38369 animateExpand : function(){
38373 initTabs : function()
38375 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38377 var ts = new Roo.bootstrap.panel.Tabs({
38378 el: this.bodyEl.dom,
38380 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38381 disableTooltips: this.config.disableTabTips,
38382 toolbar : this.config.toolbar
38385 if(this.config.hideTabs){
38386 ts.stripWrap.setDisplayed(false);
38389 ts.resizeTabs = this.config.resizeTabs === true;
38390 ts.minTabWidth = this.config.minTabWidth || 40;
38391 ts.maxTabWidth = this.config.maxTabWidth || 250;
38392 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38393 ts.monitorResize = false;
38394 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38395 ts.bodyEl.addClass('roo-layout-tabs-body');
38396 this.panels.each(this.initPanelAsTab, this);
38399 initPanelAsTab : function(panel){
38400 var ti = this.tabs.addTab(
38404 this.config.closeOnTab && panel.isClosable(),
38407 if(panel.tabTip !== undefined){
38408 ti.setTooltip(panel.tabTip);
38410 ti.on("activate", function(){
38411 this.setActivePanel(panel);
38414 if(this.config.closeOnTab){
38415 ti.on("beforeclose", function(t, e){
38417 this.remove(panel);
38421 panel.tabItem = ti;
38426 updatePanelTitle : function(panel, title)
38428 if(this.activePanel == panel){
38429 this.updateTitle(title);
38432 var ti = this.tabs.getTab(panel.getEl().id);
38434 if(panel.tabTip !== undefined){
38435 ti.setTooltip(panel.tabTip);
38440 updateTitle : function(title){
38441 if(this.titleTextEl && !this.config.title){
38442 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38446 setActivePanel : function(panel)
38448 panel = this.getPanel(panel);
38449 if(this.activePanel && this.activePanel != panel){
38450 if(this.activePanel.setActiveState(false) === false){
38454 this.activePanel = panel;
38455 panel.setActiveState(true);
38456 if(this.panelSize){
38457 panel.setSize(this.panelSize.width, this.panelSize.height);
38460 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38462 this.updateTitle(panel.getTitle());
38464 this.fireEvent("invalidated", this);
38466 this.fireEvent("panelactivated", this, panel);
38470 * Shows the specified panel.
38471 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38472 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38474 showPanel : function(panel)
38476 panel = this.getPanel(panel);
38479 var tab = this.tabs.getTab(panel.getEl().id);
38480 if(tab.isHidden()){
38481 this.tabs.unhideTab(tab.id);
38485 this.setActivePanel(panel);
38492 * Get the active panel for this region.
38493 * @return {Roo.ContentPanel} The active panel or null
38495 getActivePanel : function(){
38496 return this.activePanel;
38499 validateVisibility : function(){
38500 if(this.panels.getCount() < 1){
38501 this.updateTitle(" ");
38502 this.closeBtn.hide();
38505 if(!this.isVisible()){
38512 * Adds the passed ContentPanel(s) to this region.
38513 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38514 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38516 add : function(panel)
38518 if(arguments.length > 1){
38519 for(var i = 0, len = arguments.length; i < len; i++) {
38520 this.add(arguments[i]);
38525 // if we have not been rendered yet, then we can not really do much of this..
38526 if (!this.bodyEl) {
38527 this.unrendered_panels.push(panel);
38534 if(this.hasPanel(panel)){
38535 this.showPanel(panel);
38538 panel.setRegion(this);
38539 this.panels.add(panel);
38540 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38541 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38542 // and hide them... ???
38543 this.bodyEl.dom.appendChild(panel.getEl().dom);
38544 if(panel.background !== true){
38545 this.setActivePanel(panel);
38547 this.fireEvent("paneladded", this, panel);
38554 this.initPanelAsTab(panel);
38558 if(panel.background !== true){
38559 this.tabs.activate(panel.getEl().id);
38561 this.fireEvent("paneladded", this, panel);
38566 * Hides the tab for the specified panel.
38567 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38569 hidePanel : function(panel){
38570 if(this.tabs && (panel = this.getPanel(panel))){
38571 this.tabs.hideTab(panel.getEl().id);
38576 * Unhides the tab for a previously hidden panel.
38577 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38579 unhidePanel : function(panel){
38580 if(this.tabs && (panel = this.getPanel(panel))){
38581 this.tabs.unhideTab(panel.getEl().id);
38585 clearPanels : function(){
38586 while(this.panels.getCount() > 0){
38587 this.remove(this.panels.first());
38592 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38593 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38594 * @param {Boolean} preservePanel Overrides the config preservePanel option
38595 * @return {Roo.ContentPanel} The panel that was removed
38597 remove : function(panel, preservePanel)
38599 panel = this.getPanel(panel);
38604 this.fireEvent("beforeremove", this, panel, e);
38605 if(e.cancel === true){
38608 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38609 var panelId = panel.getId();
38610 this.panels.removeKey(panelId);
38612 document.body.appendChild(panel.getEl().dom);
38615 this.tabs.removeTab(panel.getEl().id);
38616 }else if (!preservePanel){
38617 this.bodyEl.dom.removeChild(panel.getEl().dom);
38619 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38620 var p = this.panels.first();
38621 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38622 tempEl.appendChild(p.getEl().dom);
38623 this.bodyEl.update("");
38624 this.bodyEl.dom.appendChild(p.getEl().dom);
38626 this.updateTitle(p.getTitle());
38628 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38629 this.setActivePanel(p);
38631 panel.setRegion(null);
38632 if(this.activePanel == panel){
38633 this.activePanel = null;
38635 if(this.config.autoDestroy !== false && preservePanel !== true){
38636 try{panel.destroy();}catch(e){}
38638 this.fireEvent("panelremoved", this, panel);
38643 * Returns the TabPanel component used by this region
38644 * @return {Roo.TabPanel}
38646 getTabs : function(){
38650 createTool : function(parentEl, className){
38651 var btn = Roo.DomHelper.append(parentEl, {
38653 cls: "x-layout-tools-button",
38656 cls: "roo-layout-tools-button-inner " + className,
38660 btn.addClassOnOver("roo-layout-tools-button-over");
38665 * Ext JS Library 1.1.1
38666 * Copyright(c) 2006-2007, Ext JS, LLC.
38668 * Originally Released Under LGPL - original licence link has changed is not relivant.
38671 * <script type="text/javascript">
38677 * @class Roo.SplitLayoutRegion
38678 * @extends Roo.LayoutRegion
38679 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38681 Roo.bootstrap.layout.Split = function(config){
38682 this.cursor = config.cursor;
38683 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38686 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38688 splitTip : "Drag to resize.",
38689 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38690 useSplitTips : false,
38692 applyConfig : function(config){
38693 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38696 onRender : function(ctr,pos) {
38698 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38699 if(!this.config.split){
38704 var splitEl = Roo.DomHelper.append(ctr.dom, {
38706 id: this.el.id + "-split",
38707 cls: "roo-layout-split roo-layout-split-"+this.position,
38710 /** The SplitBar for this region
38711 * @type Roo.SplitBar */
38712 // does not exist yet...
38713 Roo.log([this.position, this.orientation]);
38715 this.split = new Roo.bootstrap.SplitBar({
38716 dragElement : splitEl,
38717 resizingElement: this.el,
38718 orientation : this.orientation
38721 this.split.on("moved", this.onSplitMove, this);
38722 this.split.useShim = this.config.useShim === true;
38723 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38724 if(this.useSplitTips){
38725 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38727 //if(config.collapsible){
38728 // this.split.el.on("dblclick", this.collapse, this);
38731 if(typeof this.config.minSize != "undefined"){
38732 this.split.minSize = this.config.minSize;
38734 if(typeof this.config.maxSize != "undefined"){
38735 this.split.maxSize = this.config.maxSize;
38737 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38738 this.hideSplitter();
38743 getHMaxSize : function(){
38744 var cmax = this.config.maxSize || 10000;
38745 var center = this.mgr.getRegion("center");
38746 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38749 getVMaxSize : function(){
38750 var cmax = this.config.maxSize || 10000;
38751 var center = this.mgr.getRegion("center");
38752 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38755 onSplitMove : function(split, newSize){
38756 this.fireEvent("resized", this, newSize);
38760 * Returns the {@link Roo.SplitBar} for this region.
38761 * @return {Roo.SplitBar}
38763 getSplitBar : function(){
38768 this.hideSplitter();
38769 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38772 hideSplitter : function(){
38774 this.split.el.setLocation(-2000,-2000);
38775 this.split.el.hide();
38781 this.split.el.show();
38783 Roo.bootstrap.layout.Split.superclass.show.call(this);
38786 beforeSlide: function(){
38787 if(Roo.isGecko){// firefox overflow auto bug workaround
38788 this.bodyEl.clip();
38790 this.tabs.bodyEl.clip();
38792 if(this.activePanel){
38793 this.activePanel.getEl().clip();
38795 if(this.activePanel.beforeSlide){
38796 this.activePanel.beforeSlide();
38802 afterSlide : function(){
38803 if(Roo.isGecko){// firefox overflow auto bug workaround
38804 this.bodyEl.unclip();
38806 this.tabs.bodyEl.unclip();
38808 if(this.activePanel){
38809 this.activePanel.getEl().unclip();
38810 if(this.activePanel.afterSlide){
38811 this.activePanel.afterSlide();
38817 initAutoHide : function(){
38818 if(this.autoHide !== false){
38819 if(!this.autoHideHd){
38820 var st = new Roo.util.DelayedTask(this.slideIn, this);
38821 this.autoHideHd = {
38822 "mouseout": function(e){
38823 if(!e.within(this.el, true)){
38827 "mouseover" : function(e){
38833 this.el.on(this.autoHideHd);
38837 clearAutoHide : function(){
38838 if(this.autoHide !== false){
38839 this.el.un("mouseout", this.autoHideHd.mouseout);
38840 this.el.un("mouseover", this.autoHideHd.mouseover);
38844 clearMonitor : function(){
38845 Roo.get(document).un("click", this.slideInIf, this);
38848 // these names are backwards but not changed for compat
38849 slideOut : function(){
38850 if(this.isSlid || this.el.hasActiveFx()){
38853 this.isSlid = true;
38854 if(this.collapseBtn){
38855 this.collapseBtn.hide();
38857 this.closeBtnState = this.closeBtn.getStyle('display');
38858 this.closeBtn.hide();
38860 this.stickBtn.show();
38863 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38864 this.beforeSlide();
38865 this.el.setStyle("z-index", 10001);
38866 this.el.slideIn(this.getSlideAnchor(), {
38867 callback: function(){
38869 this.initAutoHide();
38870 Roo.get(document).on("click", this.slideInIf, this);
38871 this.fireEvent("slideshow", this);
38878 afterSlideIn : function(){
38879 this.clearAutoHide();
38880 this.isSlid = false;
38881 this.clearMonitor();
38882 this.el.setStyle("z-index", "");
38883 if(this.collapseBtn){
38884 this.collapseBtn.show();
38886 this.closeBtn.setStyle('display', this.closeBtnState);
38888 this.stickBtn.hide();
38890 this.fireEvent("slidehide", this);
38893 slideIn : function(cb){
38894 if(!this.isSlid || this.el.hasActiveFx()){
38898 this.isSlid = false;
38899 this.beforeSlide();
38900 this.el.slideOut(this.getSlideAnchor(), {
38901 callback: function(){
38902 this.el.setLeftTop(-10000, -10000);
38904 this.afterSlideIn();
38912 slideInIf : function(e){
38913 if(!e.within(this.el)){
38918 animateCollapse : function(){
38919 this.beforeSlide();
38920 this.el.setStyle("z-index", 20000);
38921 var anchor = this.getSlideAnchor();
38922 this.el.slideOut(anchor, {
38923 callback : function(){
38924 this.el.setStyle("z-index", "");
38925 this.collapsedEl.slideIn(anchor, {duration:.3});
38927 this.el.setLocation(-10000,-10000);
38929 this.fireEvent("collapsed", this);
38936 animateExpand : function(){
38937 this.beforeSlide();
38938 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38939 this.el.setStyle("z-index", 20000);
38940 this.collapsedEl.hide({
38943 this.el.slideIn(this.getSlideAnchor(), {
38944 callback : function(){
38945 this.el.setStyle("z-index", "");
38948 this.split.el.show();
38950 this.fireEvent("invalidated", this);
38951 this.fireEvent("expanded", this);
38979 getAnchor : function(){
38980 return this.anchors[this.position];
38983 getCollapseAnchor : function(){
38984 return this.canchors[this.position];
38987 getSlideAnchor : function(){
38988 return this.sanchors[this.position];
38991 getAlignAdj : function(){
38992 var cm = this.cmargins;
38993 switch(this.position){
39009 getExpandAdj : function(){
39010 var c = this.collapsedEl, cm = this.cmargins;
39011 switch(this.position){
39013 return [-(cm.right+c.getWidth()+cm.left), 0];
39016 return [cm.right+c.getWidth()+cm.left, 0];
39019 return [0, -(cm.top+cm.bottom+c.getHeight())];
39022 return [0, cm.top+cm.bottom+c.getHeight()];
39028 * Ext JS Library 1.1.1
39029 * Copyright(c) 2006-2007, Ext JS, LLC.
39031 * Originally Released Under LGPL - original licence link has changed is not relivant.
39034 * <script type="text/javascript">
39037 * These classes are private internal classes
39039 Roo.bootstrap.layout.Center = function(config){
39040 config.region = "center";
39041 Roo.bootstrap.layout.Region.call(this, config);
39042 this.visible = true;
39043 this.minWidth = config.minWidth || 20;
39044 this.minHeight = config.minHeight || 20;
39047 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39049 // center panel can't be hidden
39053 // center panel can't be hidden
39056 getMinWidth: function(){
39057 return this.minWidth;
39060 getMinHeight: function(){
39061 return this.minHeight;
39075 Roo.bootstrap.layout.North = function(config)
39077 config.region = 'north';
39078 config.cursor = 'n-resize';
39080 Roo.bootstrap.layout.Split.call(this, config);
39084 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39085 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39086 this.split.el.addClass("roo-layout-split-v");
39088 var size = config.initialSize || config.height;
39089 if(typeof size != "undefined"){
39090 this.el.setHeight(size);
39093 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39095 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39099 getBox : function(){
39100 if(this.collapsed){
39101 return this.collapsedEl.getBox();
39103 var box = this.el.getBox();
39105 box.height += this.split.el.getHeight();
39110 updateBox : function(box){
39111 if(this.split && !this.collapsed){
39112 box.height -= this.split.el.getHeight();
39113 this.split.el.setLeft(box.x);
39114 this.split.el.setTop(box.y+box.height);
39115 this.split.el.setWidth(box.width);
39117 if(this.collapsed){
39118 this.updateBody(box.width, null);
39120 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39128 Roo.bootstrap.layout.South = function(config){
39129 config.region = 'south';
39130 config.cursor = 's-resize';
39131 Roo.bootstrap.layout.Split.call(this, config);
39133 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39134 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39135 this.split.el.addClass("roo-layout-split-v");
39137 var size = config.initialSize || config.height;
39138 if(typeof size != "undefined"){
39139 this.el.setHeight(size);
39143 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39144 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39145 getBox : function(){
39146 if(this.collapsed){
39147 return this.collapsedEl.getBox();
39149 var box = this.el.getBox();
39151 var sh = this.split.el.getHeight();
39158 updateBox : function(box){
39159 if(this.split && !this.collapsed){
39160 var sh = this.split.el.getHeight();
39163 this.split.el.setLeft(box.x);
39164 this.split.el.setTop(box.y-sh);
39165 this.split.el.setWidth(box.width);
39167 if(this.collapsed){
39168 this.updateBody(box.width, null);
39170 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39174 Roo.bootstrap.layout.East = function(config){
39175 config.region = "east";
39176 config.cursor = "e-resize";
39177 Roo.bootstrap.layout.Split.call(this, config);
39179 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39180 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39181 this.split.el.addClass("roo-layout-split-h");
39183 var size = config.initialSize || config.width;
39184 if(typeof size != "undefined"){
39185 this.el.setWidth(size);
39188 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39189 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39190 getBox : function(){
39191 if(this.collapsed){
39192 return this.collapsedEl.getBox();
39194 var box = this.el.getBox();
39196 var sw = this.split.el.getWidth();
39203 updateBox : function(box){
39204 if(this.split && !this.collapsed){
39205 var sw = this.split.el.getWidth();
39207 this.split.el.setLeft(box.x);
39208 this.split.el.setTop(box.y);
39209 this.split.el.setHeight(box.height);
39212 if(this.collapsed){
39213 this.updateBody(null, box.height);
39215 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39219 Roo.bootstrap.layout.West = function(config){
39220 config.region = "west";
39221 config.cursor = "w-resize";
39223 Roo.bootstrap.layout.Split.call(this, config);
39225 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39226 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39227 this.split.el.addClass("roo-layout-split-h");
39231 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39232 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39234 onRender: function(ctr, pos)
39236 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39237 var size = this.config.initialSize || this.config.width;
39238 if(typeof size != "undefined"){
39239 this.el.setWidth(size);
39243 getBox : function(){
39244 if(this.collapsed){
39245 return this.collapsedEl.getBox();
39247 var box = this.el.getBox();
39249 box.width += this.split.el.getWidth();
39254 updateBox : function(box){
39255 if(this.split && !this.collapsed){
39256 var sw = this.split.el.getWidth();
39258 this.split.el.setLeft(box.x+box.width);
39259 this.split.el.setTop(box.y);
39260 this.split.el.setHeight(box.height);
39262 if(this.collapsed){
39263 this.updateBody(null, box.height);
39265 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39267 });Roo.namespace("Roo.bootstrap.panel");/*
39269 * Ext JS Library 1.1.1
39270 * Copyright(c) 2006-2007, Ext JS, LLC.
39272 * Originally Released Under LGPL - original licence link has changed is not relivant.
39275 * <script type="text/javascript">
39278 * @class Roo.ContentPanel
39279 * @extends Roo.util.Observable
39280 * A basic ContentPanel element.
39281 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39282 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39283 * @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
39284 * @cfg {Boolean} closable True if the panel can be closed/removed
39285 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39286 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39287 * @cfg {Toolbar} toolbar A toolbar for this panel
39288 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39289 * @cfg {String} title The title for this panel
39290 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39291 * @cfg {String} url Calls {@link #setUrl} with this value
39292 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39293 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39294 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39295 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39296 * @cfg {Boolean} badges render the badges
39297 * @cfg {String} cls extra classes to use
39298 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39301 * Create a new ContentPanel.
39302 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39303 * @param {String/Object} config A string to set only the title or a config object
39304 * @param {String} content (optional) Set the HTML content for this panel
39305 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39307 Roo.bootstrap.panel.Content = function( config){
39309 this.tpl = config.tpl || false;
39311 var el = config.el;
39312 var content = config.content;
39314 if(config.autoCreate){ // xtype is available if this is called from factory
39317 this.el = Roo.get(el);
39318 if(!this.el && config && config.autoCreate){
39319 if(typeof config.autoCreate == "object"){
39320 if(!config.autoCreate.id){
39321 config.autoCreate.id = config.id||el;
39323 this.el = Roo.DomHelper.append(document.body,
39324 config.autoCreate, true);
39328 cls: (config.cls || '') +
39329 (config.background ? ' bg-' + config.background : '') +
39330 " roo-layout-inactive-content",
39334 elcfg.html = config.html;
39338 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39341 this.closable = false;
39342 this.loaded = false;
39343 this.active = false;
39346 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39348 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39350 this.wrapEl = this.el; //this.el.wrap();
39352 if (config.toolbar.items) {
39353 ti = config.toolbar.items ;
39354 delete config.toolbar.items ;
39358 this.toolbar.render(this.wrapEl, 'before');
39359 for(var i =0;i < ti.length;i++) {
39360 // Roo.log(['add child', items[i]]);
39361 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39363 this.toolbar.items = nitems;
39364 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39365 delete config.toolbar;
39369 // xtype created footer. - not sure if will work as we normally have to render first..
39370 if (this.footer && !this.footer.el && this.footer.xtype) {
39371 if (!this.wrapEl) {
39372 this.wrapEl = this.el.wrap();
39375 this.footer.container = this.wrapEl.createChild();
39377 this.footer = Roo.factory(this.footer, Roo);
39382 if(typeof config == "string"){
39383 this.title = config;
39385 Roo.apply(this, config);
39389 this.resizeEl = Roo.get(this.resizeEl, true);
39391 this.resizeEl = this.el;
39393 // handle view.xtype
39401 * Fires when this panel is activated.
39402 * @param {Roo.ContentPanel} this
39406 * @event deactivate
39407 * Fires when this panel is activated.
39408 * @param {Roo.ContentPanel} this
39410 "deactivate" : true,
39414 * Fires when this panel is resized if fitToFrame is true.
39415 * @param {Roo.ContentPanel} this
39416 * @param {Number} width The width after any component adjustments
39417 * @param {Number} height The height after any component adjustments
39423 * Fires when this tab is created
39424 * @param {Roo.ContentPanel} this
39435 if(this.autoScroll){
39436 this.resizeEl.setStyle("overflow", "auto");
39438 // fix randome scrolling
39439 //this.el.on('scroll', function() {
39440 // Roo.log('fix random scolling');
39441 // this.scrollTo('top',0);
39444 content = content || this.content;
39446 this.setContent(content);
39448 if(config && config.url){
39449 this.setUrl(this.url, this.params, this.loadOnce);
39454 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39456 if (this.view && typeof(this.view.xtype) != 'undefined') {
39457 this.view.el = this.el.appendChild(document.createElement("div"));
39458 this.view = Roo.factory(this.view);
39459 this.view.render && this.view.render(false, '');
39463 this.fireEvent('render', this);
39466 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39473 setRegion : function(region){
39474 this.region = region;
39475 this.setActiveClass(region && !this.background);
39479 setActiveClass: function(state)
39482 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39483 this.el.setStyle('position','relative');
39485 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39486 this.el.setStyle('position', 'absolute');
39491 * Returns the toolbar for this Panel if one was configured.
39492 * @return {Roo.Toolbar}
39494 getToolbar : function(){
39495 return this.toolbar;
39498 setActiveState : function(active)
39500 this.active = active;
39501 this.setActiveClass(active);
39503 if(this.fireEvent("deactivate", this) === false){
39508 this.fireEvent("activate", this);
39512 * Updates this panel's element
39513 * @param {String} content The new content
39514 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39516 setContent : function(content, loadScripts){
39517 this.el.update(content, loadScripts);
39520 ignoreResize : function(w, h){
39521 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39524 this.lastSize = {width: w, height: h};
39529 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39530 * @return {Roo.UpdateManager} The UpdateManager
39532 getUpdateManager : function(){
39533 return this.el.getUpdateManager();
39536 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39537 * @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:
39540 url: "your-url.php",
39541 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39542 callback: yourFunction,
39543 scope: yourObject, //(optional scope)
39546 text: "Loading...",
39551 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39552 * 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.
39553 * @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}
39554 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39555 * @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.
39556 * @return {Roo.ContentPanel} this
39559 var um = this.el.getUpdateManager();
39560 um.update.apply(um, arguments);
39566 * 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.
39567 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39568 * @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)
39569 * @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)
39570 * @return {Roo.UpdateManager} The UpdateManager
39572 setUrl : function(url, params, loadOnce){
39573 if(this.refreshDelegate){
39574 this.removeListener("activate", this.refreshDelegate);
39576 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39577 this.on("activate", this.refreshDelegate);
39578 return this.el.getUpdateManager();
39581 _handleRefresh : function(url, params, loadOnce){
39582 if(!loadOnce || !this.loaded){
39583 var updater = this.el.getUpdateManager();
39584 updater.update(url, params, this._setLoaded.createDelegate(this));
39588 _setLoaded : function(){
39589 this.loaded = true;
39593 * Returns this panel's id
39596 getId : function(){
39601 * Returns this panel's element - used by regiosn to add.
39602 * @return {Roo.Element}
39604 getEl : function(){
39605 return this.wrapEl || this.el;
39610 adjustForComponents : function(width, height)
39612 //Roo.log('adjustForComponents ');
39613 if(this.resizeEl != this.el){
39614 width -= this.el.getFrameWidth('lr');
39615 height -= this.el.getFrameWidth('tb');
39618 var te = this.toolbar.getEl();
39619 te.setWidth(width);
39620 height -= te.getHeight();
39623 var te = this.footer.getEl();
39624 te.setWidth(width);
39625 height -= te.getHeight();
39629 if(this.adjustments){
39630 width += this.adjustments[0];
39631 height += this.adjustments[1];
39633 return {"width": width, "height": height};
39636 setSize : function(width, height){
39637 if(this.fitToFrame && !this.ignoreResize(width, height)){
39638 if(this.fitContainer && this.resizeEl != this.el){
39639 this.el.setSize(width, height);
39641 var size = this.adjustForComponents(width, height);
39642 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39643 this.fireEvent('resize', this, size.width, size.height);
39648 * Returns this panel's title
39651 getTitle : function(){
39653 if (typeof(this.title) != 'object') {
39658 for (var k in this.title) {
39659 if (!this.title.hasOwnProperty(k)) {
39663 if (k.indexOf('-') >= 0) {
39664 var s = k.split('-');
39665 for (var i = 0; i<s.length; i++) {
39666 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39669 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39676 * Set this panel's title
39677 * @param {String} title
39679 setTitle : function(title){
39680 this.title = title;
39682 this.region.updatePanelTitle(this, title);
39687 * Returns true is this panel was configured to be closable
39688 * @return {Boolean}
39690 isClosable : function(){
39691 return this.closable;
39694 beforeSlide : function(){
39696 this.resizeEl.clip();
39699 afterSlide : function(){
39701 this.resizeEl.unclip();
39705 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39706 * Will fail silently if the {@link #setUrl} method has not been called.
39707 * This does not activate the panel, just updates its content.
39709 refresh : function(){
39710 if(this.refreshDelegate){
39711 this.loaded = false;
39712 this.refreshDelegate();
39717 * Destroys this panel
39719 destroy : function(){
39720 this.el.removeAllListeners();
39721 var tempEl = document.createElement("span");
39722 tempEl.appendChild(this.el.dom);
39723 tempEl.innerHTML = "";
39729 * form - if the content panel contains a form - this is a reference to it.
39730 * @type {Roo.form.Form}
39734 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39735 * This contains a reference to it.
39741 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39751 * @param {Object} cfg Xtype definition of item to add.
39755 getChildContainer: function () {
39756 return this.getEl();
39761 var ret = new Roo.factory(cfg);
39766 if (cfg.xtype.match(/^Form$/)) {
39769 //if (this.footer) {
39770 // el = this.footer.container.insertSibling(false, 'before');
39772 el = this.el.createChild();
39775 this.form = new Roo.form.Form(cfg);
39778 if ( this.form.allItems.length) {
39779 this.form.render(el.dom);
39783 // should only have one of theses..
39784 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39785 // views.. should not be just added - used named prop 'view''
39787 cfg.el = this.el.appendChild(document.createElement("div"));
39790 var ret = new Roo.factory(cfg);
39792 ret.render && ret.render(false, ''); // render blank..
39802 * @class Roo.bootstrap.panel.Grid
39803 * @extends Roo.bootstrap.panel.Content
39805 * Create a new GridPanel.
39806 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39807 * @param {Object} config A the config object
39813 Roo.bootstrap.panel.Grid = function(config)
39817 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39818 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39820 config.el = this.wrapper;
39821 //this.el = this.wrapper;
39823 if (config.container) {
39824 // ctor'ed from a Border/panel.grid
39827 this.wrapper.setStyle("overflow", "hidden");
39828 this.wrapper.addClass('roo-grid-container');
39833 if(config.toolbar){
39834 var tool_el = this.wrapper.createChild();
39835 this.toolbar = Roo.factory(config.toolbar);
39837 if (config.toolbar.items) {
39838 ti = config.toolbar.items ;
39839 delete config.toolbar.items ;
39843 this.toolbar.render(tool_el);
39844 for(var i =0;i < ti.length;i++) {
39845 // Roo.log(['add child', items[i]]);
39846 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39848 this.toolbar.items = nitems;
39850 delete config.toolbar;
39853 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39854 config.grid.scrollBody = true;;
39855 config.grid.monitorWindowResize = false; // turn off autosizing
39856 config.grid.autoHeight = false;
39857 config.grid.autoWidth = false;
39859 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39861 if (config.background) {
39862 // render grid on panel activation (if panel background)
39863 this.on('activate', function(gp) {
39864 if (!gp.grid.rendered) {
39865 gp.grid.render(this.wrapper);
39866 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39871 this.grid.render(this.wrapper);
39872 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39875 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39876 // ??? needed ??? config.el = this.wrapper;
39881 // xtype created footer. - not sure if will work as we normally have to render first..
39882 if (this.footer && !this.footer.el && this.footer.xtype) {
39884 var ctr = this.grid.getView().getFooterPanel(true);
39885 this.footer.dataSource = this.grid.dataSource;
39886 this.footer = Roo.factory(this.footer, Roo);
39887 this.footer.render(ctr);
39897 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39898 getId : function(){
39899 return this.grid.id;
39903 * Returns the grid for this panel
39904 * @return {Roo.bootstrap.Table}
39906 getGrid : function(){
39910 setSize : function(width, height){
39911 if(!this.ignoreResize(width, height)){
39912 var grid = this.grid;
39913 var size = this.adjustForComponents(width, height);
39914 // tfoot is not a footer?
39917 var gridel = grid.getGridEl();
39918 gridel.setSize(size.width, size.height);
39920 var tbd = grid.getGridEl().select('tbody', true).first();
39921 var thd = grid.getGridEl().select('thead',true).first();
39922 var tbf= grid.getGridEl().select('tfoot', true).first();
39925 size.height -= thd.getHeight();
39928 size.height -= thd.getHeight();
39931 tbd.setSize(size.width, size.height );
39932 // this is for the account management tab -seems to work there.
39933 var thd = grid.getGridEl().select('thead',true).first();
39935 // tbd.setSize(size.width, size.height - thd.getHeight());
39944 beforeSlide : function(){
39945 this.grid.getView().scroller.clip();
39948 afterSlide : function(){
39949 this.grid.getView().scroller.unclip();
39952 destroy : function(){
39953 this.grid.destroy();
39955 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39960 * @class Roo.bootstrap.panel.Nest
39961 * @extends Roo.bootstrap.panel.Content
39963 * Create a new Panel, that can contain a layout.Border.
39966 * @param {Roo.BorderLayout} layout The layout for this panel
39967 * @param {String/Object} config A string to set only the title or a config object
39969 Roo.bootstrap.panel.Nest = function(config)
39971 // construct with only one argument..
39972 /* FIXME - implement nicer consturctors
39973 if (layout.layout) {
39975 layout = config.layout;
39976 delete config.layout;
39978 if (layout.xtype && !layout.getEl) {
39979 // then layout needs constructing..
39980 layout = Roo.factory(layout, Roo);
39984 config.el = config.layout.getEl();
39986 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39988 config.layout.monitorWindowResize = false; // turn off autosizing
39989 this.layout = config.layout;
39990 this.layout.getEl().addClass("roo-layout-nested-layout");
39991 this.layout.parent = this;
39998 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40000 setSize : function(width, height){
40001 if(!this.ignoreResize(width, height)){
40002 var size = this.adjustForComponents(width, height);
40003 var el = this.layout.getEl();
40004 if (size.height < 1) {
40005 el.setWidth(size.width);
40007 el.setSize(size.width, size.height);
40009 var touch = el.dom.offsetWidth;
40010 this.layout.layout();
40011 // ie requires a double layout on the first pass
40012 if(Roo.isIE && !this.initialized){
40013 this.initialized = true;
40014 this.layout.layout();
40019 // activate all subpanels if not currently active..
40021 setActiveState : function(active){
40022 this.active = active;
40023 this.setActiveClass(active);
40026 this.fireEvent("deactivate", this);
40030 this.fireEvent("activate", this);
40031 // not sure if this should happen before or after..
40032 if (!this.layout) {
40033 return; // should not happen..
40036 for (var r in this.layout.regions) {
40037 reg = this.layout.getRegion(r);
40038 if (reg.getActivePanel()) {
40039 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40040 reg.setActivePanel(reg.getActivePanel());
40043 if (!reg.panels.length) {
40046 reg.showPanel(reg.getPanel(0));
40055 * Returns the nested BorderLayout for this panel
40056 * @return {Roo.BorderLayout}
40058 getLayout : function(){
40059 return this.layout;
40063 * Adds a xtype elements to the layout of the nested panel
40067 xtype : 'ContentPanel',
40074 xtype : 'NestedLayoutPanel',
40080 items : [ ... list of content panels or nested layout panels.. ]
40084 * @param {Object} cfg Xtype definition of item to add.
40086 addxtype : function(cfg) {
40087 return this.layout.addxtype(cfg);
40092 * Ext JS Library 1.1.1
40093 * Copyright(c) 2006-2007, Ext JS, LLC.
40095 * Originally Released Under LGPL - original licence link has changed is not relivant.
40098 * <script type="text/javascript">
40101 * @class Roo.TabPanel
40102 * @extends Roo.util.Observable
40103 * A lightweight tab container.
40107 // basic tabs 1, built from existing content
40108 var tabs = new Roo.TabPanel("tabs1");
40109 tabs.addTab("script", "View Script");
40110 tabs.addTab("markup", "View Markup");
40111 tabs.activate("script");
40113 // more advanced tabs, built from javascript
40114 var jtabs = new Roo.TabPanel("jtabs");
40115 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40117 // set up the UpdateManager
40118 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40119 var updater = tab2.getUpdateManager();
40120 updater.setDefaultUrl("ajax1.htm");
40121 tab2.on('activate', updater.refresh, updater, true);
40123 // Use setUrl for Ajax loading
40124 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40125 tab3.setUrl("ajax2.htm", null, true);
40128 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40131 jtabs.activate("jtabs-1");
40134 * Create a new TabPanel.
40135 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40136 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40138 Roo.bootstrap.panel.Tabs = function(config){
40140 * The container element for this TabPanel.
40141 * @type Roo.Element
40143 this.el = Roo.get(config.el);
40146 if(typeof config == "boolean"){
40147 this.tabPosition = config ? "bottom" : "top";
40149 Roo.apply(this, config);
40153 if(this.tabPosition == "bottom"){
40154 // if tabs are at the bottom = create the body first.
40155 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40156 this.el.addClass("roo-tabs-bottom");
40158 // next create the tabs holders
40160 if (this.tabPosition == "west"){
40162 var reg = this.region; // fake it..
40164 if (!reg.mgr.parent) {
40167 reg = reg.mgr.parent.region;
40169 Roo.log("got nest?");
40171 if (reg.mgr.getRegion('west')) {
40172 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40173 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40174 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40175 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40176 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40184 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40185 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40186 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40187 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40192 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40195 // finally - if tabs are at the top, then create the body last..
40196 if(this.tabPosition != "bottom"){
40197 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40198 * @type Roo.Element
40200 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40201 this.el.addClass("roo-tabs-top");
40205 this.bodyEl.setStyle("position", "relative");
40207 this.active = null;
40208 this.activateDelegate = this.activate.createDelegate(this);
40213 * Fires when the active tab changes
40214 * @param {Roo.TabPanel} this
40215 * @param {Roo.TabPanelItem} activePanel The new active tab
40219 * @event beforetabchange
40220 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40221 * @param {Roo.TabPanel} this
40222 * @param {Object} e Set cancel to true on this object to cancel the tab change
40223 * @param {Roo.TabPanelItem} tab The tab being changed to
40225 "beforetabchange" : true
40228 Roo.EventManager.onWindowResize(this.onResize, this);
40229 this.cpad = this.el.getPadding("lr");
40230 this.hiddenCount = 0;
40233 // toolbar on the tabbar support...
40234 if (this.toolbar) {
40235 alert("no toolbar support yet");
40236 this.toolbar = false;
40238 var tcfg = this.toolbar;
40239 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40240 this.toolbar = new Roo.Toolbar(tcfg);
40241 if (Roo.isSafari) {
40242 var tbl = tcfg.container.child('table', true);
40243 tbl.setAttribute('width', '100%');
40251 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40254 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40256 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40258 tabPosition : "top",
40260 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40262 currentTabWidth : 0,
40264 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40268 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40272 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40274 preferredTabWidth : 175,
40276 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40278 resizeTabs : false,
40280 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40282 monitorResize : true,
40284 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40286 toolbar : false, // set by caller..
40288 region : false, /// set by caller
40290 disableTooltips : true, // not used yet...
40293 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40294 * @param {String} id The id of the div to use <b>or create</b>
40295 * @param {String} text The text for the tab
40296 * @param {String} content (optional) Content to put in the TabPanelItem body
40297 * @param {Boolean} closable (optional) True to create a close icon on the tab
40298 * @return {Roo.TabPanelItem} The created TabPanelItem
40300 addTab : function(id, text, content, closable, tpl)
40302 var item = new Roo.bootstrap.panel.TabItem({
40306 closable : closable,
40309 this.addTabItem(item);
40311 item.setContent(content);
40317 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40318 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40319 * @return {Roo.TabPanelItem}
40321 getTab : function(id){
40322 return this.items[id];
40326 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40327 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40329 hideTab : function(id){
40330 var t = this.items[id];
40333 this.hiddenCount++;
40334 this.autoSizeTabs();
40339 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40340 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40342 unhideTab : function(id){
40343 var t = this.items[id];
40345 t.setHidden(false);
40346 this.hiddenCount--;
40347 this.autoSizeTabs();
40352 * Adds an existing {@link Roo.TabPanelItem}.
40353 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40355 addTabItem : function(item)
40357 this.items[item.id] = item;
40358 this.items.push(item);
40359 this.autoSizeTabs();
40360 // if(this.resizeTabs){
40361 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40362 // this.autoSizeTabs();
40364 // item.autoSize();
40369 * Removes a {@link Roo.TabPanelItem}.
40370 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40372 removeTab : function(id){
40373 var items = this.items;
40374 var tab = items[id];
40375 if(!tab) { return; }
40376 var index = items.indexOf(tab);
40377 if(this.active == tab && items.length > 1){
40378 var newTab = this.getNextAvailable(index);
40383 this.stripEl.dom.removeChild(tab.pnode.dom);
40384 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40385 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40387 items.splice(index, 1);
40388 delete this.items[tab.id];
40389 tab.fireEvent("close", tab);
40390 tab.purgeListeners();
40391 this.autoSizeTabs();
40394 getNextAvailable : function(start){
40395 var items = this.items;
40397 // look for a next tab that will slide over to
40398 // replace the one being removed
40399 while(index < items.length){
40400 var item = items[++index];
40401 if(item && !item.isHidden()){
40405 // if one isn't found select the previous tab (on the left)
40408 var item = items[--index];
40409 if(item && !item.isHidden()){
40417 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40418 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40420 disableTab : function(id){
40421 var tab = this.items[id];
40422 if(tab && this.active != tab){
40428 * Enables a {@link Roo.TabPanelItem} that is disabled.
40429 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40431 enableTab : function(id){
40432 var tab = this.items[id];
40437 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40438 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40439 * @return {Roo.TabPanelItem} The TabPanelItem.
40441 activate : function(id)
40443 //Roo.log('activite:' + id);
40445 var tab = this.items[id];
40449 if(tab == this.active || tab.disabled){
40453 this.fireEvent("beforetabchange", this, e, tab);
40454 if(e.cancel !== true && !tab.disabled){
40456 this.active.hide();
40458 this.active = this.items[id];
40459 this.active.show();
40460 this.fireEvent("tabchange", this, this.active);
40466 * Gets the active {@link Roo.TabPanelItem}.
40467 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40469 getActiveTab : function(){
40470 return this.active;
40474 * Updates the tab body element to fit the height of the container element
40475 * for overflow scrolling
40476 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40478 syncHeight : function(targetHeight){
40479 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40480 var bm = this.bodyEl.getMargins();
40481 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40482 this.bodyEl.setHeight(newHeight);
40486 onResize : function(){
40487 if(this.monitorResize){
40488 this.autoSizeTabs();
40493 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40495 beginUpdate : function(){
40496 this.updating = true;
40500 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40502 endUpdate : function(){
40503 this.updating = false;
40504 this.autoSizeTabs();
40508 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40510 autoSizeTabs : function()
40512 var count = this.items.length;
40513 var vcount = count - this.hiddenCount;
40516 this.stripEl.hide();
40518 this.stripEl.show();
40521 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40526 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40527 var availWidth = Math.floor(w / vcount);
40528 var b = this.stripBody;
40529 if(b.getWidth() > w){
40530 var tabs = this.items;
40531 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40532 if(availWidth < this.minTabWidth){
40533 /*if(!this.sleft){ // incomplete scrolling code
40534 this.createScrollButtons();
40537 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40540 if(this.currentTabWidth < this.preferredTabWidth){
40541 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40547 * Returns the number of tabs in this TabPanel.
40550 getCount : function(){
40551 return this.items.length;
40555 * Resizes all the tabs to the passed width
40556 * @param {Number} The new width
40558 setTabWidth : function(width){
40559 this.currentTabWidth = width;
40560 for(var i = 0, len = this.items.length; i < len; i++) {
40561 if(!this.items[i].isHidden()) {
40562 this.items[i].setWidth(width);
40568 * Destroys this TabPanel
40569 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40571 destroy : function(removeEl){
40572 Roo.EventManager.removeResizeListener(this.onResize, this);
40573 for(var i = 0, len = this.items.length; i < len; i++){
40574 this.items[i].purgeListeners();
40576 if(removeEl === true){
40577 this.el.update("");
40582 createStrip : function(container)
40584 var strip = document.createElement("nav");
40585 strip.className = Roo.bootstrap.version == 4 ?
40586 "navbar-light bg-light" :
40587 "navbar navbar-default"; //"x-tabs-wrap";
40588 container.appendChild(strip);
40592 createStripList : function(strip)
40594 // div wrapper for retard IE
40595 // returns the "tr" element.
40596 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40597 //'<div class="x-tabs-strip-wrap">'+
40598 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40599 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40600 return strip.firstChild; //.firstChild.firstChild.firstChild;
40602 createBody : function(container)
40604 var body = document.createElement("div");
40605 Roo.id(body, "tab-body");
40606 //Roo.fly(body).addClass("x-tabs-body");
40607 Roo.fly(body).addClass("tab-content");
40608 container.appendChild(body);
40611 createItemBody :function(bodyEl, id){
40612 var body = Roo.getDom(id);
40614 body = document.createElement("div");
40617 //Roo.fly(body).addClass("x-tabs-item-body");
40618 Roo.fly(body).addClass("tab-pane");
40619 bodyEl.insertBefore(body, bodyEl.firstChild);
40623 createStripElements : function(stripEl, text, closable, tpl)
40625 var td = document.createElement("li"); // was td..
40626 td.className = 'nav-item';
40628 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40631 stripEl.appendChild(td);
40633 td.className = "x-tabs-closable";
40634 if(!this.closeTpl){
40635 this.closeTpl = new Roo.Template(
40636 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40637 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40638 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40641 var el = this.closeTpl.overwrite(td, {"text": text});
40642 var close = el.getElementsByTagName("div")[0];
40643 var inner = el.getElementsByTagName("em")[0];
40644 return {"el": el, "close": close, "inner": inner};
40647 // not sure what this is..
40648 // if(!this.tabTpl){
40649 //this.tabTpl = new Roo.Template(
40650 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40651 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40653 // this.tabTpl = new Roo.Template(
40654 // '<a href="#">' +
40655 // '<span unselectable="on"' +
40656 // (this.disableTooltips ? '' : ' title="{text}"') +
40657 // ' >{text}</span></a>'
40663 var template = tpl || this.tabTpl || false;
40666 template = new Roo.Template(
40667 Roo.bootstrap.version == 4 ?
40669 '<a class="nav-link" href="#" unselectable="on"' +
40670 (this.disableTooltips ? '' : ' title="{text}"') +
40673 '<a class="nav-link" href="#">' +
40674 '<span unselectable="on"' +
40675 (this.disableTooltips ? '' : ' title="{text}"') +
40676 ' >{text}</span></a>'
40681 switch (typeof(template)) {
40685 template = new Roo.Template(template);
40691 var el = template.overwrite(td, {"text": text});
40693 var inner = el.getElementsByTagName("span")[0];
40695 return {"el": el, "inner": inner};
40703 * @class Roo.TabPanelItem
40704 * @extends Roo.util.Observable
40705 * Represents an individual item (tab plus body) in a TabPanel.
40706 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40707 * @param {String} id The id of this TabPanelItem
40708 * @param {String} text The text for the tab of this TabPanelItem
40709 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40711 Roo.bootstrap.panel.TabItem = function(config){
40713 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40714 * @type Roo.TabPanel
40716 this.tabPanel = config.panel;
40718 * The id for this TabPanelItem
40721 this.id = config.id;
40723 this.disabled = false;
40725 this.text = config.text;
40727 this.loaded = false;
40728 this.closable = config.closable;
40731 * The body element for this TabPanelItem.
40732 * @type Roo.Element
40734 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40735 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40736 this.bodyEl.setStyle("display", "block");
40737 this.bodyEl.setStyle("zoom", "1");
40738 //this.hideAction();
40740 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40742 this.el = Roo.get(els.el);
40743 this.inner = Roo.get(els.inner, true);
40744 this.textEl = Roo.bootstrap.version == 4 ?
40745 this.el : Roo.get(this.el.dom.firstChild, true);
40747 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40748 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40751 // this.el.on("mousedown", this.onTabMouseDown, this);
40752 this.el.on("click", this.onTabClick, this);
40754 if(config.closable){
40755 var c = Roo.get(els.close, true);
40756 c.dom.title = this.closeText;
40757 c.addClassOnOver("close-over");
40758 c.on("click", this.closeClick, this);
40764 * Fires when this tab becomes the active tab.
40765 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40766 * @param {Roo.TabPanelItem} this
40770 * @event beforeclose
40771 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40772 * @param {Roo.TabPanelItem} this
40773 * @param {Object} e Set cancel to true on this object to cancel the close.
40775 "beforeclose": true,
40778 * Fires when this tab is closed.
40779 * @param {Roo.TabPanelItem} this
40783 * @event deactivate
40784 * Fires when this tab is no longer the active tab.
40785 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40786 * @param {Roo.TabPanelItem} this
40788 "deactivate" : true
40790 this.hidden = false;
40792 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40795 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40797 purgeListeners : function(){
40798 Roo.util.Observable.prototype.purgeListeners.call(this);
40799 this.el.removeAllListeners();
40802 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40805 this.status_node.addClass("active");
40808 this.tabPanel.stripWrap.repaint();
40810 this.fireEvent("activate", this.tabPanel, this);
40814 * Returns true if this tab is the active tab.
40815 * @return {Boolean}
40817 isActive : function(){
40818 return this.tabPanel.getActiveTab() == this;
40822 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40825 this.status_node.removeClass("active");
40827 this.fireEvent("deactivate", this.tabPanel, this);
40830 hideAction : function(){
40831 this.bodyEl.hide();
40832 this.bodyEl.setStyle("position", "absolute");
40833 this.bodyEl.setLeft("-20000px");
40834 this.bodyEl.setTop("-20000px");
40837 showAction : function(){
40838 this.bodyEl.setStyle("position", "relative");
40839 this.bodyEl.setTop("");
40840 this.bodyEl.setLeft("");
40841 this.bodyEl.show();
40845 * Set the tooltip for the tab.
40846 * @param {String} tooltip The tab's tooltip
40848 setTooltip : function(text){
40849 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40850 this.textEl.dom.qtip = text;
40851 this.textEl.dom.removeAttribute('title');
40853 this.textEl.dom.title = text;
40857 onTabClick : function(e){
40858 e.preventDefault();
40859 this.tabPanel.activate(this.id);
40862 onTabMouseDown : function(e){
40863 e.preventDefault();
40864 this.tabPanel.activate(this.id);
40867 getWidth : function(){
40868 return this.inner.getWidth();
40871 setWidth : function(width){
40872 var iwidth = width - this.linode.getPadding("lr");
40873 this.inner.setWidth(iwidth);
40874 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40875 this.linode.setWidth(width);
40879 * Show or hide the tab
40880 * @param {Boolean} hidden True to hide or false to show.
40882 setHidden : function(hidden){
40883 this.hidden = hidden;
40884 this.linode.setStyle("display", hidden ? "none" : "");
40888 * Returns true if this tab is "hidden"
40889 * @return {Boolean}
40891 isHidden : function(){
40892 return this.hidden;
40896 * Returns the text for this tab
40899 getText : function(){
40903 autoSize : function(){
40904 //this.el.beginMeasure();
40905 this.textEl.setWidth(1);
40907 * #2804 [new] Tabs in Roojs
40908 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40910 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40911 //this.el.endMeasure();
40915 * Sets the text for the tab (Note: this also sets the tooltip text)
40916 * @param {String} text The tab's text and tooltip
40918 setText : function(text){
40920 this.textEl.update(text);
40921 this.setTooltip(text);
40922 //if(!this.tabPanel.resizeTabs){
40923 // this.autoSize();
40927 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40929 activate : function(){
40930 this.tabPanel.activate(this.id);
40934 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40936 disable : function(){
40937 if(this.tabPanel.active != this){
40938 this.disabled = true;
40939 this.status_node.addClass("disabled");
40944 * Enables this TabPanelItem if it was previously disabled.
40946 enable : function(){
40947 this.disabled = false;
40948 this.status_node.removeClass("disabled");
40952 * Sets the content for this TabPanelItem.
40953 * @param {String} content The content
40954 * @param {Boolean} loadScripts true to look for and load scripts
40956 setContent : function(content, loadScripts){
40957 this.bodyEl.update(content, loadScripts);
40961 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40962 * @return {Roo.UpdateManager} The UpdateManager
40964 getUpdateManager : function(){
40965 return this.bodyEl.getUpdateManager();
40969 * Set a URL to be used to load the content for this TabPanelItem.
40970 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40971 * @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)
40972 * @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)
40973 * @return {Roo.UpdateManager} The UpdateManager
40975 setUrl : function(url, params, loadOnce){
40976 if(this.refreshDelegate){
40977 this.un('activate', this.refreshDelegate);
40979 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40980 this.on("activate", this.refreshDelegate);
40981 return this.bodyEl.getUpdateManager();
40985 _handleRefresh : function(url, params, loadOnce){
40986 if(!loadOnce || !this.loaded){
40987 var updater = this.bodyEl.getUpdateManager();
40988 updater.update(url, params, this._setLoaded.createDelegate(this));
40993 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40994 * Will fail silently if the setUrl method has not been called.
40995 * This does not activate the panel, just updates its content.
40997 refresh : function(){
40998 if(this.refreshDelegate){
40999 this.loaded = false;
41000 this.refreshDelegate();
41005 _setLoaded : function(){
41006 this.loaded = true;
41010 closeClick : function(e){
41013 this.fireEvent("beforeclose", this, o);
41014 if(o.cancel !== true){
41015 this.tabPanel.removeTab(this.id);
41019 * The text displayed in the tooltip for the close icon.
41022 closeText : "Close this tab"
41025 * This script refer to:
41026 * Title: International Telephone Input
41027 * Author: Jack O'Connor
41028 * Code version: v12.1.12
41029 * Availability: https://github.com/jackocnr/intl-tel-input.git
41032 Roo.bootstrap.PhoneInputData = function() {
41035 "Afghanistan (افغانستان)",
41040 "Albania (Shqipëri)",
41045 "Algeria (الجزائر)",
41070 "Antigua and Barbuda",
41080 "Armenia (Հայաստան)",
41096 "Austria (Österreich)",
41101 "Azerbaijan (Azərbaycan)",
41111 "Bahrain (البحرين)",
41116 "Bangladesh (বাংলাদেশ)",
41126 "Belarus (Беларусь)",
41131 "Belgium (België)",
41161 "Bosnia and Herzegovina (Босна и Херцеговина)",
41176 "British Indian Ocean Territory",
41181 "British Virgin Islands",
41191 "Bulgaria (България)",
41201 "Burundi (Uburundi)",
41206 "Cambodia (កម្ពុជា)",
41211 "Cameroon (Cameroun)",
41220 ["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"]
41223 "Cape Verde (Kabu Verdi)",
41228 "Caribbean Netherlands",
41239 "Central African Republic (République centrafricaine)",
41259 "Christmas Island",
41265 "Cocos (Keeling) Islands",
41276 "Comoros (جزر القمر)",
41281 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41286 "Congo (Republic) (Congo-Brazzaville)",
41306 "Croatia (Hrvatska)",
41327 "Czech Republic (Česká republika)",
41332 "Denmark (Danmark)",
41347 "Dominican Republic (República Dominicana)",
41351 ["809", "829", "849"]
41369 "Equatorial Guinea (Guinea Ecuatorial)",
41389 "Falkland Islands (Islas Malvinas)",
41394 "Faroe Islands (Føroyar)",
41415 "French Guiana (Guyane française)",
41420 "French Polynesia (Polynésie française)",
41435 "Georgia (საქართველო)",
41440 "Germany (Deutschland)",
41460 "Greenland (Kalaallit Nunaat)",
41497 "Guinea-Bissau (Guiné Bissau)",
41522 "Hungary (Magyarország)",
41527 "Iceland (Ísland)",
41547 "Iraq (العراق)",
41563 "Israel (ישראל)",
41590 "Jordan (الأردن)",
41595 "Kazakhstan (Казахстан)",
41616 "Kuwait (الكويت)",
41621 "Kyrgyzstan (Кыргызстан)",
41631 "Latvia (Latvija)",
41636 "Lebanon (لبنان)",
41651 "Libya (ليبيا)",
41661 "Lithuania (Lietuva)",
41676 "Macedonia (FYROM) (Македонија)",
41681 "Madagascar (Madagasikara)",
41711 "Marshall Islands",
41721 "Mauritania (موريتانيا)",
41726 "Mauritius (Moris)",
41747 "Moldova (Republica Moldova)",
41757 "Mongolia (Монгол)",
41762 "Montenegro (Crna Gora)",
41772 "Morocco (المغرب)",
41778 "Mozambique (Moçambique)",
41783 "Myanmar (Burma) (မြန်မာ)",
41788 "Namibia (Namibië)",
41803 "Netherlands (Nederland)",
41808 "New Caledonia (Nouvelle-Calédonie)",
41843 "North Korea (조선 민주주의 인민 공화국)",
41848 "Northern Mariana Islands",
41864 "Pakistan (پاکستان)",
41874 "Palestine (فلسطين)",
41884 "Papua New Guinea",
41926 "Réunion (La Réunion)",
41932 "Romania (România)",
41948 "Saint Barthélemy",
41959 "Saint Kitts and Nevis",
41969 "Saint Martin (Saint-Martin (partie française))",
41975 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41980 "Saint Vincent and the Grenadines",
41995 "São Tomé and Príncipe (São Tomé e Príncipe)",
42000 "Saudi Arabia (المملكة العربية السعودية)",
42005 "Senegal (Sénégal)",
42035 "Slovakia (Slovensko)",
42040 "Slovenia (Slovenija)",
42050 "Somalia (Soomaaliya)",
42060 "South Korea (대한민국)",
42065 "South Sudan (جنوب السودان)",
42075 "Sri Lanka (ශ්රී ලංකාව)",
42080 "Sudan (السودان)",
42090 "Svalbard and Jan Mayen",
42101 "Sweden (Sverige)",
42106 "Switzerland (Schweiz)",
42111 "Syria (سوريا)",
42156 "Trinidad and Tobago",
42161 "Tunisia (تونس)",
42166 "Turkey (Türkiye)",
42176 "Turks and Caicos Islands",
42186 "U.S. Virgin Islands",
42196 "Ukraine (Україна)",
42201 "United Arab Emirates (الإمارات العربية المتحدة)",
42223 "Uzbekistan (Oʻzbekiston)",
42233 "Vatican City (Città del Vaticano)",
42244 "Vietnam (Việt Nam)",
42249 "Wallis and Futuna (Wallis-et-Futuna)",
42254 "Western Sahara (الصحراء الغربية)",
42260 "Yemen (اليمن)",
42284 * This script refer to:
42285 * Title: International Telephone Input
42286 * Author: Jack O'Connor
42287 * Code version: v12.1.12
42288 * Availability: https://github.com/jackocnr/intl-tel-input.git
42292 * @class Roo.bootstrap.PhoneInput
42293 * @extends Roo.bootstrap.TriggerField
42294 * An input with International dial-code selection
42296 * @cfg {String} defaultDialCode default '+852'
42297 * @cfg {Array} preferedCountries default []
42300 * Create a new PhoneInput.
42301 * @param {Object} config Configuration options
42304 Roo.bootstrap.PhoneInput = function(config) {
42305 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42308 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42310 listWidth: undefined,
42312 selectedClass: 'active',
42314 invalidClass : "has-warning",
42316 validClass: 'has-success',
42318 allowed: '0123456789',
42323 * @cfg {String} defaultDialCode The default dial code when initializing the input
42325 defaultDialCode: '+852',
42328 * @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
42330 preferedCountries: false,
42332 getAutoCreate : function()
42334 var data = Roo.bootstrap.PhoneInputData();
42335 var align = this.labelAlign || this.parentLabelAlign();
42338 this.allCountries = [];
42339 this.dialCodeMapping = [];
42341 for (var i = 0; i < data.length; i++) {
42343 this.allCountries[i] = {
42347 priority: c[3] || 0,
42348 areaCodes: c[4] || null
42350 this.dialCodeMapping[c[2]] = {
42353 priority: c[3] || 0,
42354 areaCodes: c[4] || null
42366 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42367 maxlength: this.max_length,
42368 cls : 'form-control tel-input',
42369 autocomplete: 'new-password'
42372 var hiddenInput = {
42375 cls: 'hidden-tel-input'
42379 hiddenInput.name = this.name;
42382 if (this.disabled) {
42383 input.disabled = true;
42386 var flag_container = {
42403 cls: this.hasFeedback ? 'has-feedback' : '',
42409 cls: 'dial-code-holder',
42416 cls: 'roo-select2-container input-group',
42423 if (this.fieldLabel.length) {
42426 tooltip: 'This field is required'
42432 cls: 'control-label',
42438 html: this.fieldLabel
42441 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42447 if(this.indicatorpos == 'right') {
42448 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42455 if(align == 'left') {
42463 if(this.labelWidth > 12){
42464 label.style = "width: " + this.labelWidth + 'px';
42466 if(this.labelWidth < 13 && this.labelmd == 0){
42467 this.labelmd = this.labelWidth;
42469 if(this.labellg > 0){
42470 label.cls += ' col-lg-' + this.labellg;
42471 input.cls += ' col-lg-' + (12 - this.labellg);
42473 if(this.labelmd > 0){
42474 label.cls += ' col-md-' + this.labelmd;
42475 container.cls += ' col-md-' + (12 - this.labelmd);
42477 if(this.labelsm > 0){
42478 label.cls += ' col-sm-' + this.labelsm;
42479 container.cls += ' col-sm-' + (12 - this.labelsm);
42481 if(this.labelxs > 0){
42482 label.cls += ' col-xs-' + this.labelxs;
42483 container.cls += ' col-xs-' + (12 - this.labelxs);
42493 var settings = this;
42495 ['xs','sm','md','lg'].map(function(size){
42496 if (settings[size]) {
42497 cfg.cls += ' col-' + size + '-' + settings[size];
42501 this.store = new Roo.data.Store({
42502 proxy : new Roo.data.MemoryProxy({}),
42503 reader : new Roo.data.JsonReader({
42514 'name' : 'dialCode',
42518 'name' : 'priority',
42522 'name' : 'areaCodes',
42529 if(!this.preferedCountries) {
42530 this.preferedCountries = [
42537 var p = this.preferedCountries.reverse();
42540 for (var i = 0; i < p.length; i++) {
42541 for (var j = 0; j < this.allCountries.length; j++) {
42542 if(this.allCountries[j].iso2 == p[i]) {
42543 var t = this.allCountries[j];
42544 this.allCountries.splice(j,1);
42545 this.allCountries.unshift(t);
42551 this.store.proxy.data = {
42553 data: this.allCountries
42559 initEvents : function()
42562 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42564 this.indicator = this.indicatorEl();
42565 this.flag = this.flagEl();
42566 this.dialCodeHolder = this.dialCodeHolderEl();
42568 this.trigger = this.el.select('div.flag-box',true).first();
42569 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42574 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42575 _this.list.setWidth(lw);
42578 this.list.on('mouseover', this.onViewOver, this);
42579 this.list.on('mousemove', this.onViewMove, this);
42580 this.inputEl().on("keyup", this.onKeyUp, this);
42581 this.inputEl().on("keypress", this.onKeyPress, this);
42583 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42585 this.view = new Roo.View(this.list, this.tpl, {
42586 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42589 this.view.on('click', this.onViewClick, this);
42590 this.setValue(this.defaultDialCode);
42593 onTriggerClick : function(e)
42595 Roo.log('trigger click');
42600 if(this.isExpanded()){
42602 this.hasFocus = false;
42604 this.store.load({});
42605 this.hasFocus = true;
42610 isExpanded : function()
42612 return this.list.isVisible();
42615 collapse : function()
42617 if(!this.isExpanded()){
42621 Roo.get(document).un('mousedown', this.collapseIf, this);
42622 Roo.get(document).un('mousewheel', this.collapseIf, this);
42623 this.fireEvent('collapse', this);
42627 expand : function()
42631 if(this.isExpanded() || !this.hasFocus){
42635 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42636 this.list.setWidth(lw);
42639 this.restrictHeight();
42641 Roo.get(document).on('mousedown', this.collapseIf, this);
42642 Roo.get(document).on('mousewheel', this.collapseIf, this);
42644 this.fireEvent('expand', this);
42647 restrictHeight : function()
42649 this.list.alignTo(this.inputEl(), this.listAlign);
42650 this.list.alignTo(this.inputEl(), this.listAlign);
42653 onViewOver : function(e, t)
42655 if(this.inKeyMode){
42658 var item = this.view.findItemFromChild(t);
42661 var index = this.view.indexOf(item);
42662 this.select(index, false);
42667 onViewClick : function(view, doFocus, el, e)
42669 var index = this.view.getSelectedIndexes()[0];
42671 var r = this.store.getAt(index);
42674 this.onSelect(r, index);
42676 if(doFocus !== false && !this.blockFocus){
42677 this.inputEl().focus();
42681 onViewMove : function(e, t)
42683 this.inKeyMode = false;
42686 select : function(index, scrollIntoView)
42688 this.selectedIndex = index;
42689 this.view.select(index);
42690 if(scrollIntoView !== false){
42691 var el = this.view.getNode(index);
42693 this.list.scrollChildIntoView(el, false);
42698 createList : function()
42700 this.list = Roo.get(document.body).createChild({
42702 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42703 style: 'display:none'
42706 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42709 collapseIf : function(e)
42711 var in_combo = e.within(this.el);
42712 var in_list = e.within(this.list);
42713 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42715 if (in_combo || in_list || is_list) {
42721 onSelect : function(record, index)
42723 if(this.fireEvent('beforeselect', this, record, index) !== false){
42725 this.setFlagClass(record.data.iso2);
42726 this.setDialCode(record.data.dialCode);
42727 this.hasFocus = false;
42729 this.fireEvent('select', this, record, index);
42733 flagEl : function()
42735 var flag = this.el.select('div.flag',true).first();
42742 dialCodeHolderEl : function()
42744 var d = this.el.select('input.dial-code-holder',true).first();
42751 setDialCode : function(v)
42753 this.dialCodeHolder.dom.value = '+'+v;
42756 setFlagClass : function(n)
42758 this.flag.dom.className = 'flag '+n;
42761 getValue : function()
42763 var v = this.inputEl().getValue();
42764 if(this.dialCodeHolder) {
42765 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42770 setValue : function(v)
42772 var d = this.getDialCode(v);
42774 //invalid dial code
42775 if(v.length == 0 || !d || d.length == 0) {
42777 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42778 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42784 this.setFlagClass(this.dialCodeMapping[d].iso2);
42785 this.setDialCode(d);
42786 this.inputEl().dom.value = v.replace('+'+d,'');
42787 this.hiddenEl().dom.value = this.getValue();
42792 getDialCode : function(v)
42796 if (v.length == 0) {
42797 return this.dialCodeHolder.dom.value;
42801 if (v.charAt(0) != "+") {
42804 var numericChars = "";
42805 for (var i = 1; i < v.length; i++) {
42806 var c = v.charAt(i);
42809 if (this.dialCodeMapping[numericChars]) {
42810 dialCode = v.substr(1, i);
42812 if (numericChars.length == 4) {
42822 this.setValue(this.defaultDialCode);
42826 hiddenEl : function()
42828 return this.el.select('input.hidden-tel-input',true).first();
42831 // after setting val
42832 onKeyUp : function(e){
42833 this.setValue(this.getValue());
42836 onKeyPress : function(e){
42837 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42844 * @class Roo.bootstrap.MoneyField
42845 * @extends Roo.bootstrap.ComboBox
42846 * Bootstrap MoneyField class
42849 * Create a new MoneyField.
42850 * @param {Object} config Configuration options
42853 Roo.bootstrap.MoneyField = function(config) {
42855 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42859 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42862 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42864 allowDecimals : true,
42866 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42868 decimalSeparator : ".",
42870 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42872 decimalPrecision : 0,
42874 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42876 allowNegative : true,
42878 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42882 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42884 minValue : Number.NEGATIVE_INFINITY,
42886 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42888 maxValue : Number.MAX_VALUE,
42890 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42892 minText : "The minimum value for this field is {0}",
42894 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42896 maxText : "The maximum value for this field is {0}",
42898 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42899 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42901 nanText : "{0} is not a valid number",
42903 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42907 * @cfg {String} defaults currency of the MoneyField
42908 * value should be in lkey
42910 defaultCurrency : false,
42912 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42914 thousandsDelimiter : false,
42916 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42927 getAutoCreate : function()
42929 var align = this.labelAlign || this.parentLabelAlign();
42941 cls : 'form-control roo-money-amount-input',
42942 autocomplete: 'new-password'
42945 var hiddenInput = {
42949 cls: 'hidden-number-input'
42952 if(this.max_length) {
42953 input.maxlength = this.max_length;
42957 hiddenInput.name = this.name;
42960 if (this.disabled) {
42961 input.disabled = true;
42964 var clg = 12 - this.inputlg;
42965 var cmd = 12 - this.inputmd;
42966 var csm = 12 - this.inputsm;
42967 var cxs = 12 - this.inputxs;
42971 cls : 'row roo-money-field',
42975 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42979 cls: 'roo-select2-container input-group',
42983 cls : 'form-control roo-money-currency-input',
42984 autocomplete: 'new-password',
42986 name : this.currencyName
42990 cls : 'input-group-addon',
43004 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43008 cls: this.hasFeedback ? 'has-feedback' : '',
43019 if (this.fieldLabel.length) {
43022 tooltip: 'This field is required'
43028 cls: 'control-label',
43034 html: this.fieldLabel
43037 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43043 if(this.indicatorpos == 'right') {
43044 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43051 if(align == 'left') {
43059 if(this.labelWidth > 12){
43060 label.style = "width: " + this.labelWidth + 'px';
43062 if(this.labelWidth < 13 && this.labelmd == 0){
43063 this.labelmd = this.labelWidth;
43065 if(this.labellg > 0){
43066 label.cls += ' col-lg-' + this.labellg;
43067 input.cls += ' col-lg-' + (12 - this.labellg);
43069 if(this.labelmd > 0){
43070 label.cls += ' col-md-' + this.labelmd;
43071 container.cls += ' col-md-' + (12 - this.labelmd);
43073 if(this.labelsm > 0){
43074 label.cls += ' col-sm-' + this.labelsm;
43075 container.cls += ' col-sm-' + (12 - this.labelsm);
43077 if(this.labelxs > 0){
43078 label.cls += ' col-xs-' + this.labelxs;
43079 container.cls += ' col-xs-' + (12 - this.labelxs);
43090 var settings = this;
43092 ['xs','sm','md','lg'].map(function(size){
43093 if (settings[size]) {
43094 cfg.cls += ' col-' + size + '-' + settings[size];
43101 initEvents : function()
43103 this.indicator = this.indicatorEl();
43105 this.initCurrencyEvent();
43107 this.initNumberEvent();
43110 initCurrencyEvent : function()
43113 throw "can not find store for combo";
43116 this.store = Roo.factory(this.store, Roo.data);
43117 this.store.parent = this;
43121 this.triggerEl = this.el.select('.input-group-addon', true).first();
43123 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43128 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43129 _this.list.setWidth(lw);
43132 this.list.on('mouseover', this.onViewOver, this);
43133 this.list.on('mousemove', this.onViewMove, this);
43134 this.list.on('scroll', this.onViewScroll, this);
43137 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43140 this.view = new Roo.View(this.list, this.tpl, {
43141 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43144 this.view.on('click', this.onViewClick, this);
43146 this.store.on('beforeload', this.onBeforeLoad, this);
43147 this.store.on('load', this.onLoad, this);
43148 this.store.on('loadexception', this.onLoadException, this);
43150 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43151 "up" : function(e){
43152 this.inKeyMode = true;
43156 "down" : function(e){
43157 if(!this.isExpanded()){
43158 this.onTriggerClick();
43160 this.inKeyMode = true;
43165 "enter" : function(e){
43168 if(this.fireEvent("specialkey", this, e)){
43169 this.onViewClick(false);
43175 "esc" : function(e){
43179 "tab" : function(e){
43182 if(this.fireEvent("specialkey", this, e)){
43183 this.onViewClick(false);
43191 doRelay : function(foo, bar, hname){
43192 if(hname == 'down' || this.scope.isExpanded()){
43193 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43201 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43205 initNumberEvent : function(e)
43207 this.inputEl().on("keydown" , this.fireKey, this);
43208 this.inputEl().on("focus", this.onFocus, this);
43209 this.inputEl().on("blur", this.onBlur, this);
43211 this.inputEl().relayEvent('keyup', this);
43213 if(this.indicator){
43214 this.indicator.addClass('invisible');
43217 this.originalValue = this.getValue();
43219 if(this.validationEvent == 'keyup'){
43220 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43221 this.inputEl().on('keyup', this.filterValidation, this);
43223 else if(this.validationEvent !== false){
43224 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43227 if(this.selectOnFocus){
43228 this.on("focus", this.preFocus, this);
43231 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43232 this.inputEl().on("keypress", this.filterKeys, this);
43234 this.inputEl().relayEvent('keypress', this);
43237 var allowed = "0123456789";
43239 if(this.allowDecimals){
43240 allowed += this.decimalSeparator;
43243 if(this.allowNegative){
43247 if(this.thousandsDelimiter) {
43251 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43253 var keyPress = function(e){
43255 var k = e.getKey();
43257 var c = e.getCharCode();
43260 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43261 allowed.indexOf(String.fromCharCode(c)) === -1
43267 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43271 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43276 this.inputEl().on("keypress", keyPress, this);
43280 onTriggerClick : function(e)
43287 this.loadNext = false;
43289 if(this.isExpanded()){
43294 this.hasFocus = true;
43296 if(this.triggerAction == 'all') {
43297 this.doQuery(this.allQuery, true);
43301 this.doQuery(this.getRawValue());
43304 getCurrency : function()
43306 var v = this.currencyEl().getValue();
43311 restrictHeight : function()
43313 this.list.alignTo(this.currencyEl(), this.listAlign);
43314 this.list.alignTo(this.currencyEl(), this.listAlign);
43317 onViewClick : function(view, doFocus, el, e)
43319 var index = this.view.getSelectedIndexes()[0];
43321 var r = this.store.getAt(index);
43324 this.onSelect(r, index);
43328 onSelect : function(record, index){
43330 if(this.fireEvent('beforeselect', this, record, index) !== false){
43332 this.setFromCurrencyData(index > -1 ? record.data : false);
43336 this.fireEvent('select', this, record, index);
43340 setFromCurrencyData : function(o)
43344 this.lastCurrency = o;
43346 if (this.currencyField) {
43347 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43349 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43352 this.lastSelectionText = currency;
43354 //setting default currency
43355 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43356 this.setCurrency(this.defaultCurrency);
43360 this.setCurrency(currency);
43363 setFromData : function(o)
43367 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43369 this.setFromCurrencyData(c);
43374 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43376 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43379 this.setValue(value);
43383 setCurrency : function(v)
43385 this.currencyValue = v;
43388 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43393 setValue : function(v)
43395 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43401 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43403 this.inputEl().dom.value = (v == '') ? '' :
43404 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43406 if(!this.allowZero && v === '0') {
43407 this.hiddenEl().dom.value = '';
43408 this.inputEl().dom.value = '';
43415 getRawValue : function()
43417 var v = this.inputEl().getValue();
43422 getValue : function()
43424 return this.fixPrecision(this.parseValue(this.getRawValue()));
43427 parseValue : function(value)
43429 if(this.thousandsDelimiter) {
43431 r = new RegExp(",", "g");
43432 value = value.replace(r, "");
43435 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43436 return isNaN(value) ? '' : value;
43440 fixPrecision : function(value)
43442 if(this.thousandsDelimiter) {
43444 r = new RegExp(",", "g");
43445 value = value.replace(r, "");
43448 var nan = isNaN(value);
43450 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43451 return nan ? '' : value;
43453 return parseFloat(value).toFixed(this.decimalPrecision);
43456 decimalPrecisionFcn : function(v)
43458 return Math.floor(v);
43461 validateValue : function(value)
43463 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43467 var num = this.parseValue(value);
43470 this.markInvalid(String.format(this.nanText, value));
43474 if(num < this.minValue){
43475 this.markInvalid(String.format(this.minText, this.minValue));
43479 if(num > this.maxValue){
43480 this.markInvalid(String.format(this.maxText, this.maxValue));
43487 validate : function()
43489 if(this.disabled || this.allowBlank){
43494 var currency = this.getCurrency();
43496 if(this.validateValue(this.getRawValue()) && currency.length){
43501 this.markInvalid();
43505 getName: function()
43510 beforeBlur : function()
43516 var v = this.parseValue(this.getRawValue());
43523 onBlur : function()
43527 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43528 //this.el.removeClass(this.focusClass);
43531 this.hasFocus = false;
43533 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43537 var v = this.getValue();
43539 if(String(v) !== String(this.startValue)){
43540 this.fireEvent('change', this, v, this.startValue);
43543 this.fireEvent("blur", this);
43546 inputEl : function()
43548 return this.el.select('.roo-money-amount-input', true).first();
43551 currencyEl : function()
43553 return this.el.select('.roo-money-currency-input', true).first();
43556 hiddenEl : function()
43558 return this.el.select('input.hidden-number-input',true).first();
43562 * @class Roo.bootstrap.BezierSignature
43563 * @extends Roo.bootstrap.Component
43564 * Bootstrap BezierSignature class
43565 * This script refer to:
43566 * Title: Signature Pad
43568 * Availability: https://github.com/szimek/signature_pad
43571 * Create a new BezierSignature
43572 * @param {Object} config The config object
43575 Roo.bootstrap.BezierSignature = function(config){
43576 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43582 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43589 mouse_btn_down: true,
43592 * @cfg {int} canvas height
43594 canvas_height: '200px',
43597 * @cfg {float|function} Radius of a single dot.
43602 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43607 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43612 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43617 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43622 * @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.
43624 bg_color: 'rgba(0, 0, 0, 0)',
43627 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43629 dot_color: 'black',
43632 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43634 velocity_filter_weight: 0.7,
43637 * @cfg {function} Callback when stroke begin.
43642 * @cfg {function} Callback when stroke end.
43646 getAutoCreate : function()
43648 var cls = 'roo-signature column';
43651 cls += ' ' + this.cls;
43661 for(var i = 0; i < col_sizes.length; i++) {
43662 if(this[col_sizes[i]]) {
43663 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43673 cls: 'roo-signature-body',
43677 cls: 'roo-signature-body-canvas',
43678 height: this.canvas_height,
43679 width: this.canvas_width
43686 style: 'display: none'
43694 initEvents: function()
43696 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43698 var canvas = this.canvasEl();
43700 // mouse && touch event swapping...
43701 canvas.dom.style.touchAction = 'none';
43702 canvas.dom.style.msTouchAction = 'none';
43704 this.mouse_btn_down = false;
43705 canvas.on('mousedown', this._handleMouseDown, this);
43706 canvas.on('mousemove', this._handleMouseMove, this);
43707 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43709 if (window.PointerEvent) {
43710 canvas.on('pointerdown', this._handleMouseDown, this);
43711 canvas.on('pointermove', this._handleMouseMove, this);
43712 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43715 if ('ontouchstart' in window) {
43716 canvas.on('touchstart', this._handleTouchStart, this);
43717 canvas.on('touchmove', this._handleTouchMove, this);
43718 canvas.on('touchend', this._handleTouchEnd, this);
43721 Roo.EventManager.onWindowResize(this.resize, this, true);
43723 // file input event
43724 this.fileEl().on('change', this.uploadImage, this);
43731 resize: function(){
43733 var canvas = this.canvasEl().dom;
43734 var ctx = this.canvasElCtx();
43735 var img_data = false;
43737 if(canvas.width > 0) {
43738 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43740 // setting canvas width will clean img data
43743 var style = window.getComputedStyle ?
43744 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43746 var padding_left = parseInt(style.paddingLeft) || 0;
43747 var padding_right = parseInt(style.paddingRight) || 0;
43749 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43752 ctx.putImageData(img_data, 0, 0);
43756 _handleMouseDown: function(e)
43758 if (e.browserEvent.which === 1) {
43759 this.mouse_btn_down = true;
43760 this.strokeBegin(e);
43764 _handleMouseMove: function (e)
43766 if (this.mouse_btn_down) {
43767 this.strokeMoveUpdate(e);
43771 _handleMouseUp: function (e)
43773 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43774 this.mouse_btn_down = false;
43779 _handleTouchStart: function (e) {
43781 e.preventDefault();
43782 if (e.browserEvent.targetTouches.length === 1) {
43783 // var touch = e.browserEvent.changedTouches[0];
43784 // this.strokeBegin(touch);
43786 this.strokeBegin(e); // assume e catching the correct xy...
43790 _handleTouchMove: function (e) {
43791 e.preventDefault();
43792 // var touch = event.targetTouches[0];
43793 // _this._strokeMoveUpdate(touch);
43794 this.strokeMoveUpdate(e);
43797 _handleTouchEnd: function (e) {
43798 var wasCanvasTouched = e.target === this.canvasEl().dom;
43799 if (wasCanvasTouched) {
43800 e.preventDefault();
43801 // var touch = event.changedTouches[0];
43802 // _this._strokeEnd(touch);
43807 reset: function () {
43808 this._lastPoints = [];
43809 this._lastVelocity = 0;
43810 this._lastWidth = (this.min_width + this.max_width) / 2;
43811 this.canvasElCtx().fillStyle = this.dot_color;
43814 strokeMoveUpdate: function(e)
43816 this.strokeUpdate(e);
43818 if (this.throttle) {
43819 this.throttleStroke(this.strokeUpdate, this.throttle);
43822 this.strokeUpdate(e);
43826 strokeBegin: function(e)
43828 var newPointGroup = {
43829 color: this.dot_color,
43833 if (typeof this.onBegin === 'function') {
43837 this.curve_data.push(newPointGroup);
43839 this.strokeUpdate(e);
43842 strokeUpdate: function(e)
43844 var rect = this.canvasEl().dom.getBoundingClientRect();
43845 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43846 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43847 var lastPoints = lastPointGroup.points;
43848 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43849 var isLastPointTooClose = lastPoint
43850 ? point.distanceTo(lastPoint) <= this.min_distance
43852 var color = lastPointGroup.color;
43853 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43854 var curve = this.addPoint(point);
43856 this.drawDot({color: color, point: point});
43859 this.drawCurve({color: color, curve: curve});
43869 strokeEnd: function(e)
43871 this.strokeUpdate(e);
43872 if (typeof this.onEnd === 'function') {
43877 addPoint: function (point) {
43878 var _lastPoints = this._lastPoints;
43879 _lastPoints.push(point);
43880 if (_lastPoints.length > 2) {
43881 if (_lastPoints.length === 3) {
43882 _lastPoints.unshift(_lastPoints[0]);
43884 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43885 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43886 _lastPoints.shift();
43892 calculateCurveWidths: function (startPoint, endPoint) {
43893 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43894 (1 - this.velocity_filter_weight) * this._lastVelocity;
43896 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43899 start: this._lastWidth
43902 this._lastVelocity = velocity;
43903 this._lastWidth = newWidth;
43907 drawDot: function (_a) {
43908 var color = _a.color, point = _a.point;
43909 var ctx = this.canvasElCtx();
43910 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43912 this.drawCurveSegment(point.x, point.y, width);
43914 ctx.fillStyle = color;
43918 drawCurve: function (_a) {
43919 var color = _a.color, curve = _a.curve;
43920 var ctx = this.canvasElCtx();
43921 var widthDelta = curve.endWidth - curve.startWidth;
43922 var drawSteps = Math.floor(curve.length()) * 2;
43924 ctx.fillStyle = color;
43925 for (var i = 0; i < drawSteps; i += 1) {
43926 var t = i / drawSteps;
43932 var x = uuu * curve.startPoint.x;
43933 x += 3 * uu * t * curve.control1.x;
43934 x += 3 * u * tt * curve.control2.x;
43935 x += ttt * curve.endPoint.x;
43936 var y = uuu * curve.startPoint.y;
43937 y += 3 * uu * t * curve.control1.y;
43938 y += 3 * u * tt * curve.control2.y;
43939 y += ttt * curve.endPoint.y;
43940 var width = curve.startWidth + ttt * widthDelta;
43941 this.drawCurveSegment(x, y, width);
43947 drawCurveSegment: function (x, y, width) {
43948 var ctx = this.canvasElCtx();
43950 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43951 this.is_empty = false;
43956 var ctx = this.canvasElCtx();
43957 var canvas = this.canvasEl().dom;
43958 ctx.fillStyle = this.bg_color;
43959 ctx.clearRect(0, 0, canvas.width, canvas.height);
43960 ctx.fillRect(0, 0, canvas.width, canvas.height);
43961 this.curve_data = [];
43963 this.is_empty = true;
43968 return this.el.select('input',true).first();
43971 canvasEl: function()
43973 return this.el.select('canvas',true).first();
43976 canvasElCtx: function()
43978 return this.el.select('canvas',true).first().dom.getContext('2d');
43981 getImage: function(type)
43983 if(this.is_empty) {
43988 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43991 drawFromImage: function(img_src)
43993 var img = new Image();
43995 img.onload = function(){
43996 this.canvasElCtx().drawImage(img, 0, 0);
44001 this.is_empty = false;
44004 selectImage: function()
44006 this.fileEl().dom.click();
44009 uploadImage: function(e)
44011 var reader = new FileReader();
44013 reader.onload = function(e){
44014 var img = new Image();
44015 img.onload = function(){
44017 this.canvasElCtx().drawImage(img, 0, 0);
44019 img.src = e.target.result;
44022 reader.readAsDataURL(e.target.files[0]);
44025 // Bezier Point Constructor
44026 Point: (function () {
44027 function Point(x, y, time) {
44030 this.time = time || Date.now();
44032 Point.prototype.distanceTo = function (start) {
44033 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44035 Point.prototype.equals = function (other) {
44036 return this.x === other.x && this.y === other.y && this.time === other.time;
44038 Point.prototype.velocityFrom = function (start) {
44039 return this.time !== start.time
44040 ? this.distanceTo(start) / (this.time - start.time)
44047 // Bezier Constructor
44048 Bezier: (function () {
44049 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44050 this.startPoint = startPoint;
44051 this.control2 = control2;
44052 this.control1 = control1;
44053 this.endPoint = endPoint;
44054 this.startWidth = startWidth;
44055 this.endWidth = endWidth;
44057 Bezier.fromPoints = function (points, widths, scope) {
44058 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44059 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44060 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44062 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44063 var dx1 = s1.x - s2.x;
44064 var dy1 = s1.y - s2.y;
44065 var dx2 = s2.x - s3.x;
44066 var dy2 = s2.y - s3.y;
44067 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44068 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44069 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44070 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44071 var dxm = m1.x - m2.x;
44072 var dym = m1.y - m2.y;
44073 var k = l2 / (l1 + l2);
44074 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44075 var tx = s2.x - cm.x;
44076 var ty = s2.y - cm.y;
44078 c1: new scope.Point(m1.x + tx, m1.y + ty),
44079 c2: new scope.Point(m2.x + tx, m2.y + ty)
44082 Bezier.prototype.length = function () {
44087 for (var i = 0; i <= steps; i += 1) {
44089 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44090 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44092 var xdiff = cx - px;
44093 var ydiff = cy - py;
44094 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44101 Bezier.prototype.point = function (t, start, c1, c2, end) {
44102 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44103 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44104 + (3.0 * c2 * (1.0 - t) * t * t)
44105 + (end * t * t * t);
44110 throttleStroke: function(fn, wait) {
44111 if (wait === void 0) { wait = 250; }
44113 var timeout = null;
44117 var later = function () {
44118 previous = Date.now();
44120 result = fn.apply(storedContext, storedArgs);
44122 storedContext = null;
44126 return function wrapper() {
44128 for (var _i = 0; _i < arguments.length; _i++) {
44129 args[_i] = arguments[_i];
44131 var now = Date.now();
44132 var remaining = wait - (now - previous);
44133 storedContext = this;
44135 if (remaining <= 0 || remaining > wait) {
44137 clearTimeout(timeout);
44141 result = fn.apply(storedContext, storedArgs);
44143 storedContext = null;
44147 else if (!timeout) {
44148 timeout = window.setTimeout(later, remaining);