2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2549 // remove Card from items.
2552 if (this.items.length) {
2554 //Roo.log([info.items_n, info.position, this.items.length]);
2555 for (var i =0; i < this.items.length; i++) {
2556 if (i == to_items_n && position == 'above') {
2557 nitems.push(move_card);
2559 nitems.push(this.items[i]);
2560 if (i == to_items_n && position == 'below') {
2561 nitems.push(move_card);
2564 this.items = nitems;
2565 Roo.log(this.items);
2567 this.items.push(move_card);
2570 move_card.parentId = this.id;
2576 removeCard : function(c)
2578 this.items = this.items.filter(function(e) { return e != c });
2581 dom.parentNode.removeChild(dom);
2582 dom.style.width = ''; // clear with - which is set by drag.
2587 /** Decide whether to drop above or below a View node. */
2588 getDropPoint : function(e, n, dd)
2593 if (n == this.containerEl.dom) {
2596 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2597 var c = t + (b - t) / 2;
2598 var y = Roo.lib.Event.getPageY(e);
2605 onToggleCollapse : function(e)
2607 if (this.collapsed) {
2608 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2609 this.collapsableEl.addClass('show');
2610 this.collapsed = false;
2613 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2614 this.collapsableEl.removeClass('show');
2615 this.collapsed = true;
2620 onToggleRotate : function(e)
2622 this.collapsableEl.removeClass('show');
2623 this.footerEl.removeClass('d-none');
2624 this.el.removeClass('roo-card-rotated');
2625 this.el.removeClass('d-none');
2628 this.collapsableEl.addClass('show');
2629 this.rotated = false;
2630 this.fireEvent('rotate', this, this.rotated);
2633 this.el.addClass('roo-card-rotated');
2634 this.footerEl.addClass('d-none');
2635 this.el.select('.roo-collapsable').removeClass('show');
2637 this.rotated = true;
2638 this.fireEvent('rotate', this, this.rotated);
2642 dropPlaceHolder: function (action, info, data)
2644 if (this.dropEl === false) {
2645 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2649 this.dropEl.removeClass(['d-none', 'd-block']);
2650 if (action == 'hide') {
2652 this.dropEl.addClass('d-none');
2655 // FIXME - info.card == true!!!
2656 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2658 if (info.card !== true) {
2659 var cardel = info.card.el.dom;
2661 if (info.position == 'above') {
2662 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2663 } else if (cardel.nextSibling) {
2664 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2666 cardel.parentNode.append(this.dropEl.dom);
2669 // card container???
2670 this.containerEl.dom.append(this.dropEl.dom);
2673 this.dropEl.addClass('d-block roo-card-dropzone');
2675 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2682 setHeaderText: function(html)
2684 this.headerContainerEl.dom.innerHTML = html;
2693 * Card header - holder for the card header elements.
2698 * @class Roo.bootstrap.CardHeader
2699 * @extends Roo.bootstrap.Element
2700 * Bootstrap CardHeader class
2702 * Create a new Card Header - that you can embed children into
2703 * @param {Object} config The config object
2706 Roo.bootstrap.CardHeader = function(config){
2707 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2710 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2713 container_method : 'getCardHeader'
2726 * Card footer - holder for the card footer elements.
2731 * @class Roo.bootstrap.CardFooter
2732 * @extends Roo.bootstrap.Element
2733 * Bootstrap CardFooter class
2735 * Create a new Card Footer - that you can embed children into
2736 * @param {Object} config The config object
2739 Roo.bootstrap.CardFooter = function(config){
2740 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2743 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2746 container_method : 'getCardFooter'
2759 * Card header - holder for the card header elements.
2764 * @class Roo.bootstrap.CardImageTop
2765 * @extends Roo.bootstrap.Element
2766 * Bootstrap CardImageTop class
2768 * Create a new Card Image Top container
2769 * @param {Object} config The config object
2772 Roo.bootstrap.CardImageTop = function(config){
2773 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2776 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2779 container_method : 'getCardImageTop'
2797 * @class Roo.bootstrap.Img
2798 * @extends Roo.bootstrap.Component
2799 * Bootstrap Img class
2800 * @cfg {Boolean} imgResponsive false | true
2801 * @cfg {String} border rounded | circle | thumbnail
2802 * @cfg {String} src image source
2803 * @cfg {String} alt image alternative text
2804 * @cfg {String} href a tag href
2805 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2806 * @cfg {String} xsUrl xs image source
2807 * @cfg {String} smUrl sm image source
2808 * @cfg {String} mdUrl md image source
2809 * @cfg {String} lgUrl lg image source
2812 * Create a new Input
2813 * @param {Object} config The config object
2816 Roo.bootstrap.Img = function(config){
2817 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2823 * The img click event for the img.
2824 * @param {Roo.EventObject} e
2830 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2832 imgResponsive: true,
2842 getAutoCreate : function()
2844 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2845 return this.createSingleImg();
2850 cls: 'roo-image-responsive-group',
2855 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857 if(!_this[size + 'Url']){
2863 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2864 html: _this.html || cfg.html,
2865 src: _this[size + 'Url']
2868 img.cls += ' roo-image-responsive-' + size;
2870 var s = ['xs', 'sm', 'md', 'lg'];
2872 s.splice(s.indexOf(size), 1);
2874 Roo.each(s, function(ss){
2875 img.cls += ' hidden-' + ss;
2878 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2879 cfg.cls += ' img-' + _this.border;
2883 cfg.alt = _this.alt;
2896 a.target = _this.target;
2900 cfg.cn.push((_this.href) ? a : img);
2907 createSingleImg : function()
2911 cls: (this.imgResponsive) ? 'img-responsive' : '',
2913 src : 'about:blank' // just incase src get's set to undefined?!?
2916 cfg.html = this.html || cfg.html;
2918 cfg.src = this.src || cfg.src;
2920 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2921 cfg.cls += ' img-' + this.border;
2938 a.target = this.target;
2943 return (this.href) ? a : cfg;
2946 initEvents: function()
2949 this.el.on('click', this.onClick, this);
2954 onClick : function(e)
2956 Roo.log('img onclick');
2957 this.fireEvent('click', this, e);
2960 * Sets the url of the image - used to update it
2961 * @param {String} url the url of the image
2964 setSrc : function(url)
2968 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2969 this.el.dom.src = url;
2973 this.el.select('img', true).first().dom.src = url;
2989 * @class Roo.bootstrap.Link
2990 * @extends Roo.bootstrap.Component
2991 * Bootstrap Link Class
2992 * @cfg {String} alt image alternative text
2993 * @cfg {String} href a tag href
2994 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2995 * @cfg {String} html the content of the link.
2996 * @cfg {String} anchor name for the anchor link
2997 * @cfg {String} fa - favicon
2999 * @cfg {Boolean} preventDefault (true | false) default false
3003 * Create a new Input
3004 * @param {Object} config The config object
3007 Roo.bootstrap.Link = function(config){
3008 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3014 * The img click event for the img.
3015 * @param {Roo.EventObject} e
3021 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3025 preventDefault: false,
3031 getAutoCreate : function()
3033 var html = this.html || '';
3035 if (this.fa !== false) {
3036 html = '<i class="fa fa-' + this.fa + '"></i>';
3041 // anchor's do not require html/href...
3042 if (this.anchor === false) {
3044 cfg.href = this.href || '#';
3046 cfg.name = this.anchor;
3047 if (this.html !== false || this.fa !== false) {
3050 if (this.href !== false) {
3051 cfg.href = this.href;
3055 if(this.alt !== false){
3060 if(this.target !== false) {
3061 cfg.target = this.target;
3067 initEvents: function() {
3069 if(!this.href || this.preventDefault){
3070 this.el.on('click', this.onClick, this);
3074 onClick : function(e)
3076 if(this.preventDefault){
3079 //Roo.log('img onclick');
3080 this.fireEvent('click', this, e);
3093 * @class Roo.bootstrap.Header
3094 * @extends Roo.bootstrap.Component
3095 * Bootstrap Header class
3096 * @cfg {String} html content of header
3097 * @cfg {Number} level (1|2|3|4|5|6) default 1
3100 * Create a new Header
3101 * @param {Object} config The config object
3105 Roo.bootstrap.Header = function(config){
3106 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3109 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3117 getAutoCreate : function(){
3122 tag: 'h' + (1 *this.level),
3123 html: this.html || ''
3135 * Ext JS Library 1.1.1
3136 * Copyright(c) 2006-2007, Ext JS, LLC.
3138 * Originally Released Under LGPL - original licence link has changed is not relivant.
3141 * <script type="text/javascript">
3145 * @class Roo.bootstrap.MenuMgr
3146 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3149 Roo.bootstrap.MenuMgr = function(){
3150 var menus, active, groups = {}, attached = false, lastShow = new Date();
3152 // private - called when first menu is created
3155 active = new Roo.util.MixedCollection();
3156 Roo.get(document).addKeyListener(27, function(){
3157 if(active.length > 0){
3165 if(active && active.length > 0){
3166 var c = active.clone();
3176 if(active.length < 1){
3177 Roo.get(document).un("mouseup", onMouseDown);
3185 var last = active.last();
3186 lastShow = new Date();
3189 Roo.get(document).on("mouseup", onMouseDown);
3194 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3195 m.parentMenu.activeChild = m;
3196 }else if(last && last.isVisible()){
3197 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3202 function onBeforeHide(m){
3204 m.activeChild.hide();
3206 if(m.autoHideTimer){
3207 clearTimeout(m.autoHideTimer);
3208 delete m.autoHideTimer;
3213 function onBeforeShow(m){
3214 var pm = m.parentMenu;
3215 if(!pm && !m.allowOtherMenus){
3217 }else if(pm && pm.activeChild && active != m){
3218 pm.activeChild.hide();
3222 // private this should really trigger on mouseup..
3223 function onMouseDown(e){
3224 Roo.log("on Mouse Up");
3226 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3227 Roo.log("MenuManager hideAll");
3236 function onBeforeCheck(mi, state){
3238 var g = groups[mi.group];
3239 for(var i = 0, l = g.length; i < l; i++){
3241 g[i].setChecked(false);
3250 * Hides all menus that are currently visible
3252 hideAll : function(){
3257 register : function(menu){
3261 menus[menu.id] = menu;
3262 menu.on("beforehide", onBeforeHide);
3263 menu.on("hide", onHide);
3264 menu.on("beforeshow", onBeforeShow);
3265 menu.on("show", onShow);
3267 if(g && menu.events["checkchange"]){
3271 groups[g].push(menu);
3272 menu.on("checkchange", onCheck);
3277 * Returns a {@link Roo.menu.Menu} object
3278 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3279 * be used to generate and return a new Menu instance.
3281 get : function(menu){
3282 if(typeof menu == "string"){ // menu id
3284 }else if(menu.events){ // menu instance
3287 /*else if(typeof menu.length == 'number'){ // array of menu items?
3288 return new Roo.bootstrap.Menu({items:menu});
3289 }else{ // otherwise, must be a config
3290 return new Roo.bootstrap.Menu(menu);
3297 unregister : function(menu){
3298 delete menus[menu.id];
3299 menu.un("beforehide", onBeforeHide);
3300 menu.un("hide", onHide);
3301 menu.un("beforeshow", onBeforeShow);
3302 menu.un("show", onShow);
3304 if(g && menu.events["checkchange"]){
3305 groups[g].remove(menu);
3306 menu.un("checkchange", onCheck);
3311 registerCheckable : function(menuItem){
3312 var g = menuItem.group;
3317 groups[g].push(menuItem);
3318 menuItem.on("beforecheckchange", onBeforeCheck);
3323 unregisterCheckable : function(menuItem){
3324 var g = menuItem.group;
3326 groups[g].remove(menuItem);
3327 menuItem.un("beforecheckchange", onBeforeCheck);
3339 * @class Roo.bootstrap.Menu
3340 * @extends Roo.bootstrap.Component
3341 * Bootstrap Menu class - container for MenuItems
3342 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3343 * @cfg {bool} hidden if the menu should be hidden when rendered.
3344 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3345 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3349 * @param {Object} config The config object
3353 Roo.bootstrap.Menu = function(config){
3354 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3355 if (this.registerMenu && this.type != 'treeview') {
3356 Roo.bootstrap.MenuMgr.register(this);
3363 * Fires before this menu is displayed (return false to block)
3364 * @param {Roo.menu.Menu} this
3369 * Fires before this menu is hidden (return false to block)
3370 * @param {Roo.menu.Menu} this
3375 * Fires after this menu is displayed
3376 * @param {Roo.menu.Menu} this
3381 * Fires after this menu is hidden
3382 * @param {Roo.menu.Menu} this
3387 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3388 * @param {Roo.menu.Menu} this
3389 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3390 * @param {Roo.EventObject} e
3395 * Fires when the mouse is hovering over this menu
3396 * @param {Roo.menu.Menu} this
3397 * @param {Roo.EventObject} e
3398 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3403 * Fires when the mouse exits this menu
3404 * @param {Roo.menu.Menu} this
3405 * @param {Roo.EventObject} e
3406 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3411 * Fires when a menu item contained in this menu is clicked
3412 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3413 * @param {Roo.EventObject} e
3417 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3420 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3424 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3427 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429 registerMenu : true,
3431 menuItems :false, // stores the menu items..
3441 getChildContainer : function() {
3445 getAutoCreate : function(){
3447 //if (['right'].indexOf(this.align)!==-1) {
3448 // cfg.cn[1].cls += ' pull-right'
3454 cls : 'dropdown-menu' ,
3455 style : 'z-index:1000'
3459 if (this.type === 'submenu') {
3460 cfg.cls = 'submenu active';
3462 if (this.type === 'treeview') {
3463 cfg.cls = 'treeview-menu';
3468 initEvents : function() {
3470 // Roo.log("ADD event");
3471 // Roo.log(this.triggerEl.dom);
3473 this.triggerEl.on('click', this.onTriggerClick, this);
3475 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3478 if (this.triggerEl.hasClass('nav-item')) {
3479 // dropdown toggle on the 'a' in BS4?
3480 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482 this.triggerEl.addClass('dropdown-toggle');
3485 this.el.on('touchstart' , this.onTouch, this);
3487 this.el.on('click' , this.onClick, this);
3489 this.el.on("mouseover", this.onMouseOver, this);
3490 this.el.on("mouseout", this.onMouseOut, this);
3494 findTargetItem : function(e)
3496 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3500 //Roo.log(t); Roo.log(t.id);
3502 //Roo.log(this.menuitems);
3503 return this.menuitems.get(t.id);
3505 //return this.items.get(t.menuItemId);
3511 onTouch : function(e)
3513 Roo.log("menu.onTouch");
3514 //e.stopEvent(); this make the user popdown broken
3518 onClick : function(e)
3520 Roo.log("menu.onClick");
3522 var t = this.findTargetItem(e);
3523 if(!t || t.isContainer){
3528 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3529 if(t == this.activeItem && t.shouldDeactivate(e)){
3530 this.activeItem.deactivate();
3531 delete this.activeItem;
3535 this.setActiveItem(t, true);
3543 Roo.log('pass click event');
3547 this.fireEvent("click", this, t, e);
3551 if(!t.href.length || t.href == '#'){
3552 (function() { _this.hide(); }).defer(100);
3557 onMouseOver : function(e){
3558 var t = this.findTargetItem(e);
3561 // if(t.canActivate && !t.disabled){
3562 // this.setActiveItem(t, true);
3566 this.fireEvent("mouseover", this, e, t);
3568 isVisible : function(){
3569 return !this.hidden;
3571 onMouseOut : function(e){
3572 var t = this.findTargetItem(e);
3575 // if(t == this.activeItem && t.shouldDeactivate(e)){
3576 // this.activeItem.deactivate();
3577 // delete this.activeItem;
3580 this.fireEvent("mouseout", this, e, t);
3585 * Displays this menu relative to another element
3586 * @param {String/HTMLElement/Roo.Element} element The element to align to
3587 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3588 * the element (defaults to this.defaultAlign)
3589 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591 show : function(el, pos, parentMenu)
3593 if (false === this.fireEvent("beforeshow", this)) {
3594 Roo.log("show canceled");
3597 this.parentMenu = parentMenu;
3602 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3605 * Displays this menu at a specific xy position
3606 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3607 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609 showAt : function(xy, parentMenu, /* private: */_e){
3610 this.parentMenu = parentMenu;
3615 this.fireEvent("beforeshow", this);
3616 //xy = this.el.adjustForConstraints(xy);
3620 this.hideMenuItems();
3621 this.hidden = false;
3622 this.triggerEl.addClass('open');
3623 this.el.addClass('show');
3625 // reassign x when hitting right
3626 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3627 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3630 // reassign y when hitting bottom
3631 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3632 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3635 // but the list may align on trigger left or trigger top... should it be a properity?
3637 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3642 this.fireEvent("show", this);
3648 this.doFocus.defer(50, this);
3652 doFocus : function(){
3654 this.focusEl.focus();
3659 * Hides this menu and optionally all parent menus
3660 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662 hide : function(deep)
3664 if (false === this.fireEvent("beforehide", this)) {
3665 Roo.log("hide canceled");
3668 this.hideMenuItems();
3669 if(this.el && this.isVisible()){
3671 if(this.activeItem){
3672 this.activeItem.deactivate();
3673 this.activeItem = null;
3675 this.triggerEl.removeClass('open');;
3676 this.el.removeClass('show');
3678 this.fireEvent("hide", this);
3680 if(deep === true && this.parentMenu){
3681 this.parentMenu.hide(true);
3685 onTriggerClick : function(e)
3687 Roo.log('trigger click');
3689 var target = e.getTarget();
3691 Roo.log(target.nodeName.toLowerCase());
3693 if(target.nodeName.toLowerCase() === 'i'){
3699 onTriggerPress : function(e)
3701 Roo.log('trigger press');
3702 //Roo.log(e.getTarget());
3703 // Roo.log(this.triggerEl.dom);
3705 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3706 var pel = Roo.get(e.getTarget());
3707 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3708 Roo.log('is treeview or dropdown?');
3712 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3716 if (this.isVisible()) {
3721 this.show(this.triggerEl, '?', false);
3724 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3731 hideMenuItems : function()
3733 Roo.log("hide Menu Items");
3738 this.el.select('.open',true).each(function(aa) {
3740 aa.removeClass('open');
3744 addxtypeChild : function (tree, cntr) {
3745 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747 this.menuitems.add(comp);
3759 this.getEl().dom.innerHTML = '';
3760 this.menuitems.clear();
3774 * @class Roo.bootstrap.MenuItem
3775 * @extends Roo.bootstrap.Component
3776 * Bootstrap MenuItem class
3777 * @cfg {String} html the menu label
3778 * @cfg {String} href the link
3779 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3780 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3781 * @cfg {Boolean} active used on sidebars to highlight active itesm
3782 * @cfg {String} fa favicon to show on left of menu item.
3783 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3787 * Create a new MenuItem
3788 * @param {Object} config The config object
3792 Roo.bootstrap.MenuItem = function(config){
3793 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3798 * The raw click event for the entire grid.
3799 * @param {Roo.bootstrap.MenuItem} this
3800 * @param {Roo.EventObject} e
3806 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3810 preventDefault: false,
3811 isContainer : false,
3815 getAutoCreate : function(){
3817 if(this.isContainer){
3820 cls: 'dropdown-menu-item '
3830 cls : 'dropdown-item',
3835 if (this.fa !== false) {
3838 cls : 'fa fa-' + this.fa
3847 cls: 'dropdown-menu-item',
3850 if (this.parent().type == 'treeview') {
3851 cfg.cls = 'treeview-menu';
3854 cfg.cls += ' active';
3859 anc.href = this.href || cfg.cn[0].href ;
3860 ctag.html = this.html || cfg.cn[0].html ;
3864 initEvents: function()
3866 if (this.parent().type == 'treeview') {
3867 this.el.select('a').on('click', this.onClick, this);
3871 this.menu.parentType = this.xtype;
3872 this.menu.triggerEl = this.el;
3873 this.menu = this.addxtype(Roo.apply({}, this.menu));
3877 onClick : function(e)
3879 Roo.log('item on click ');
3881 if(this.preventDefault){
3884 //this.parent().hideMenuItems();
3886 this.fireEvent('click', this, e);
3905 * @class Roo.bootstrap.MenuSeparator
3906 * @extends Roo.bootstrap.Component
3907 * Bootstrap MenuSeparator class
3910 * Create a new MenuItem
3911 * @param {Object} config The config object
3915 Roo.bootstrap.MenuSeparator = function(config){
3916 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3919 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3921 getAutoCreate : function(){
3940 * @class Roo.bootstrap.Modal
3941 * @extends Roo.bootstrap.Component
3942 * Bootstrap Modal class
3943 * @cfg {String} title Title of dialog
3944 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3945 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3946 * @cfg {Boolean} specificTitle default false
3947 * @cfg {Array} buttons Array of buttons or standard button set..
3948 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3949 * @cfg {Boolean} animate default true
3950 * @cfg {Boolean} allow_close default true
3951 * @cfg {Boolean} fitwindow default false
3952 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3953 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3954 * @cfg {String} size (sm|lg|xl) default empty
3955 * @cfg {Number} max_width set the max width of modal
3956 * @cfg {Boolean} editableTitle can the title be edited
3961 * Create a new Modal Dialog
3962 * @param {Object} config The config object
3965 Roo.bootstrap.Modal = function(config){
3966 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3971 * The raw btnclick event for the button
3972 * @param {Roo.EventObject} e
3977 * Fire when dialog resize
3978 * @param {Roo.bootstrap.Modal} this
3979 * @param {Roo.EventObject} e
3983 * @event titlechanged
3984 * Fire when the editable title has been changed
3985 * @param {Roo.bootstrap.Modal} this
3986 * @param {Roo.EventObject} value
3988 "titlechanged" : true
3991 this.buttons = this.buttons || [];
3994 this.tmpl = Roo.factory(this.tmpl);
3999 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4001 title : 'test dialog',
4011 specificTitle: false,
4013 buttonPosition: 'right',
4035 editableTitle : false,
4037 onRender : function(ct, position)
4039 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042 var cfg = Roo.apply({}, this.getAutoCreate());
4045 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4047 //if (!cfg.name.length) {
4051 cfg.cls += ' ' + this.cls;
4054 cfg.style = this.style;
4056 this.el = Roo.get(document.body).createChild(cfg, position);
4058 //var type = this.el.dom.type;
4061 if(this.tabIndex !== undefined){
4062 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065 this.dialogEl = this.el.select('.modal-dialog',true).first();
4066 this.bodyEl = this.el.select('.modal-body',true).first();
4067 this.closeEl = this.el.select('.modal-header .close', true).first();
4068 this.headerEl = this.el.select('.modal-header',true).first();
4069 this.titleEl = this.el.select('.modal-title',true).first();
4070 this.footerEl = this.el.select('.modal-footer',true).first();
4072 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4074 //this.el.addClass("x-dlg-modal");
4076 if (this.buttons.length) {
4077 Roo.each(this.buttons, function(bb) {
4078 var b = Roo.apply({}, bb);
4079 b.xns = b.xns || Roo.bootstrap;
4080 b.xtype = b.xtype || 'Button';
4081 if (typeof(b.listeners) == 'undefined') {
4082 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4085 var btn = Roo.factory(b);
4087 btn.render(this.getButtonContainer());
4091 // render the children.
4094 if(typeof(this.items) != 'undefined'){
4095 var items = this.items;
4098 for(var i =0;i < items.length;i++) {
4099 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4103 this.items = nitems;
4105 // where are these used - they used to be body/close/footer
4109 //this.el.addClass([this.fieldClass, this.cls]);
4113 getAutoCreate : function()
4115 // we will default to modal-body-overflow - might need to remove or make optional later.
4117 cls : 'modal-body enable-modal-body-overflow ',
4118 html : this.html || ''
4123 cls : 'modal-title',
4127 if(this.specificTitle){ // WTF is this?
4132 if (this.allow_close && Roo.bootstrap.version == 3) {
4142 if (this.editableTitle) {
4144 cls: 'form-control roo-editable-title d-none',
4150 if (this.allow_close && Roo.bootstrap.version == 4) {
4160 if(this.size.length){
4161 size = 'modal-' + this.size;
4164 var footer = Roo.bootstrap.version == 3 ?
4166 cls : 'modal-footer',
4170 cls: 'btn-' + this.buttonPosition
4175 { // BS4 uses mr-auto on left buttons....
4176 cls : 'modal-footer'
4187 cls: "modal-dialog " + size,
4190 cls : "modal-content",
4193 cls : 'modal-header',
4208 modal.cls += ' fade';
4214 getChildContainer : function() {
4219 getButtonContainer : function() {
4221 return Roo.bootstrap.version == 4 ?
4222 this.el.select('.modal-footer',true).first()
4223 : this.el.select('.modal-footer div',true).first();
4226 initEvents : function()
4228 if (this.allow_close) {
4229 this.closeEl.on('click', this.hide, this);
4231 Roo.EventManager.onWindowResize(this.resize, this, true);
4232 if (this.editableTitle) {
4233 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4234 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4235 this.headerEditEl.on('keyup', function(e) {
4236 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4237 this.toggleHeaderInput(false)
4240 this.headerEditEl.on('blur', function(e) {
4241 this.toggleHeaderInput(false)
4250 this.maskEl.setSize(
4251 Roo.lib.Dom.getViewWidth(true),
4252 Roo.lib.Dom.getViewHeight(true)
4255 if (this.fitwindow) {
4259 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4260 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4265 if(this.max_width !== 0) {
4267 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270 this.setSize(w, this.height);
4274 if(this.max_height) {
4275 this.setSize(w,Math.min(
4277 Roo.lib.Dom.getViewportHeight(true) - 60
4283 if(!this.fit_content) {
4284 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4288 this.setSize(w, Math.min(
4290 this.headerEl.getHeight() +
4291 this.footerEl.getHeight() +
4292 this.getChildHeight(this.bodyEl.dom.childNodes),
4293 Roo.lib.Dom.getViewportHeight(true) - 60)
4299 setSize : function(w,h)
4310 if (!this.rendered) {
4313 this.toggleHeaderInput(false);
4314 //this.el.setStyle('display', 'block');
4315 this.el.removeClass('hideing');
4316 this.el.dom.style.display='block';
4318 Roo.get(document.body).addClass('modal-open');
4320 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4323 this.el.addClass('show');
4324 this.el.addClass('in');
4327 this.el.addClass('show');
4328 this.el.addClass('in');
4331 // not sure how we can show data in here..
4333 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336 Roo.get(document.body).addClass("x-body-masked");
4338 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4339 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4340 this.maskEl.dom.style.display = 'block';
4341 this.maskEl.addClass('show');
4346 this.fireEvent('show', this);
4348 // set zindex here - otherwise it appears to be ignored...
4349 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352 this.items.forEach( function(e) {
4353 e.layout ? e.layout() : false;
4361 if(this.fireEvent("beforehide", this) !== false){
4363 this.maskEl.removeClass('show');
4365 this.maskEl.dom.style.display = '';
4366 Roo.get(document.body).removeClass("x-body-masked");
4367 this.el.removeClass('in');
4368 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4370 if(this.animate){ // why
4371 this.el.addClass('hideing');
4372 this.el.removeClass('show');
4374 if (!this.el.hasClass('hideing')) {
4375 return; // it's been shown again...
4378 this.el.dom.style.display='';
4380 Roo.get(document.body).removeClass('modal-open');
4381 this.el.removeClass('hideing');
4385 this.el.removeClass('show');
4386 this.el.dom.style.display='';
4387 Roo.get(document.body).removeClass('modal-open');
4390 this.fireEvent('hide', this);
4393 isVisible : function()
4396 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4400 addButton : function(str, cb)
4404 var b = Roo.apply({}, { html : str } );
4405 b.xns = b.xns || Roo.bootstrap;
4406 b.xtype = b.xtype || 'Button';
4407 if (typeof(b.listeners) == 'undefined') {
4408 b.listeners = { click : cb.createDelegate(this) };
4411 var btn = Roo.factory(b);
4413 btn.render(this.getButtonContainer());
4419 setDefaultButton : function(btn)
4421 //this.el.select('.modal-footer').()
4424 resizeTo: function(w,h)
4426 this.dialogEl.setWidth(w);
4428 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4430 this.bodyEl.setHeight(h - diff);
4432 this.fireEvent('resize', this);
4435 setContentSize : function(w, h)
4439 onButtonClick: function(btn,e)
4442 this.fireEvent('btnclick', btn.name, e);
4445 * Set the title of the Dialog
4446 * @param {String} str new Title
4448 setTitle: function(str) {
4449 this.titleEl.dom.innerHTML = str;
4453 * Set the body of the Dialog
4454 * @param {String} str new Title
4456 setBody: function(str) {
4457 this.bodyEl.dom.innerHTML = str;
4460 * Set the body of the Dialog using the template
4461 * @param {Obj} data - apply this data to the template and replace the body contents.
4463 applyBody: function(obj)
4466 Roo.log("Error - using apply Body without a template");
4469 this.tmpl.overwrite(this.bodyEl, obj);
4472 getChildHeight : function(child_nodes)
4476 child_nodes.length == 0
4481 var child_height = 0;
4483 for(var i = 0; i < child_nodes.length; i++) {
4486 * for modal with tabs...
4487 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4489 var layout_childs = child_nodes[i].childNodes;
4491 for(var j = 0; j < layout_childs.length; j++) {
4493 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4495 var layout_body_childs = layout_childs[j].childNodes;
4497 for(var k = 0; k < layout_body_childs.length; k++) {
4499 if(layout_body_childs[k].classList.contains('navbar')) {
4500 child_height += layout_body_childs[k].offsetHeight;
4504 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4506 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4508 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4510 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4511 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4526 child_height += child_nodes[i].offsetHeight;
4527 // Roo.log(child_nodes[i].offsetHeight);
4530 return child_height;
4532 toggleHeaderInput : function(is_edit)
4534 if (!this.editableTitle) {
4535 return; // not editable.
4537 if (is_edit && this.is_header_editing) {
4538 return; // already editing..
4542 this.headerEditEl.dom.value = this.title;
4543 this.headerEditEl.removeClass('d-none');
4544 this.headerEditEl.dom.focus();
4545 this.titleEl.addClass('d-none');
4547 this.is_header_editing = true;
4550 // flip back to not editing.
4551 this.title = this.headerEditEl.dom.value;
4552 this.headerEditEl.addClass('d-none');
4553 this.titleEl.removeClass('d-none');
4554 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4555 this.is_header_editing = false;
4556 this.fireEvent('titlechanged', this, this.title);
4565 Roo.apply(Roo.bootstrap.Modal, {
4567 * Button config that displays a single OK button
4576 * Button config that displays Yes and No buttons
4592 * Button config that displays OK and Cancel buttons
4607 * Button config that displays Yes, No and Cancel buttons
4632 * messagebox - can be used as a replace
4636 * @class Roo.MessageBox
4637 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4641 Roo.Msg.alert('Status', 'Changes saved successfully.');
4643 // Prompt for user data:
4644 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4646 // process text value...
4650 // Show a dialog using config options:
4652 title:'Save Changes?',
4653 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4654 buttons: Roo.Msg.YESNOCANCEL,
4661 Roo.bootstrap.MessageBox = function(){
4662 var dlg, opt, mask, waitTimer;
4663 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4664 var buttons, activeTextEl, bwidth;
4668 var handleButton = function(button){
4670 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4674 var handleHide = function(){
4676 dlg.el.removeClass(opt.cls);
4679 // Roo.TaskMgr.stop(waitTimer);
4680 // waitTimer = null;
4685 var updateButtons = function(b){
4688 buttons["ok"].hide();
4689 buttons["cancel"].hide();
4690 buttons["yes"].hide();
4691 buttons["no"].hide();
4692 dlg.footerEl.hide();
4696 dlg.footerEl.show();
4697 for(var k in buttons){
4698 if(typeof buttons[k] != "function"){
4701 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4702 width += buttons[k].el.getWidth()+15;
4712 var handleEsc = function(d, k, e){
4713 if(opt && opt.closable !== false){
4723 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4724 * @return {Roo.BasicDialog} The BasicDialog element
4726 getDialog : function(){
4728 dlg = new Roo.bootstrap.Modal( {
4731 //constraintoviewport:false,
4733 //collapsible : false,
4738 //buttonAlign:"center",
4739 closeClick : function(){
4740 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743 handleButton("cancel");
4748 dlg.on("hide", handleHide);
4750 //dlg.addKeyListener(27, handleEsc);
4752 this.buttons = buttons;
4753 var bt = this.buttonText;
4754 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4755 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4756 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4757 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4759 bodyEl = dlg.bodyEl.createChild({
4761 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4762 '<textarea class="roo-mb-textarea"></textarea>' +
4763 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4765 msgEl = bodyEl.dom.firstChild;
4766 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4767 textboxEl.enableDisplayMode();
4768 textboxEl.addKeyListener([10,13], function(){
4769 if(dlg.isVisible() && opt && opt.buttons){
4772 }else if(opt.buttons.yes){
4773 handleButton("yes");
4777 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4778 textareaEl.enableDisplayMode();
4779 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4780 progressEl.enableDisplayMode();
4782 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4783 var pf = progressEl.dom.firstChild;
4785 pp = Roo.get(pf.firstChild);
4786 pp.setHeight(pf.offsetHeight);
4794 * Updates the message box body text
4795 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4796 * the XHTML-compliant non-breaking space character '&#160;')
4797 * @return {Roo.MessageBox} This message box
4799 updateText : function(text)
4801 if(!dlg.isVisible() && !opt.width){
4802 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4803 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4805 msgEl.innerHTML = text || ' ';
4807 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4808 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4810 Math.min(opt.width || cw , this.maxWidth),
4811 Math.max(opt.minWidth || this.minWidth, bwidth)
4814 activeTextEl.setWidth(w);
4816 if(dlg.isVisible()){
4817 dlg.fixedcenter = false;
4819 // to big, make it scroll. = But as usual stupid IE does not support
4822 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4823 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4824 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4826 bodyEl.dom.style.height = '';
4827 bodyEl.dom.style.overflowY = '';
4830 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4832 bodyEl.dom.style.overflowX = '';
4835 dlg.setContentSize(w, bodyEl.getHeight());
4836 if(dlg.isVisible()){
4837 dlg.fixedcenter = true;
4843 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4844 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4845 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4846 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4847 * @return {Roo.MessageBox} This message box
4849 updateProgress : function(value, text){
4851 this.updateText(text);
4854 if (pp) { // weird bug on my firefox - for some reason this is not defined
4855 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4856 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4862 * Returns true if the message box is currently displayed
4863 * @return {Boolean} True if the message box is visible, else false
4865 isVisible : function(){
4866 return dlg && dlg.isVisible();
4870 * Hides the message box if it is displayed
4873 if(this.isVisible()){
4879 * Displays a new message box, or reinitializes an existing message box, based on the config options
4880 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4881 * The following config object properties are supported:
4883 Property Type Description
4884 ---------- --------------- ------------------------------------------------------------------------------------
4885 animEl String/Element An id or Element from which the message box should animate as it opens and
4886 closes (defaults to undefined)
4887 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4888 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4889 closable Boolean False to hide the top-right close button (defaults to true). Note that
4890 progress and wait dialogs will ignore this property and always hide the
4891 close button as they can only be closed programmatically.
4892 cls String A custom CSS class to apply to the message box element
4893 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4894 displayed (defaults to 75)
4895 fn Function A callback function to execute after closing the dialog. The arguments to the
4896 function will be btn (the name of the button that was clicked, if applicable,
4897 e.g. "ok"), and text (the value of the active text field, if applicable).
4898 Progress and wait dialogs will ignore this option since they do not respond to
4899 user actions and can only be closed programmatically, so any required function
4900 should be called by the same code after it closes the dialog.
4901 icon String A CSS class that provides a background image to be used as an icon for
4902 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4903 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4904 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4905 modal Boolean False to allow user interaction with the page while the message box is
4906 displayed (defaults to true)
4907 msg String A string that will replace the existing message box body text (defaults
4908 to the XHTML-compliant non-breaking space character ' ')
4909 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4910 progress Boolean True to display a progress bar (defaults to false)
4911 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4912 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4913 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4914 title String The title text
4915 value String The string value to set into the active textbox element if displayed
4916 wait Boolean True to display a progress bar (defaults to false)
4917 width Number The width of the dialog in pixels
4924 msg: 'Please enter your address:',
4926 buttons: Roo.MessageBox.OKCANCEL,
4929 animEl: 'addAddressBtn'
4932 * @param {Object} config Configuration options
4933 * @return {Roo.MessageBox} This message box
4935 show : function(options)
4938 // this causes nightmares if you show one dialog after another
4939 // especially on callbacks..
4941 if(this.isVisible()){
4944 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4945 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4946 Roo.log("New Dialog Message:" + options.msg )
4947 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4948 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951 var d = this.getDialog();
4953 d.setTitle(opt.title || " ");
4954 d.closeEl.setDisplayed(opt.closable !== false);
4955 activeTextEl = textboxEl;
4956 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4961 textareaEl.setHeight(typeof opt.multiline == "number" ?
4962 opt.multiline : this.defaultTextHeight);
4963 activeTextEl = textareaEl;
4972 progressEl.setDisplayed(opt.progress === true);
4974 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4976 this.updateProgress(0);
4977 activeTextEl.dom.value = opt.value || "";
4979 dlg.setDefaultButton(activeTextEl);
4981 var bs = opt.buttons;
4985 }else if(bs && bs.yes){
4986 db = buttons["yes"];
4988 dlg.setDefaultButton(db);
4990 bwidth = updateButtons(opt.buttons);
4991 this.updateText(opt.msg);
4993 d.el.addClass(opt.cls);
4995 d.proxyDrag = opt.proxyDrag === true;
4996 d.modal = opt.modal !== false;
4997 d.mask = opt.modal !== false ? mask : false;
4999 // force it to the end of the z-index stack so it gets a cursor in FF
5000 document.body.appendChild(dlg.el.dom);
5001 d.animateTarget = null;
5002 d.show(options.animEl);
5008 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5009 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5010 * and closing the message box when the process is complete.
5011 * @param {String} title The title bar text
5012 * @param {String} msg The message box body text
5013 * @return {Roo.MessageBox} This message box
5015 progress : function(title, msg){
5022 minWidth: this.minProgressWidth,
5029 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5030 * If a callback function is passed it will be called after the user clicks the button, and the
5031 * id of the button that was clicked will be passed as the only parameter to the callback
5032 * (could also be the top-right close button).
5033 * @param {String} title The title bar text
5034 * @param {String} msg The message box body text
5035 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5036 * @param {Object} scope (optional) The scope of the callback function
5037 * @return {Roo.MessageBox} This message box
5039 alert : function(title, msg, fn, scope)
5054 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5055 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5056 * You are responsible for closing the message box when the process is complete.
5057 * @param {String} msg The message box body text
5058 * @param {String} title (optional) The title bar text
5059 * @return {Roo.MessageBox} This message box
5061 wait : function(msg, title){
5072 waitTimer = Roo.TaskMgr.start({
5074 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5082 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5083 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5084 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5085 * @param {String} title The title bar text
5086 * @param {String} msg The message box body text
5087 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5088 * @param {Object} scope (optional) The scope of the callback function
5089 * @return {Roo.MessageBox} This message box
5091 confirm : function(title, msg, fn, scope){
5095 buttons: this.YESNO,
5104 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5105 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5106 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5107 * (could also be the top-right close button) and the text that was entered will be passed as the two
5108 * parameters to the callback.
5109 * @param {String} title The title bar text
5110 * @param {String} msg The message box body text
5111 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5112 * @param {Object} scope (optional) The scope of the callback function
5113 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5114 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5115 * @return {Roo.MessageBox} This message box
5117 prompt : function(title, msg, fn, scope, multiline){
5121 buttons: this.OKCANCEL,
5126 multiline: multiline,
5133 * Button config that displays a single OK button
5138 * Button config that displays Yes and No buttons
5141 YESNO : {yes:true, no:true},
5143 * Button config that displays OK and Cancel buttons
5146 OKCANCEL : {ok:true, cancel:true},
5148 * Button config that displays Yes, No and Cancel buttons
5151 YESNOCANCEL : {yes:true, no:true, cancel:true},
5154 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157 defaultTextHeight : 75,
5159 * The maximum width in pixels of the message box (defaults to 600)
5164 * The minimum width in pixels of the message box (defaults to 100)
5169 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5170 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173 minProgressWidth : 250,
5175 * An object containing the default button text strings that can be overriden for localized language support.
5176 * Supported properties are: ok, cancel, yes and no.
5177 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5190 * Shorthand for {@link Roo.MessageBox}
5192 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5193 Roo.Msg = Roo.Msg || Roo.MessageBox;
5202 * @class Roo.bootstrap.Navbar
5203 * @extends Roo.bootstrap.Component
5204 * Bootstrap Navbar class
5207 * Create a new Navbar
5208 * @param {Object} config The config object
5212 Roo.bootstrap.Navbar = function(config){
5213 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5217 * @event beforetoggle
5218 * Fire before toggle the menu
5219 * @param {Roo.EventObject} e
5221 "beforetoggle" : true
5225 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5234 getAutoCreate : function(){
5237 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5241 initEvents :function ()
5243 //Roo.log(this.el.select('.navbar-toggle',true));
5244 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5251 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5253 var size = this.el.getSize();
5254 this.maskEl.setSize(size.width, size.height);
5255 this.maskEl.enableDisplayMode("block");
5264 getChildContainer : function()
5266 if (this.el && this.el.select('.collapse').getCount()) {
5267 return this.el.select('.collapse',true).first();
5282 onToggle : function()
5285 if(this.fireEvent('beforetoggle', this) === false){
5288 var ce = this.el.select('.navbar-collapse',true).first();
5290 if (!ce.hasClass('show')) {
5300 * Expand the navbar pulldown
5302 expand : function ()
5305 var ce = this.el.select('.navbar-collapse',true).first();
5306 if (ce.hasClass('collapsing')) {
5309 ce.dom.style.height = '';
5311 ce.addClass('in'); // old...
5312 ce.removeClass('collapse');
5313 ce.addClass('show');
5314 var h = ce.getHeight();
5316 ce.removeClass('show');
5317 // at this point we should be able to see it..
5318 ce.addClass('collapsing');
5320 ce.setHeight(0); // resize it ...
5321 ce.on('transitionend', function() {
5322 //Roo.log('done transition');
5323 ce.removeClass('collapsing');
5324 ce.addClass('show');
5325 ce.removeClass('collapse');
5327 ce.dom.style.height = '';
5328 }, this, { single: true} );
5330 ce.dom.scrollTop = 0;
5333 * Collapse the navbar pulldown
5335 collapse : function()
5337 var ce = this.el.select('.navbar-collapse',true).first();
5339 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5340 // it's collapsed or collapsing..
5343 ce.removeClass('in'); // old...
5344 ce.setHeight(ce.getHeight());
5345 ce.removeClass('show');
5346 ce.addClass('collapsing');
5348 ce.on('transitionend', function() {
5349 ce.dom.style.height = '';
5350 ce.removeClass('collapsing');
5351 ce.addClass('collapse');
5352 }, this, { single: true} );
5372 * @class Roo.bootstrap.NavSimplebar
5373 * @extends Roo.bootstrap.Navbar
5374 * Bootstrap Sidebar class
5376 * @cfg {Boolean} inverse is inverted color
5378 * @cfg {String} type (nav | pills | tabs)
5379 * @cfg {Boolean} arrangement stacked | justified
5380 * @cfg {String} align (left | right) alignment
5382 * @cfg {Boolean} main (true|false) main nav bar? default false
5383 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5385 * @cfg {String} tag (header|footer|nav|div) default is nav
5387 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5391 * Create a new Sidebar
5392 * @param {Object} config The config object
5396 Roo.bootstrap.NavSimplebar = function(config){
5397 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5416 getAutoCreate : function(){
5420 tag : this.tag || 'div',
5421 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5423 if (['light','white'].indexOf(this.weight) > -1) {
5424 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5426 cfg.cls += ' bg-' + this.weight;
5429 cfg.cls += ' navbar-inverse';
5433 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5435 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5444 cls: 'nav nav-' + this.xtype,
5450 this.type = this.type || 'nav';
5451 if (['tabs','pills'].indexOf(this.type) != -1) {
5452 cfg.cn[0].cls += ' nav-' + this.type
5456 if (this.type!=='nav') {
5457 Roo.log('nav type must be nav/tabs/pills')
5459 cfg.cn[0].cls += ' navbar-nav'
5465 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5466 cfg.cn[0].cls += ' nav-' + this.arrangement;
5470 if (this.align === 'right') {
5471 cfg.cn[0].cls += ' navbar-right';
5496 * navbar-expand-md fixed-top
5500 * @class Roo.bootstrap.NavHeaderbar
5501 * @extends Roo.bootstrap.NavSimplebar
5502 * Bootstrap Sidebar class
5504 * @cfg {String} brand what is brand
5505 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5506 * @cfg {String} brand_href href of the brand
5507 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5508 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5509 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5510 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513 * Create a new Sidebar
5514 * @param {Object} config The config object
5518 Roo.bootstrap.NavHeaderbar = function(config){
5519 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5523 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5530 desktopCenter : false,
5533 getAutoCreate : function(){
5536 tag: this.nav || 'nav',
5537 cls: 'navbar navbar-expand-md',
5543 if (this.desktopCenter) {
5544 cn.push({cls : 'container', cn : []});
5552 cls: 'navbar-toggle navbar-toggler',
5553 'data-toggle': 'collapse',
5558 html: 'Toggle navigation'
5562 cls: 'icon-bar navbar-toggler-icon'
5575 cn.push( Roo.bootstrap.version == 4 ? btn : {
5577 cls: 'navbar-header',
5586 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5590 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5592 if (['light','white'].indexOf(this.weight) > -1) {
5593 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5595 cfg.cls += ' bg-' + this.weight;
5598 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5599 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5601 // tag can override this..
5603 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5606 if (this.brand !== '') {
5607 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5608 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5610 href: this.brand_href ? this.brand_href : '#',
5611 cls: 'navbar-brand',
5619 cfg.cls += ' main-nav';
5627 getHeaderChildContainer : function()
5629 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5630 return this.el.select('.navbar-header',true).first();
5633 return this.getChildContainer();
5636 getChildContainer : function()
5639 return this.el.select('.roo-navbar-collapse',true).first();
5644 initEvents : function()
5646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5648 if (this.autohide) {
5653 Roo.get(document).on('scroll',function(e) {
5654 var ns = Roo.get(document).getScroll().top;
5655 var os = prevScroll;
5659 ft.removeClass('slideDown');
5660 ft.addClass('slideUp');
5663 ft.removeClass('slideUp');
5664 ft.addClass('slideDown');
5685 * @class Roo.bootstrap.NavSidebar
5686 * @extends Roo.bootstrap.Navbar
5687 * Bootstrap Sidebar class
5690 * Create a new Sidebar
5691 * @param {Object} config The config object
5695 Roo.bootstrap.NavSidebar = function(config){
5696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5703 getAutoCreate : function(){
5708 cls: 'sidebar sidebar-nav'
5730 * @class Roo.bootstrap.NavGroup
5731 * @extends Roo.bootstrap.Component
5732 * Bootstrap NavGroup class
5733 * @cfg {String} align (left|right)
5734 * @cfg {Boolean} inverse
5735 * @cfg {String} type (nav|pills|tab) default nav
5736 * @cfg {String} navId - reference Id for navbar.
5740 * Create a new nav group
5741 * @param {Object} config The config object
5744 Roo.bootstrap.NavGroup = function(config){
5745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748 Roo.bootstrap.NavGroup.register(this);
5752 * Fires when the active item changes
5753 * @param {Roo.bootstrap.NavGroup} this
5754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5773 getAutoCreate : function()
5775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5781 if (Roo.bootstrap.version == 4) {
5782 if (['tabs','pills'].indexOf(this.type) != -1) {
5783 cfg.cls += ' nav-' + this.type;
5785 // trying to remove so header bar can right align top?
5786 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787 // do not use on header bar...
5788 cfg.cls += ' navbar-nav';
5793 if (['tabs','pills'].indexOf(this.type) != -1) {
5794 cfg.cls += ' nav-' + this.type
5796 if (this.type !== 'nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cls += ' navbar-nav'
5803 if (this.parent() && this.parent().sidebar) {
5806 cls: 'dashboard-menu sidebar-menu'
5812 if (this.form === true) {
5815 cls: 'navbar-form form-inline'
5817 //nav navbar-right ml-md-auto
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' navbar-left';
5825 if (this.align === 'right') {
5826 cfg.cls += ' navbar-right ml-md-auto';
5828 cfg.cls += ' mr-auto';
5832 cfg.cls += ' navbar-inverse';
5840 * sets the active Navigation item
5841 * @param {Roo.bootstrap.NavItem} the new current navitem
5843 setActiveItem : function(item)
5846 Roo.each(this.navItems, function(v){
5851 v.setActive(false, true);
5858 item.setActive(true, true);
5859 this.fireEvent('changed', this, item, prev);
5864 * gets the active Navigation item
5865 * @return {Roo.bootstrap.NavItem} the current navitem
5867 getActive : function()
5871 Roo.each(this.navItems, function(v){
5882 indexOfNav : function()
5886 Roo.each(this.navItems, function(v,i){
5897 * adds a Navigation item
5898 * @param {Roo.bootstrap.NavItem} the navitem to add
5900 addItem : function(cfg)
5902 if (this.form && Roo.bootstrap.version == 4) {
5905 var cn = new Roo.bootstrap.NavItem(cfg);
5907 cn.parentId = this.id;
5908 cn.onRender(this.el, null);
5912 * register a Navigation item
5913 * @param {Roo.bootstrap.NavItem} the navitem to add
5915 register : function(item)
5917 this.navItems.push( item);
5918 item.navId = this.navId;
5923 * clear all the Navigation item
5926 clearAll : function()
5929 this.el.dom.innerHTML = '';
5932 getNavItem: function(tabId)
5935 Roo.each(this.navItems, function(e) {
5936 if (e.tabId == tabId) {
5946 setActiveNext : function()
5948 var i = this.indexOfNav(this.getActive());
5949 if (i > this.navItems.length) {
5952 this.setActiveItem(this.navItems[i+1]);
5954 setActivePrev : function()
5956 var i = this.indexOfNav(this.getActive());
5960 this.setActiveItem(this.navItems[i-1]);
5962 clearWasActive : function(except) {
5963 Roo.each(this.navItems, function(e) {
5964 if (e.tabId != except.tabId && e.was_active) {
5965 e.was_active = false;
5972 getWasActive : function ()
5975 Roo.each(this.navItems, function(e) {
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5994 * register a Navigation Group
5995 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5997 register : function(navgrp)
5999 this.groups[navgrp.navId] = navgrp;
6003 * fetch a Navigation Group based on the navigation ID
6004 * @param {string} the navgroup to add
6005 * @returns {Roo.bootstrap.NavGroup} the navgroup
6007 get: function(navId) {
6008 if (typeof(this.groups[navId]) == 'undefined') {
6010 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6012 return this.groups[navId] ;
6027 * @class Roo.bootstrap.NavItem
6028 * @extends Roo.bootstrap.Component
6029 * Bootstrap Navbar.NavItem class
6030 * @cfg {String} href link to
6031 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6033 * @cfg {String} html content of button
6034 * @cfg {String} badge text inside badge
6035 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036 * @cfg {String} glyphicon DEPRICATED - use fa
6037 * @cfg {String} icon DEPRICATED - use fa
6038 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039 * @cfg {Boolean} active Is item active
6040 * @cfg {Boolean} disabled Is item disabled
6042 * @cfg {Boolean} preventDefault (true | false) default false
6043 * @cfg {String} tabId the tab that this item activates.
6044 * @cfg {String} tagtype (a|span) render as a href or span?
6045 * @cfg {Boolean} animateRef (true|false) link to element default false
6048 * Create a new Navbar Item
6049 * @param {Object} config The config object
6051 Roo.bootstrap.NavItem = function(config){
6052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6057 * The raw click event for the entire grid.
6058 * @param {Roo.EventObject} e
6063 * Fires when the active item active state changes
6064 * @param {Roo.bootstrap.NavItem} this
6065 * @param {boolean} state the new state
6071 * Fires when scroll to element
6072 * @param {Roo.bootstrap.NavItem} this
6073 * @param {Object} options
6074 * @param {Roo.EventObject} e
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6091 preventDefault : false,
6099 button_outline : false,
6103 getAutoCreate : function(){
6111 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6113 if (this.disabled) {
6114 cfg.cls += ' disabled';
6118 if (this.button_weight.length) {
6119 cfg.tag = this.href ? 'a' : 'button';
6120 cfg.html = this.html || '';
6121 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6123 cfg.href = this.href;
6126 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129 // menu .. should add dropdown-menu class - so no need for carat..
6131 if (this.badge !== '') {
6133 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6138 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6142 href : this.href || "#",
6143 html: this.html || ''
6146 if (this.tagtype == 'a') {
6147 cfg.cn[0].cls = 'nav-link';
6150 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6153 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6155 if(this.glyphicon) {
6156 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6161 cfg.cn[0].html += " <span class='caret'></span>";
6165 if (this.badge !== '') {
6167 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6175 onRender : function(ct, position)
6177 // Roo.log("Call onRender: " + this.xtype);
6178 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6183 this.navLink = this.el.select('.nav-link',true).first();
6188 initEvents: function()
6190 if (typeof (this.menu) != 'undefined') {
6191 this.menu.parentType = this.xtype;
6192 this.menu.triggerEl = this.el;
6193 this.menu = this.addxtype(Roo.apply({}, this.menu));
6196 this.el.select('a',true).on('click', this.onClick, this);
6198 if(this.tagtype == 'span'){
6199 this.el.select('span',true).on('click', this.onClick, this);
6202 // at this point parent should be available..
6203 this.parent().register(this);
6206 onClick : function(e)
6208 if (e.getTarget('.dropdown-menu-item')) {
6209 // did you click on a menu itemm.... - then don't trigger onclick..
6214 this.preventDefault ||
6217 Roo.log("NavItem - prevent Default?");
6221 if (this.disabled) {
6225 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6226 if (tg && tg.transition) {
6227 Roo.log("waiting for the transitionend");
6233 //Roo.log("fire event clicked");
6234 if(this.fireEvent('click', this, e) === false){
6238 if(this.tagtype == 'span'){
6242 //Roo.log(this.href);
6243 var ael = this.el.select('a',true).first();
6246 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6247 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6248 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6249 return; // ignore... - it's a 'hash' to another page.
6251 Roo.log("NavItem - prevent Default?");
6253 this.scrollToElement(e);
6257 var p = this.parent();
6259 if (['tabs','pills'].indexOf(p.type)!==-1) {
6260 if (typeof(p.setActiveItem) !== 'undefined') {
6261 p.setActiveItem(this);
6265 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6266 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6267 // remove the collapsed menu expand...
6268 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6272 isActive: function () {
6275 setActive : function(state, fire, is_was_active)
6277 if (this.active && !state && this.navId) {
6278 this.was_active = true;
6279 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6281 nv.clearWasActive(this);
6285 this.active = state;
6288 this.el.removeClass('active');
6289 this.navLink ? this.navLink.removeClass('active') : false;
6290 } else if (!this.el.hasClass('active')) {
6292 this.el.addClass('active');
6293 if (Roo.bootstrap.version == 4 && this.navLink ) {
6294 this.navLink.addClass('active');
6299 this.fireEvent('changed', this, state);
6302 // show a panel if it's registered and related..
6304 if (!this.navId || !this.tabId || !state || is_was_active) {
6308 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312 var pan = tg.getPanelByName(this.tabId);
6316 // if we can not flip to new panel - go back to old nav highlight..
6317 if (false == tg.showPanel(pan)) {
6318 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6320 var onav = nv.getWasActive();
6322 onav.setActive(true, false, true);
6331 // this should not be here...
6332 setDisabled : function(state)
6334 this.disabled = state;
6336 this.el.removeClass('disabled');
6337 } else if (!this.el.hasClass('disabled')) {
6338 this.el.addClass('disabled');
6344 * Fetch the element to display the tooltip on.
6345 * @return {Roo.Element} defaults to this.el
6347 tooltipEl : function()
6349 return this.el.select('' + this.tagtype + '', true).first();
6352 scrollToElement : function(e)
6354 var c = document.body;
6357 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6359 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6360 c = document.documentElement;
6363 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6369 var o = target.calcOffsetsTo(c);
6376 this.fireEvent('scrollto', this, options, e);
6378 Roo.get(c).scrollTo('top', options.value, true);
6391 * <span> icon </span>
6392 * <span> text </span>
6393 * <span>badge </span>
6397 * @class Roo.bootstrap.NavSidebarItem
6398 * @extends Roo.bootstrap.NavItem
6399 * Bootstrap Navbar.NavSidebarItem class
6400 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6401 * {Boolean} open is the menu open
6402 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6403 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6404 * {String} buttonSize (sm|md|lg)the extra classes for the button
6405 * {Boolean} showArrow show arrow next to the text (default true)
6407 * Create a new Navbar Button
6408 * @param {Object} config The config object
6410 Roo.bootstrap.NavSidebarItem = function(config){
6411 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6416 * The raw click event for the entire grid.
6417 * @param {Roo.EventObject} e
6422 * Fires when the active item active state changes
6423 * @param {Roo.bootstrap.NavSidebarItem} this
6424 * @param {boolean} state the new state
6432 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6434 badgeWeight : 'default',
6440 buttonWeight : 'default',
6446 getAutoCreate : function(){
6451 href : this.href || '#',
6457 if(this.buttonView){
6460 href : this.href || '#',
6461 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6474 cfg.cls += ' active';
6477 if (this.disabled) {
6478 cfg.cls += ' disabled';
6481 cfg.cls += ' open x-open';
6484 if (this.glyphicon || this.icon) {
6485 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6486 a.cn.push({ tag : 'i', cls : c }) ;
6489 if(!this.buttonView){
6492 html : this.html || ''
6499 if (this.badge !== '') {
6500 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6506 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6509 a.cls += ' dropdown-toggle treeview' ;
6515 initEvents : function()
6517 if (typeof (this.menu) != 'undefined') {
6518 this.menu.parentType = this.xtype;
6519 this.menu.triggerEl = this.el;
6520 this.menu = this.addxtype(Roo.apply({}, this.menu));
6523 this.el.on('click', this.onClick, this);
6525 if(this.badge !== ''){
6526 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6531 onClick : function(e)
6538 if(this.preventDefault){
6542 this.fireEvent('click', this, e);
6545 disable : function()
6547 this.setDisabled(true);
6552 this.setDisabled(false);
6555 setDisabled : function(state)
6557 if(this.disabled == state){
6561 this.disabled = state;
6564 this.el.addClass('disabled');
6568 this.el.removeClass('disabled');
6573 setActive : function(state)
6575 if(this.active == state){
6579 this.active = state;
6582 this.el.addClass('active');
6586 this.el.removeClass('active');
6591 isActive: function ()
6596 setBadge : function(str)
6602 this.badgeEl.dom.innerHTML = str;
6617 Roo.namespace('Roo.bootstrap.breadcrumb');
6621 * @class Roo.bootstrap.breadcrumb.Nav
6622 * @extends Roo.bootstrap.Component
6623 * Bootstrap Breadcrumb Nav Class
6625 * @children Roo.bootstrap.breadcrumb.Item
6628 * Create a new breadcrumb.Nav
6629 * @param {Object} config The config object
6633 Roo.bootstrap.breadcrumb.Nav = function(config){
6634 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6639 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6641 getAutoCreate : function()
6658 initEvents: function()
6660 this.olEl = this.el.select('ol',true).first();
6662 getChildContainer : function()
6678 * @class Roo.bootstrap.breadcrumb.Nav
6679 * @extends Roo.bootstrap.Component
6680 * Bootstrap Breadcrumb Nav Class
6682 * @children Roo.bootstrap.breadcrumb.Component
6683 * @cfg {String} html the content of the link.
6684 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6685 * @cfg {Boolean} active is it active
6689 * Create a new breadcrumb.Nav
6690 * @param {Object} config The config object
6693 Roo.bootstrap.breadcrumb.Item = function(config){
6694 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6699 * The img click event for the img.
6700 * @param {Roo.EventObject} e
6707 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6712 getAutoCreate : function()
6717 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6719 if (this.href !== false) {
6726 cfg.html = this.html;
6732 initEvents: function()
6735 this.el.select('a', true).first().on('click',this.onClick, this)
6739 onClick : function(e)
6742 this.fireEvent('click',this, e);
6755 * @class Roo.bootstrap.Row
6756 * @extends Roo.bootstrap.Component
6757 * Bootstrap Row class (contains columns...)
6761 * @param {Object} config The config object
6764 Roo.bootstrap.Row = function(config){
6765 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6770 getAutoCreate : function(){
6789 * @class Roo.bootstrap.Pagination
6790 * @extends Roo.bootstrap.Component
6791 * Bootstrap Pagination class
6792 * @cfg {String} size xs | sm | md | lg
6793 * @cfg {Boolean} inverse false | true
6796 * Create a new Pagination
6797 * @param {Object} config The config object
6800 Roo.bootstrap.Pagination = function(config){
6801 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6804 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6810 getAutoCreate : function(){
6816 cfg.cls += ' inverse';
6822 cfg.cls += " " + this.cls;
6840 * @class Roo.bootstrap.PaginationItem
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap PaginationItem class
6843 * @cfg {String} html text
6844 * @cfg {String} href the link
6845 * @cfg {Boolean} preventDefault (true | false) default true
6846 * @cfg {Boolean} active (true | false) default false
6847 * @cfg {Boolean} disabled default false
6851 * Create a new PaginationItem
6852 * @param {Object} config The config object
6856 Roo.bootstrap.PaginationItem = function(config){
6857 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6862 * The raw click event for the entire grid.
6863 * @param {Roo.EventObject} e
6869 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6873 preventDefault: true,
6878 getAutoCreate : function(){
6884 href : this.href ? this.href : '#',
6885 html : this.html ? this.html : ''
6895 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6905 initEvents: function() {
6907 this.el.on('click', this.onClick, this);
6910 onClick : function(e)
6912 Roo.log('PaginationItem on click ');
6913 if(this.preventDefault){
6921 this.fireEvent('click', this, e);
6937 * @class Roo.bootstrap.Slider
6938 * @extends Roo.bootstrap.Component
6939 * Bootstrap Slider class
6942 * Create a new Slider
6943 * @param {Object} config The config object
6946 Roo.bootstrap.Slider = function(config){
6947 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6950 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6952 getAutoCreate : function(){
6956 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6972 * Ext JS Library 1.1.1
6973 * Copyright(c) 2006-2007, Ext JS, LLC.
6975 * Originally Released Under LGPL - original licence link has changed is not relivant.
6978 * <script type="text/javascript">
6983 * @class Roo.grid.ColumnModel
6984 * @extends Roo.util.Observable
6985 * This is the default implementation of a ColumnModel used by the Grid. It defines
6986 * the columns in the grid.
6989 var colModel = new Roo.grid.ColumnModel([
6990 {header: "Ticker", width: 60, sortable: true, locked: true},
6991 {header: "Company Name", width: 150, sortable: true},
6992 {header: "Market Cap.", width: 100, sortable: true},
6993 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6994 {header: "Employees", width: 100, sortable: true, resizable: false}
6999 * The config options listed for this class are options which may appear in each
7000 * individual column definition.
7001 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7003 * @param {Object} config An Array of column config objects. See this class's
7004 * config objects for details.
7006 Roo.grid.ColumnModel = function(config){
7008 * The config passed into the constructor
7010 this.config = config;
7013 // if no id, create one
7014 // if the column does not have a dataIndex mapping,
7015 // map it to the order it is in the config
7016 for(var i = 0, len = config.length; i < len; i++){
7018 if(typeof c.dataIndex == "undefined"){
7021 if(typeof c.renderer == "string"){
7022 c.renderer = Roo.util.Format[c.renderer];
7024 if(typeof c.id == "undefined"){
7027 if(c.editor && c.editor.xtype){
7028 c.editor = Roo.factory(c.editor, Roo.grid);
7030 if(c.editor && c.editor.isFormField){
7031 c.editor = new Roo.grid.GridEditor(c.editor);
7033 this.lookup[c.id] = c;
7037 * The width of columns which have no width specified (defaults to 100)
7040 this.defaultWidth = 100;
7043 * Default sortable of columns which have no sortable specified (defaults to false)
7046 this.defaultSortable = false;
7050 * @event widthchange
7051 * Fires when the width of a column changes.
7052 * @param {ColumnModel} this
7053 * @param {Number} columnIndex The column index
7054 * @param {Number} newWidth The new width
7056 "widthchange": true,
7058 * @event headerchange
7059 * Fires when the text of a header changes.
7060 * @param {ColumnModel} this
7061 * @param {Number} columnIndex The column index
7062 * @param {Number} newText The new header text
7064 "headerchange": true,
7066 * @event hiddenchange
7067 * Fires when a column is hidden or "unhidden".
7068 * @param {ColumnModel} this
7069 * @param {Number} columnIndex The column index
7070 * @param {Boolean} hidden true if hidden, false otherwise
7072 "hiddenchange": true,
7074 * @event columnmoved
7075 * Fires when a column is moved.
7076 * @param {ColumnModel} this
7077 * @param {Number} oldIndex
7078 * @param {Number} newIndex
7080 "columnmoved" : true,
7082 * @event columlockchange
7083 * Fires when a column's locked state is changed
7084 * @param {ColumnModel} this
7085 * @param {Number} colIndex
7086 * @param {Boolean} locked true if locked
7088 "columnlockchange" : true
7090 Roo.grid.ColumnModel.superclass.constructor.call(this);
7092 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7094 * @cfg {String} header The header text to display in the Grid view.
7097 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7098 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7099 * specified, the column's index is used as an index into the Record's data Array.
7102 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7103 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7106 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7107 * Defaults to the value of the {@link #defaultSortable} property.
7108 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7111 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7114 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7117 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7120 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7123 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7124 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7125 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7126 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7129 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7132 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7135 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7138 * @cfg {String} cursor (Optional)
7141 * @cfg {String} tooltip (Optional)
7144 * @cfg {Number} xs (Optional)
7147 * @cfg {Number} sm (Optional)
7150 * @cfg {Number} md (Optional)
7153 * @cfg {Number} lg (Optional)
7156 * Returns the id of the column at the specified index.
7157 * @param {Number} index The column index
7158 * @return {String} the id
7160 getColumnId : function(index){
7161 return this.config[index].id;
7165 * Returns the column for a specified id.
7166 * @param {String} id The column id
7167 * @return {Object} the column
7169 getColumnById : function(id){
7170 return this.lookup[id];
7175 * Returns the column for a specified dataIndex.
7176 * @param {String} dataIndex The column dataIndex
7177 * @return {Object|Boolean} the column or false if not found
7179 getColumnByDataIndex: function(dataIndex){
7180 var index = this.findColumnIndex(dataIndex);
7181 return index > -1 ? this.config[index] : false;
7185 * Returns the index for a specified column id.
7186 * @param {String} id The column id
7187 * @return {Number} the index, or -1 if not found
7189 getIndexById : function(id){
7190 for(var i = 0, len = this.config.length; i < len; i++){
7191 if(this.config[i].id == id){
7199 * Returns the index for a specified column dataIndex.
7200 * @param {String} dataIndex The column dataIndex
7201 * @return {Number} the index, or -1 if not found
7204 findColumnIndex : function(dataIndex){
7205 for(var i = 0, len = this.config.length; i < len; i++){
7206 if(this.config[i].dataIndex == dataIndex){
7214 moveColumn : function(oldIndex, newIndex){
7215 var c = this.config[oldIndex];
7216 this.config.splice(oldIndex, 1);
7217 this.config.splice(newIndex, 0, c);
7218 this.dataMap = null;
7219 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7222 isLocked : function(colIndex){
7223 return this.config[colIndex].locked === true;
7226 setLocked : function(colIndex, value, suppressEvent){
7227 if(this.isLocked(colIndex) == value){
7230 this.config[colIndex].locked = value;
7232 this.fireEvent("columnlockchange", this, colIndex, value);
7236 getTotalLockedWidth : function(){
7238 for(var i = 0; i < this.config.length; i++){
7239 if(this.isLocked(i) && !this.isHidden(i)){
7240 this.totalWidth += this.getColumnWidth(i);
7246 getLockedCount : function(){
7247 for(var i = 0, len = this.config.length; i < len; i++){
7248 if(!this.isLocked(i)){
7253 return this.config.length;
7257 * Returns the number of columns.
7260 getColumnCount : function(visibleOnly){
7261 if(visibleOnly === true){
7263 for(var i = 0, len = this.config.length; i < len; i++){
7264 if(!this.isHidden(i)){
7270 return this.config.length;
7274 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7275 * @param {Function} fn
7276 * @param {Object} scope (optional)
7277 * @return {Array} result
7279 getColumnsBy : function(fn, scope){
7281 for(var i = 0, len = this.config.length; i < len; i++){
7282 var c = this.config[i];
7283 if(fn.call(scope||this, c, i) === true){
7291 * Returns true if the specified column is sortable.
7292 * @param {Number} col The column index
7295 isSortable : function(col){
7296 if(typeof this.config[col].sortable == "undefined"){
7297 return this.defaultSortable;
7299 return this.config[col].sortable;
7303 * Returns the rendering (formatting) function defined for the column.
7304 * @param {Number} col The column index.
7305 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7307 getRenderer : function(col){
7308 if(!this.config[col].renderer){
7309 return Roo.grid.ColumnModel.defaultRenderer;
7311 return this.config[col].renderer;
7315 * Sets the rendering (formatting) function for a column.
7316 * @param {Number} col The column index
7317 * @param {Function} fn The function to use to process the cell's raw data
7318 * to return HTML markup for the grid view. The render function is called with
7319 * the following parameters:<ul>
7320 * <li>Data value.</li>
7321 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7322 * <li>css A CSS style string to apply to the table cell.</li>
7323 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7324 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7325 * <li>Row index</li>
7326 * <li>Column index</li>
7327 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7329 setRenderer : function(col, fn){
7330 this.config[col].renderer = fn;
7334 * Returns the width for the specified column.
7335 * @param {Number} col The column index
7338 getColumnWidth : function(col){
7339 return this.config[col].width * 1 || this.defaultWidth;
7343 * Sets the width for a column.
7344 * @param {Number} col The column index
7345 * @param {Number} width The new width
7347 setColumnWidth : function(col, width, suppressEvent){
7348 this.config[col].width = width;
7349 this.totalWidth = null;
7351 this.fireEvent("widthchange", this, col, width);
7356 * Returns the total width of all columns.
7357 * @param {Boolean} includeHidden True to include hidden column widths
7360 getTotalWidth : function(includeHidden){
7361 if(!this.totalWidth){
7362 this.totalWidth = 0;
7363 for(var i = 0, len = this.config.length; i < len; i++){
7364 if(includeHidden || !this.isHidden(i)){
7365 this.totalWidth += this.getColumnWidth(i);
7369 return this.totalWidth;
7373 * Returns the header for the specified column.
7374 * @param {Number} col The column index
7377 getColumnHeader : function(col){
7378 return this.config[col].header;
7382 * Sets the header for a column.
7383 * @param {Number} col The column index
7384 * @param {String} header The new header
7386 setColumnHeader : function(col, header){
7387 this.config[col].header = header;
7388 this.fireEvent("headerchange", this, col, header);
7392 * Returns the tooltip for the specified column.
7393 * @param {Number} col The column index
7396 getColumnTooltip : function(col){
7397 return this.config[col].tooltip;
7400 * Sets the tooltip for a column.
7401 * @param {Number} col The column index
7402 * @param {String} tooltip The new tooltip
7404 setColumnTooltip : function(col, tooltip){
7405 this.config[col].tooltip = tooltip;
7409 * Returns the dataIndex for the specified column.
7410 * @param {Number} col The column index
7413 getDataIndex : function(col){
7414 return this.config[col].dataIndex;
7418 * Sets the dataIndex for a column.
7419 * @param {Number} col The column index
7420 * @param {Number} dataIndex The new dataIndex
7422 setDataIndex : function(col, dataIndex){
7423 this.config[col].dataIndex = dataIndex;
7429 * Returns true if the cell is editable.
7430 * @param {Number} colIndex The column index
7431 * @param {Number} rowIndex The row index - this is nto actually used..?
7434 isCellEditable : function(colIndex, rowIndex){
7435 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439 * Returns the editor defined for the cell/column.
7440 * return false or null to disable editing.
7441 * @param {Number} colIndex The column index
7442 * @param {Number} rowIndex The row index
7445 getCellEditor : function(colIndex, rowIndex){
7446 return this.config[colIndex].editor;
7450 * Sets if a column is editable.
7451 * @param {Number} col The column index
7452 * @param {Boolean} editable True if the column is editable
7454 setEditable : function(col, editable){
7455 this.config[col].editable = editable;
7460 * Returns true if the column is hidden.
7461 * @param {Number} colIndex The column index
7464 isHidden : function(colIndex){
7465 return this.config[colIndex].hidden;
7470 * Returns true if the column width cannot be changed
7472 isFixed : function(colIndex){
7473 return this.config[colIndex].fixed;
7477 * Returns true if the column can be resized
7480 isResizable : function(colIndex){
7481 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7484 * Sets if a column is hidden.
7485 * @param {Number} colIndex The column index
7486 * @param {Boolean} hidden True if the column is hidden
7488 setHidden : function(colIndex, hidden){
7489 this.config[colIndex].hidden = hidden;
7490 this.totalWidth = null;
7491 this.fireEvent("hiddenchange", this, colIndex, hidden);
7495 * Sets the editor for a column.
7496 * @param {Number} col The column index
7497 * @param {Object} editor The editor object
7499 setEditor : function(col, editor){
7500 this.config[col].editor = editor;
7504 Roo.grid.ColumnModel.defaultRenderer = function(value)
7506 if(typeof value == "object") {
7509 if(typeof value == "string" && value.length < 1){
7513 return String.format("{0}", value);
7516 // Alias for backwards compatibility
7517 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7520 * Ext JS Library 1.1.1
7521 * Copyright(c) 2006-2007, Ext JS, LLC.
7523 * Originally Released Under LGPL - original licence link has changed is not relivant.
7526 * <script type="text/javascript">
7530 * @class Roo.LoadMask
7531 * A simple utility class for generically masking elements while loading data. If the element being masked has
7532 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7533 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7534 * element's UpdateManager load indicator and will be destroyed after the initial load.
7536 * Create a new LoadMask
7537 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7538 * @param {Object} config The config object
7540 Roo.LoadMask = function(el, config){
7541 this.el = Roo.get(el);
7542 Roo.apply(this, config);
7544 this.store.on('beforeload', this.onBeforeLoad, this);
7545 this.store.on('load', this.onLoad, this);
7546 this.store.on('loadexception', this.onLoadException, this);
7547 this.removeMask = false;
7549 var um = this.el.getUpdateManager();
7550 um.showLoadIndicator = false; // disable the default indicator
7551 um.on('beforeupdate', this.onBeforeLoad, this);
7552 um.on('update', this.onLoad, this);
7553 um.on('failure', this.onLoad, this);
7554 this.removeMask = true;
7558 Roo.LoadMask.prototype = {
7560 * @cfg {Boolean} removeMask
7561 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7562 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7566 * The text to display in a centered loading message box (defaults to 'Loading...')
7570 * @cfg {String} msgCls
7571 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7573 msgCls : 'x-mask-loading',
7576 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7582 * Disables the mask to prevent it from being displayed
7584 disable : function(){
7585 this.disabled = true;
7589 * Enables the mask so that it can be displayed
7591 enable : function(){
7592 this.disabled = false;
7595 onLoadException : function()
7599 if (typeof(arguments[3]) != 'undefined') {
7600 Roo.MessageBox.alert("Error loading",arguments[3]);
7604 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7605 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621 onBeforeLoad : function(){
7623 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7628 destroy : function(){
7630 this.store.un('beforeload', this.onBeforeLoad, this);
7631 this.store.un('load', this.onLoad, this);
7632 this.store.un('loadexception', this.onLoadException, this);
7634 var um = this.el.getUpdateManager();
7635 um.un('beforeupdate', this.onBeforeLoad, this);
7636 um.un('update', this.onLoad, this);
7637 um.un('failure', this.onLoad, this);
7648 * @class Roo.bootstrap.Table
7649 * @extends Roo.bootstrap.Component
7650 * Bootstrap Table class
7651 * @cfg {String} cls table class
7652 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7653 * @cfg {String} bgcolor Specifies the background color for a table
7654 * @cfg {Number} border Specifies whether the table cells should have borders or not
7655 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7656 * @cfg {Number} cellspacing Specifies the space between cells
7657 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7658 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7659 * @cfg {String} sortable Specifies that the table should be sortable
7660 * @cfg {String} summary Specifies a summary of the content of a table
7661 * @cfg {Number} width Specifies the width of a table
7662 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7664 * @cfg {boolean} striped Should the rows be alternative striped
7665 * @cfg {boolean} bordered Add borders to the table
7666 * @cfg {boolean} hover Add hover highlighting
7667 * @cfg {boolean} condensed Format condensed
7668 * @cfg {boolean} responsive Format condensed
7669 * @cfg {Boolean} loadMask (true|false) default false
7670 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7671 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7672 * @cfg {Boolean} rowSelection (true|false) default false
7673 * @cfg {Boolean} cellSelection (true|false) default false
7674 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7675 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7676 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7677 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7681 * Create a new Table
7682 * @param {Object} config The config object
7685 Roo.bootstrap.Table = function(config){
7686 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7691 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7692 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7693 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7694 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7696 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7698 this.sm.grid = this;
7699 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7700 this.sm = this.selModel;
7701 this.sm.xmodule = this.xmodule || false;
7704 if (this.cm && typeof(this.cm.config) == 'undefined') {
7705 this.colModel = new Roo.grid.ColumnModel(this.cm);
7706 this.cm = this.colModel;
7707 this.cm.xmodule = this.xmodule || false;
7710 this.store= Roo.factory(this.store, Roo.data);
7711 this.ds = this.store;
7712 this.ds.xmodule = this.xmodule || false;
7715 if (this.footer && this.store) {
7716 this.footer.dataSource = this.ds;
7717 this.footer = Roo.factory(this.footer);
7724 * Fires when a cell is clicked
7725 * @param {Roo.bootstrap.Table} this
7726 * @param {Roo.Element} el
7727 * @param {Number} rowIndex
7728 * @param {Number} columnIndex
7729 * @param {Roo.EventObject} e
7733 * @event celldblclick
7734 * Fires when a cell is double clicked
7735 * @param {Roo.bootstrap.Table} this
7736 * @param {Roo.Element} el
7737 * @param {Number} rowIndex
7738 * @param {Number} columnIndex
7739 * @param {Roo.EventObject} e
7741 "celldblclick" : true,
7744 * Fires when a row is clicked
7745 * @param {Roo.bootstrap.Table} this
7746 * @param {Roo.Element} el
7747 * @param {Number} rowIndex
7748 * @param {Roo.EventObject} e
7752 * @event rowdblclick
7753 * Fires when a row is double clicked
7754 * @param {Roo.bootstrap.Table} this
7755 * @param {Roo.Element} el
7756 * @param {Number} rowIndex
7757 * @param {Roo.EventObject} e
7759 "rowdblclick" : true,
7762 * Fires when a mouseover occur
7763 * @param {Roo.bootstrap.Table} this
7764 * @param {Roo.Element} el
7765 * @param {Number} rowIndex
7766 * @param {Number} columnIndex
7767 * @param {Roo.EventObject} e
7772 * Fires when a mouseout occur
7773 * @param {Roo.bootstrap.Table} this
7774 * @param {Roo.Element} el
7775 * @param {Number} rowIndex
7776 * @param {Number} columnIndex
7777 * @param {Roo.EventObject} e
7782 * Fires when a row is rendered, so you can change add a style to it.
7783 * @param {Roo.bootstrap.Table} this
7784 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7788 * @event rowsrendered
7789 * Fires when all the rows have been rendered
7790 * @param {Roo.bootstrap.Table} this
7792 'rowsrendered' : true,
7794 * @event contextmenu
7795 * The raw contextmenu event for the entire grid.
7796 * @param {Roo.EventObject} e
7798 "contextmenu" : true,
7800 * @event rowcontextmenu
7801 * Fires when a row is right clicked
7802 * @param {Roo.bootstrap.Table} this
7803 * @param {Number} rowIndex
7804 * @param {Roo.EventObject} e
7806 "rowcontextmenu" : true,
7808 * @event cellcontextmenu
7809 * Fires when a cell is right clicked
7810 * @param {Roo.bootstrap.Table} this
7811 * @param {Number} rowIndex
7812 * @param {Number} cellIndex
7813 * @param {Roo.EventObject} e
7815 "cellcontextmenu" : true,
7817 * @event headercontextmenu
7818 * Fires when a header is right clicked
7819 * @param {Roo.bootstrap.Table} this
7820 * @param {Number} columnIndex
7821 * @param {Roo.EventObject} e
7823 "headercontextmenu" : true
7827 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7853 rowSelection : false,
7854 cellSelection : false,
7857 // Roo.Element - the tbody
7859 // Roo.Element - thead element
7862 container: false, // used by gridpanel...
7868 auto_hide_footer : false,
7870 getAutoCreate : function()
7872 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879 if (this.scrollBody) {
7880 cfg.cls += ' table-body-fixed';
7883 cfg.cls += ' table-striped';
7887 cfg.cls += ' table-hover';
7889 if (this.bordered) {
7890 cfg.cls += ' table-bordered';
7892 if (this.condensed) {
7893 cfg.cls += ' table-condensed';
7895 if (this.responsive) {
7896 cfg.cls += ' table-responsive';
7900 cfg.cls+= ' ' +this.cls;
7903 // this lot should be simplifed...
7916 ].forEach(function(k) {
7924 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7927 if(this.store || this.cm){
7928 if(this.headerShow){
7929 cfg.cn.push(this.renderHeader());
7932 cfg.cn.push(this.renderBody());
7934 if(this.footerShow){
7935 cfg.cn.push(this.renderFooter());
7937 // where does this come from?
7938 //cfg.cls+= ' TableGrid';
7941 return { cn : [ cfg ] };
7944 initEvents : function()
7946 if(!this.store || !this.cm){
7949 if (this.selModel) {
7950 this.selModel.initEvents();
7954 //Roo.log('initEvents with ds!!!!');
7956 this.mainBody = this.el.select('tbody', true).first();
7957 this.mainHead = this.el.select('thead', true).first();
7958 this.mainFoot = this.el.select('tfoot', true).first();
7964 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7965 e.on('click', _this.sort, _this);
7968 this.mainBody.on("click", this.onClick, this);
7969 this.mainBody.on("dblclick", this.onDblClick, this);
7971 // why is this done????? = it breaks dialogs??
7972 //this.parent().el.setStyle('position', 'relative');
7976 this.footer.parentId = this.id;
7977 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7980 this.el.select('tfoot tr td').first().addClass('hide');
7985 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7988 this.store.on('load', this.onLoad, this);
7989 this.store.on('beforeload', this.onBeforeLoad, this);
7990 this.store.on('update', this.onUpdate, this);
7991 this.store.on('add', this.onAdd, this);
7992 this.store.on("clear", this.clear, this);
7994 this.el.on("contextmenu", this.onContextMenu, this);
7996 this.mainBody.on('scroll', this.onBodyScroll, this);
7998 this.cm.on("headerchange", this.onHeaderChange, this);
8000 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004 onContextMenu : function(e, t)
8006 this.processEvent("contextmenu", e);
8009 processEvent : function(name, e)
8011 if (name != 'touchstart' ) {
8012 this.fireEvent(name, e);
8015 var t = e.getTarget();
8017 var cell = Roo.get(t);
8023 if(cell.findParent('tfoot', false, true)){
8027 if(cell.findParent('thead', false, true)){
8029 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8030 cell = Roo.get(t).findParent('th', false, true);
8032 Roo.log("failed to find th in thead?");
8033 Roo.log(e.getTarget());
8038 var cellIndex = cell.dom.cellIndex;
8040 var ename = name == 'touchstart' ? 'click' : name;
8041 this.fireEvent("header" + ename, this, cellIndex, e);
8046 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8047 cell = Roo.get(t).findParent('td', false, true);
8049 Roo.log("failed to find th in tbody?");
8050 Roo.log(e.getTarget());
8055 var row = cell.findParent('tr', false, true);
8056 var cellIndex = cell.dom.cellIndex;
8057 var rowIndex = row.dom.rowIndex - 1;
8061 this.fireEvent("row" + name, this, rowIndex, e);
8065 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8071 onMouseover : function(e, el)
8073 var cell = Roo.get(el);
8079 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8080 cell = cell.findParent('td', false, true);
8083 var row = cell.findParent('tr', false, true);
8084 var cellIndex = cell.dom.cellIndex;
8085 var rowIndex = row.dom.rowIndex - 1; // start from 0
8087 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091 onMouseout : function(e, el)
8093 var cell = Roo.get(el);
8099 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8100 cell = cell.findParent('td', false, true);
8103 var row = cell.findParent('tr', false, true);
8104 var cellIndex = cell.dom.cellIndex;
8105 var rowIndex = row.dom.rowIndex - 1; // start from 0
8107 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111 onClick : function(e, el)
8113 var cell = Roo.get(el);
8115 if(!cell || (!this.cellSelection && !this.rowSelection)){
8119 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120 cell = cell.findParent('td', false, true);
8123 if(!cell || typeof(cell) == 'undefined'){
8127 var row = cell.findParent('tr', false, true);
8129 if(!row || typeof(row) == 'undefined'){
8133 var cellIndex = cell.dom.cellIndex;
8134 var rowIndex = this.getRowIndex(row);
8136 // why??? - should these not be based on SelectionModel?
8137 if(this.cellSelection){
8138 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8141 if(this.rowSelection){
8142 this.fireEvent('rowclick', this, row, rowIndex, e);
8148 onDblClick : function(e,el)
8150 var cell = Roo.get(el);
8152 if(!cell || (!this.cellSelection && !this.rowSelection)){
8156 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8157 cell = cell.findParent('td', false, true);
8160 if(!cell || typeof(cell) == 'undefined'){
8164 var row = cell.findParent('tr', false, true);
8166 if(!row || typeof(row) == 'undefined'){
8170 var cellIndex = cell.dom.cellIndex;
8171 var rowIndex = this.getRowIndex(row);
8173 if(this.cellSelection){
8174 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8177 if(this.rowSelection){
8178 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182 sort : function(e,el)
8184 var col = Roo.get(el);
8186 if(!col.hasClass('sortable')){
8190 var sort = col.attr('sort');
8193 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197 this.store.sortInfo = {field : sort, direction : dir};
8200 Roo.log("calling footer first");
8201 this.footer.onClick('first');
8204 this.store.load({ params : { start : 0 } });
8208 renderHeader : function()
8216 this.totalWidth = 0;
8218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8220 var config = cm.config[i];
8224 cls : 'x-hcol-' + i,
8226 html: cm.getColumnHeader(i)
8231 if(typeof(config.sortable) != 'undefined' && config.sortable){
8233 c.html = '<i class="glyphicon"></i>' + c.html;
8236 // could use BS4 hidden-..-down
8238 if(typeof(config.lgHeader) != 'undefined'){
8239 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8242 if(typeof(config.mdHeader) != 'undefined'){
8243 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8246 if(typeof(config.smHeader) != 'undefined'){
8247 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8250 if(typeof(config.xsHeader) != 'undefined'){
8251 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258 if(typeof(config.tooltip) != 'undefined'){
8259 c.tooltip = config.tooltip;
8262 if(typeof(config.colspan) != 'undefined'){
8263 c.colspan = config.colspan;
8266 if(typeof(config.hidden) != 'undefined' && config.hidden){
8267 c.style += ' display:none;';
8270 if(typeof(config.dataIndex) != 'undefined'){
8271 c.sort = config.dataIndex;
8276 if(typeof(config.align) != 'undefined' && config.align.length){
8277 c.style += ' text-align:' + config.align + ';';
8280 if(typeof(config.width) != 'undefined'){
8281 c.style += ' width:' + config.width + 'px;';
8282 this.totalWidth += config.width;
8284 this.totalWidth += 100; // assume minimum of 100 per column?
8287 if(typeof(config.cls) != 'undefined'){
8288 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8291 ['xs','sm','md','lg'].map(function(size){
8293 if(typeof(config[size]) == 'undefined'){
8297 if (!config[size]) { // 0 = hidden
8298 // BS 4 '0' is treated as hide that column and below.
8299 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303 c.cls += ' col-' + size + '-' + config[size] + (
8304 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8316 renderBody : function()
8326 colspan : this.cm.getColumnCount()
8336 renderFooter : function()
8346 colspan : this.cm.getColumnCount()
8360 // Roo.log('ds onload');
8365 var ds = this.store;
8367 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8368 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8369 if (_this.store.sortInfo) {
8371 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8372 e.select('i', true).addClass(['glyphicon-arrow-up']);
8375 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8376 e.select('i', true).addClass(['glyphicon-arrow-down']);
8381 var tbody = this.mainBody;
8383 if(ds.getCount() > 0){
8384 ds.data.each(function(d,rowIndex){
8385 var row = this.renderRow(cm, ds, rowIndex);
8387 tbody.createChild(row);
8391 if(row.cellObjects.length){
8392 Roo.each(row.cellObjects, function(r){
8393 _this.renderCellObject(r);
8400 var tfoot = this.el.select('tfoot', true).first();
8402 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8404 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8406 var total = this.ds.getTotalCount();
8408 if(this.footer.pageSize < total){
8409 this.mainFoot.show();
8413 Roo.each(this.el.select('tbody td', true).elements, function(e){
8414 e.on('mouseover', _this.onMouseover, _this);
8417 Roo.each(this.el.select('tbody td', true).elements, function(e){
8418 e.on('mouseout', _this.onMouseout, _this);
8420 this.fireEvent('rowsrendered', this);
8426 onUpdate : function(ds,record)
8428 this.refreshRow(record);
8432 onRemove : function(ds, record, index, isUpdate){
8433 if(isUpdate !== true){
8434 this.fireEvent("beforerowremoved", this, index, record);
8436 var bt = this.mainBody.dom;
8438 var rows = this.el.select('tbody > tr', true).elements;
8440 if(typeof(rows[index]) != 'undefined'){
8441 bt.removeChild(rows[index].dom);
8444 // if(bt.rows[index]){
8445 // bt.removeChild(bt.rows[index]);
8448 if(isUpdate !== true){
8449 //this.stripeRows(index);
8450 //this.syncRowHeights(index, index);
8452 this.fireEvent("rowremoved", this, index, record);
8456 onAdd : function(ds, records, rowIndex)
8458 //Roo.log('on Add called');
8459 // - note this does not handle multiple adding very well..
8460 var bt = this.mainBody.dom;
8461 for (var i =0 ; i < records.length;i++) {
8462 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8463 //Roo.log(records[i]);
8464 //Roo.log(this.store.getAt(rowIndex+i));
8465 this.insertRow(this.store, rowIndex + i, false);
8472 refreshRow : function(record){
8473 var ds = this.store, index;
8474 if(typeof record == 'number'){
8476 record = ds.getAt(index);
8478 index = ds.indexOf(record);
8480 return; // should not happen - but seems to
8483 this.insertRow(ds, index, true);
8485 this.onRemove(ds, record, index+1, true);
8487 //this.syncRowHeights(index, index);
8489 this.fireEvent("rowupdated", this, index, record);
8492 insertRow : function(dm, rowIndex, isUpdate){
8495 this.fireEvent("beforerowsinserted", this, rowIndex);
8497 //var s = this.getScrollState();
8498 var row = this.renderRow(this.cm, this.store, rowIndex);
8499 // insert before rowIndex..
8500 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504 if(row.cellObjects.length){
8505 Roo.each(row.cellObjects, function(r){
8506 _this.renderCellObject(r);
8511 this.fireEvent("rowsinserted", this, rowIndex);
8512 //this.syncRowHeights(firstRow, lastRow);
8513 //this.stripeRows(firstRow);
8520 getRowDom : function(rowIndex)
8522 var rows = this.el.select('tbody > tr', true).elements;
8524 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8527 // returns the object tree for a tr..
8530 renderRow : function(cm, ds, rowIndex)
8532 var d = ds.getAt(rowIndex);
8536 cls : 'x-row-' + rowIndex,
8540 var cellObjects = [];
8542 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8543 var config = cm.config[i];
8545 var renderer = cm.getRenderer(i);
8549 if(typeof(renderer) !== 'undefined'){
8550 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8552 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8553 // and are rendered into the cells after the row is rendered - using the id for the element.
8555 if(typeof(value) === 'object'){
8565 rowIndex : rowIndex,
8570 this.fireEvent('rowclass', this, rowcfg);
8574 cls : rowcfg.rowClass + ' x-col-' + i,
8576 html: (typeof(value) === 'object') ? '' : value
8583 if(typeof(config.colspan) != 'undefined'){
8584 td.colspan = config.colspan;
8587 if(typeof(config.hidden) != 'undefined' && config.hidden){
8588 td.style += ' display:none;';
8591 if(typeof(config.align) != 'undefined' && config.align.length){
8592 td.style += ' text-align:' + config.align + ';';
8594 if(typeof(config.valign) != 'undefined' && config.valign.length){
8595 td.style += ' vertical-align:' + config.valign + ';';
8598 if(typeof(config.width) != 'undefined'){
8599 td.style += ' width:' + config.width + 'px;';
8602 if(typeof(config.cursor) != 'undefined'){
8603 td.style += ' cursor:' + config.cursor + ';';
8606 if(typeof(config.cls) != 'undefined'){
8607 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8610 ['xs','sm','md','lg'].map(function(size){
8612 if(typeof(config[size]) == 'undefined'){
8618 if (!config[size]) { // 0 = hidden
8619 // BS 4 '0' is treated as hide that column and below.
8620 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624 td.cls += ' col-' + size + '-' + config[size] + (
8625 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8635 row.cellObjects = cellObjects;
8643 onBeforeLoad : function()
8652 this.el.select('tbody', true).first().dom.innerHTML = '';
8655 * Show or hide a row.
8656 * @param {Number} rowIndex to show or hide
8657 * @param {Boolean} state hide
8659 setRowVisibility : function(rowIndex, state)
8661 var bt = this.mainBody.dom;
8663 var rows = this.el.select('tbody > tr', true).elements;
8665 if(typeof(rows[rowIndex]) == 'undefined'){
8668 rows[rowIndex].dom.style.display = state ? '' : 'none';
8672 getSelectionModel : function(){
8674 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8676 return this.selModel;
8679 * Render the Roo.bootstrap object from renderder
8681 renderCellObject : function(r)
8685 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8687 var t = r.cfg.render(r.container);
8690 Roo.each(r.cfg.cn, function(c){
8692 container: t.getChildContainer(),
8695 _this.renderCellObject(child);
8700 getRowIndex : function(row)
8704 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8715 * Returns the grid's underlying element = used by panel.Grid
8716 * @return {Element} The element
8718 getGridEl : function(){
8722 * Forces a resize - used by panel.Grid
8723 * @return {Element} The element
8725 autoSize : function()
8727 //var ctr = Roo.get(this.container.dom.parentElement);
8728 var ctr = Roo.get(this.el.dom);
8730 var thd = this.getGridEl().select('thead',true).first();
8731 var tbd = this.getGridEl().select('tbody', true).first();
8732 var tfd = this.getGridEl().select('tfoot', true).first();
8734 var cw = ctr.getWidth();
8735 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8739 tbd.setWidth(ctr.getWidth());
8740 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8741 // this needs fixing for various usage - currently only hydra job advers I think..
8743 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8745 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8748 cw = Math.max(cw, this.totalWidth);
8749 this.getGridEl().select('tbody tr',true).setWidth(cw);
8751 // resize 'expandable coloumn?
8753 return; // we doe not have a view in this design..
8756 onBodyScroll: function()
8758 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8760 this.mainHead.setStyle({
8761 'position' : 'relative',
8762 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8768 var scrollHeight = this.mainBody.dom.scrollHeight;
8770 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8772 var height = this.mainBody.getHeight();
8774 if(scrollHeight - height == scrollTop) {
8776 var total = this.ds.getTotalCount();
8778 if(this.footer.cursor + this.footer.pageSize < total){
8780 this.footer.ds.load({
8782 start : this.footer.cursor + this.footer.pageSize,
8783 limit : this.footer.pageSize
8793 onHeaderChange : function()
8795 var header = this.renderHeader();
8796 var table = this.el.select('table', true).first();
8798 this.mainHead.remove();
8799 this.mainHead = table.createChild(header, this.mainBody, false);
8802 onHiddenChange : function(colModel, colIndex, hidden)
8804 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8805 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8807 this.CSS.updateRule(thSelector, "display", "");
8808 this.CSS.updateRule(tdSelector, "display", "");
8811 this.CSS.updateRule(thSelector, "display", "none");
8812 this.CSS.updateRule(tdSelector, "display", "none");
8815 this.onHeaderChange();
8819 setColumnWidth: function(col_index, width)
8821 // width = "md-2 xs-2..."
8822 if(!this.colModel.config[col_index]) {
8826 var w = width.split(" ");
8828 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8830 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8833 for(var j = 0; j < w.length; j++) {
8839 var size_cls = w[j].split("-");
8841 if(!Number.isInteger(size_cls[1] * 1)) {
8845 if(!this.colModel.config[col_index][size_cls[0]]) {
8849 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853 h_row[0].classList.replace(
8854 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8855 "col-"+size_cls[0]+"-"+size_cls[1]
8858 for(var i = 0; i < rows.length; i++) {
8860 var size_cls = w[j].split("-");
8862 if(!Number.isInteger(size_cls[1] * 1)) {
8866 if(!this.colModel.config[col_index][size_cls[0]]) {
8870 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874 rows[i].classList.replace(
8875 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8876 "col-"+size_cls[0]+"-"+size_cls[1]
8880 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8895 * @class Roo.bootstrap.TableCell
8896 * @extends Roo.bootstrap.Component
8897 * Bootstrap TableCell class
8898 * @cfg {String} html cell contain text
8899 * @cfg {String} cls cell class
8900 * @cfg {String} tag cell tag (td|th) default td
8901 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8902 * @cfg {String} align Aligns the content in a cell
8903 * @cfg {String} axis Categorizes cells
8904 * @cfg {String} bgcolor Specifies the background color of a cell
8905 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8906 * @cfg {Number} colspan Specifies the number of columns a cell should span
8907 * @cfg {String} headers Specifies one or more header cells a cell is related to
8908 * @cfg {Number} height Sets the height of a cell
8909 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8910 * @cfg {Number} rowspan Sets the number of rows a cell should span
8911 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8912 * @cfg {String} valign Vertical aligns the content in a cell
8913 * @cfg {Number} width Specifies the width of a cell
8916 * Create a new TableCell
8917 * @param {Object} config The config object
8920 Roo.bootstrap.TableCell = function(config){
8921 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8924 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8944 getAutoCreate : function(){
8945 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8965 cfg.align=this.align
8971 cfg.bgcolor=this.bgcolor
8974 cfg.charoff=this.charoff
8977 cfg.colspan=this.colspan
8980 cfg.headers=this.headers
8983 cfg.height=this.height
8986 cfg.nowrap=this.nowrap
8989 cfg.rowspan=this.rowspan
8992 cfg.scope=this.scope
8995 cfg.valign=this.valign
8998 cfg.width=this.width
9017 * @class Roo.bootstrap.TableRow
9018 * @extends Roo.bootstrap.Component
9019 * Bootstrap TableRow class
9020 * @cfg {String} cls row class
9021 * @cfg {String} align Aligns the content in a table row
9022 * @cfg {String} bgcolor Specifies a background color for a table row
9023 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9024 * @cfg {String} valign Vertical aligns the content in a table row
9027 * Create a new TableRow
9028 * @param {Object} config The config object
9031 Roo.bootstrap.TableRow = function(config){
9032 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9035 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9043 getAutoCreate : function(){
9044 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9054 cfg.align = this.align;
9057 cfg.bgcolor = this.bgcolor;
9060 cfg.charoff = this.charoff;
9063 cfg.valign = this.valign;
9081 * @class Roo.bootstrap.TableBody
9082 * @extends Roo.bootstrap.Component
9083 * Bootstrap TableBody class
9084 * @cfg {String} cls element class
9085 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9086 * @cfg {String} align Aligns the content inside the element
9087 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9088 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9091 * Create a new TableBody
9092 * @param {Object} config The config object
9095 Roo.bootstrap.TableBody = function(config){
9096 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9099 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9107 getAutoCreate : function(){
9108 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9122 cfg.align = this.align;
9125 cfg.charoff = this.charoff;
9128 cfg.valign = this.valign;
9135 // initEvents : function()
9142 // this.store = Roo.factory(this.store, Roo.data);
9143 // this.store.on('load', this.onLoad, this);
9145 // this.store.load();
9149 // onLoad: function ()
9151 // this.fireEvent('load', this);
9161 * Ext JS Library 1.1.1
9162 * Copyright(c) 2006-2007, Ext JS, LLC.
9164 * Originally Released Under LGPL - original licence link has changed is not relivant.
9167 * <script type="text/javascript">
9170 // as we use this in bootstrap.
9171 Roo.namespace('Roo.form');
9173 * @class Roo.form.Action
9174 * Internal Class used to handle form actions
9176 * @param {Roo.form.BasicForm} el The form element or its id
9177 * @param {Object} config Configuration options
9182 // define the action interface
9183 Roo.form.Action = function(form, options){
9185 this.options = options || {};
9188 * Client Validation Failed
9191 Roo.form.Action.CLIENT_INVALID = 'client';
9193 * Server Validation Failed
9196 Roo.form.Action.SERVER_INVALID = 'server';
9198 * Connect to Server Failed
9201 Roo.form.Action.CONNECT_FAILURE = 'connect';
9203 * Reading Data from Server Failed
9206 Roo.form.Action.LOAD_FAILURE = 'load';
9208 Roo.form.Action.prototype = {
9210 failureType : undefined,
9211 response : undefined,
9215 run : function(options){
9220 success : function(response){
9225 handleResponse : function(response){
9229 // default connection failure
9230 failure : function(response){
9232 this.response = response;
9233 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9234 this.form.afterAction(this, false);
9237 processResponse : function(response){
9238 this.response = response;
9239 if(!response.responseText){
9242 this.result = this.handleResponse(response);
9246 // utility functions used internally
9247 getUrl : function(appendParams){
9248 var url = this.options.url || this.form.url || this.form.el.dom.action;
9250 var p = this.getParams();
9252 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9258 getMethod : function(){
9259 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9262 getParams : function(){
9263 var bp = this.form.baseParams;
9264 var p = this.options.params;
9266 if(typeof p == "object"){
9267 p = Roo.urlEncode(Roo.applyIf(p, bp));
9268 }else if(typeof p == 'string' && bp){
9269 p += '&' + Roo.urlEncode(bp);
9272 p = Roo.urlEncode(bp);
9277 createCallback : function(){
9279 success: this.success,
9280 failure: this.failure,
9282 timeout: (this.form.timeout*1000),
9283 upload: this.form.fileUpload ? this.success : undefined
9288 Roo.form.Action.Submit = function(form, options){
9289 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9292 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9295 haveProgress : false,
9296 uploadComplete : false,
9298 // uploadProgress indicator.
9299 uploadProgress : function()
9301 if (!this.form.progressUrl) {
9305 if (!this.haveProgress) {
9306 Roo.MessageBox.progress("Uploading", "Uploading");
9308 if (this.uploadComplete) {
9309 Roo.MessageBox.hide();
9313 this.haveProgress = true;
9315 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9317 var c = new Roo.data.Connection();
9319 url : this.form.progressUrl,
9324 success : function(req){
9325 //console.log(data);
9329 rdata = Roo.decode(req.responseText)
9331 Roo.log("Invalid data from server..");
9335 if (!rdata || !rdata.success) {
9337 Roo.MessageBox.alert(Roo.encode(rdata));
9340 var data = rdata.data;
9342 if (this.uploadComplete) {
9343 Roo.MessageBox.hide();
9348 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9349 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9352 this.uploadProgress.defer(2000,this);
9355 failure: function(data) {
9356 Roo.log('progress url failed ');
9367 // run get Values on the form, so it syncs any secondary forms.
9368 this.form.getValues();
9370 var o = this.options;
9371 var method = this.getMethod();
9372 var isPost = method == 'POST';
9373 if(o.clientValidation === false || this.form.isValid()){
9375 if (this.form.progressUrl) {
9376 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9377 (new Date() * 1) + '' + Math.random());
9382 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9383 form:this.form.el.dom,
9384 url:this.getUrl(!isPost),
9386 params:isPost ? this.getParams() : null,
9387 isUpload: this.form.fileUpload,
9388 formData : this.form.formData
9391 this.uploadProgress();
9393 }else if (o.clientValidation !== false){ // client validation failed
9394 this.failureType = Roo.form.Action.CLIENT_INVALID;
9395 this.form.afterAction(this, false);
9399 success : function(response)
9401 this.uploadComplete= true;
9402 if (this.haveProgress) {
9403 Roo.MessageBox.hide();
9407 var result = this.processResponse(response);
9408 if(result === true || result.success){
9409 this.form.afterAction(this, true);
9413 this.form.markInvalid(result.errors);
9414 this.failureType = Roo.form.Action.SERVER_INVALID;
9416 this.form.afterAction(this, false);
9418 failure : function(response)
9420 this.uploadComplete= true;
9421 if (this.haveProgress) {
9422 Roo.MessageBox.hide();
9425 this.response = response;
9426 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9427 this.form.afterAction(this, false);
9430 handleResponse : function(response){
9431 if(this.form.errorReader){
9432 var rs = this.form.errorReader.read(response);
9435 for(var i = 0, len = rs.records.length; i < len; i++) {
9436 var r = rs.records[i];
9440 if(errors.length < 1){
9444 success : rs.success,
9450 ret = Roo.decode(response.responseText);
9454 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9464 Roo.form.Action.Load = function(form, options){
9465 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9466 this.reader = this.form.reader;
9469 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9474 Roo.Ajax.request(Roo.apply(
9475 this.createCallback(), {
9476 method:this.getMethod(),
9477 url:this.getUrl(false),
9478 params:this.getParams()
9482 success : function(response){
9484 var result = this.processResponse(response);
9485 if(result === true || !result.success || !result.data){
9486 this.failureType = Roo.form.Action.LOAD_FAILURE;
9487 this.form.afterAction(this, false);
9490 this.form.clearInvalid();
9491 this.form.setValues(result.data);
9492 this.form.afterAction(this, true);
9495 handleResponse : function(response){
9496 if(this.form.reader){
9497 var rs = this.form.reader.read(response);
9498 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9500 success : rs.success,
9504 return Roo.decode(response.responseText);
9508 Roo.form.Action.ACTION_TYPES = {
9509 'load' : Roo.form.Action.Load,
9510 'submit' : Roo.form.Action.Submit
9519 * @class Roo.bootstrap.Form
9520 * @extends Roo.bootstrap.Component
9521 * Bootstrap Form class
9522 * @cfg {String} method GET | POST (default POST)
9523 * @cfg {String} labelAlign top | left (default top)
9524 * @cfg {String} align left | right - for navbars
9525 * @cfg {Boolean} loadMask load mask when submit (default true)
9530 * @param {Object} config The config object
9534 Roo.bootstrap.Form = function(config){
9536 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9538 Roo.bootstrap.Form.popover.apply();
9542 * @event clientvalidation
9543 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9544 * @param {Form} this
9545 * @param {Boolean} valid true if the form has passed client-side validation
9547 clientvalidation: true,
9549 * @event beforeaction
9550 * Fires before any action is performed. Return false to cancel the action.
9551 * @param {Form} this
9552 * @param {Action} action The action to be performed
9556 * @event actionfailed
9557 * Fires when an action fails.
9558 * @param {Form} this
9559 * @param {Action} action The action that failed
9561 actionfailed : true,
9563 * @event actioncomplete
9564 * Fires when an action is completed.
9565 * @param {Form} this
9566 * @param {Action} action The action that completed
9568 actioncomplete : true
9572 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9575 * @cfg {String} method
9576 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9581 * The URL to use for form actions if one isn't supplied in the action options.
9584 * @cfg {Boolean} fileUpload
9585 * Set to true if this form is a file upload.
9589 * @cfg {Object} baseParams
9590 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598 * @cfg {Sting} align (left|right) for navbar forms
9603 activeAction : null,
9606 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9607 * element by passing it or its id or mask the form itself by passing in true.
9610 waitMsgTarget : false,
9615 * @cfg {Boolean} errorMask (true|false) default false
9620 * @cfg {Number} maskOffset Default 100
9625 * @cfg {Boolean} maskBody
9629 getAutoCreate : function(){
9633 method : this.method || 'POST',
9634 id : this.id || Roo.id(),
9637 if (this.parent().xtype.match(/^Nav/)) {
9638 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642 if (this.labelAlign == 'left' ) {
9643 cfg.cls += ' form-horizontal';
9649 initEvents : function()
9651 this.el.on('submit', this.onSubmit, this);
9652 // this was added as random key presses on the form where triggering form submit.
9653 this.el.on('keypress', function(e) {
9654 if (e.getCharCode() != 13) {
9657 // we might need to allow it for textareas.. and some other items.
9658 // check e.getTarget().
9660 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664 Roo.log("keypress blocked");
9672 onSubmit : function(e){
9677 * Returns true if client-side validation on the form is successful.
9680 isValid : function(){
9681 var items = this.getItems();
9685 items.each(function(f){
9691 Roo.log('invalid field: ' + f.name);
9695 if(!target && f.el.isVisible(true)){
9701 if(this.errorMask && !valid){
9702 Roo.bootstrap.Form.popover.mask(this, target);
9709 * Returns true if any fields in this form have changed since their original load.
9712 isDirty : function(){
9714 var items = this.getItems();
9715 items.each(function(f){
9725 * Performs a predefined action (submit or load) or custom actions you define on this form.
9726 * @param {String} actionName The name of the action type
9727 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9728 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9729 * accept other config options):
9731 Property Type Description
9732 ---------------- --------------- ----------------------------------------------------------------------------------
9733 url String The url for the action (defaults to the form's url)
9734 method String The form method to use (defaults to the form's method, or POST if not defined)
9735 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9736 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9737 validate the form on the client (defaults to false)
9739 * @return {BasicForm} this
9741 doAction : function(action, options){
9742 if(typeof action == 'string'){
9743 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9745 if(this.fireEvent('beforeaction', this, action) !== false){
9746 this.beforeAction(action);
9747 action.run.defer(100, action);
9753 beforeAction : function(action){
9754 var o = action.options;
9759 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9761 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9764 // not really supported yet.. ??
9766 //if(this.waitMsgTarget === true){
9767 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768 //}else if(this.waitMsgTarget){
9769 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9770 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9772 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9778 afterAction : function(action, success){
9779 this.activeAction = null;
9780 var o = action.options;
9785 Roo.get(document.body).unmask();
9791 //if(this.waitMsgTarget === true){
9792 // this.el.unmask();
9793 //}else if(this.waitMsgTarget){
9794 // this.waitMsgTarget.unmask();
9796 // Roo.MessageBox.updateProgress(1);
9797 // Roo.MessageBox.hide();
9804 Roo.callback(o.success, o.scope, [this, action]);
9805 this.fireEvent('actioncomplete', this, action);
9809 // failure condition..
9810 // we have a scenario where updates need confirming.
9811 // eg. if a locking scenario exists..
9812 // we look for { errors : { needs_confirm : true }} in the response.
9814 (typeof(action.result) != 'undefined') &&
9815 (typeof(action.result.errors) != 'undefined') &&
9816 (typeof(action.result.errors.needs_confirm) != 'undefined')
9819 Roo.log("not supported yet");
9822 Roo.MessageBox.confirm(
9823 "Change requires confirmation",
9824 action.result.errorMsg,
9829 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9839 Roo.callback(o.failure, o.scope, [this, action]);
9840 // show an error message if no failed handler is set..
9841 if (!this.hasListener('actionfailed')) {
9842 Roo.log("need to add dialog support");
9844 Roo.MessageBox.alert("Error",
9845 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9846 action.result.errorMsg :
9847 "Saving Failed, please check your entries or try again"
9852 this.fireEvent('actionfailed', this, action);
9857 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9858 * @param {String} id The value to search for
9861 findField : function(id){
9862 var items = this.getItems();
9863 var field = items.get(id);
9865 items.each(function(f){
9866 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873 return field || null;
9876 * Mark fields in this form invalid in bulk.
9877 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9878 * @return {BasicForm} this
9880 markInvalid : function(errors){
9881 if(errors instanceof Array){
9882 for(var i = 0, len = errors.length; i < len; i++){
9883 var fieldError = errors[i];
9884 var f = this.findField(fieldError.id);
9886 f.markInvalid(fieldError.msg);
9892 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9893 field.markInvalid(errors[id]);
9897 //Roo.each(this.childForms || [], function (f) {
9898 // f.markInvalid(errors);
9905 * Set values for fields in this form in bulk.
9906 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9907 * @return {BasicForm} this
9909 setValues : function(values){
9910 if(values instanceof Array){ // array of objects
9911 for(var i = 0, len = values.length; i < len; i++){
9913 var f = this.findField(v.id);
9915 f.setValue(v.value);
9916 if(this.trackResetOnLoad){
9917 f.originalValue = f.getValue();
9921 }else{ // object hash
9924 if(typeof values[id] != 'function' && (field = this.findField(id))){
9926 if (field.setFromData &&
9928 field.displayField &&
9929 // combos' with local stores can
9930 // be queried via setValue()
9931 // to set their value..
9932 (field.store && !field.store.isLocal)
9936 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9937 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9938 field.setFromData(sd);
9940 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9942 field.setFromData(values);
9945 field.setValue(values[id]);
9949 if(this.trackResetOnLoad){
9950 field.originalValue = field.getValue();
9956 //Roo.each(this.childForms || [], function (f) {
9957 // f.setValues(values);
9964 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9965 * they are returned as an array.
9966 * @param {Boolean} asString
9969 getValues : function(asString){
9970 //if (this.childForms) {
9971 // copy values from the child forms
9972 // Roo.each(this.childForms, function (f) {
9973 // this.setValues(f.getValues());
9979 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9980 if(asString === true){
9983 return Roo.urlDecode(fs);
9987 * Returns the fields in this form as an object with key/value pairs.
9988 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9991 getFieldValues : function(with_hidden)
9993 var items = this.getItems();
9995 items.each(function(f){
10001 var v = f.getValue();
10003 if (f.inputType =='radio') {
10004 if (typeof(ret[f.getName()]) == 'undefined') {
10005 ret[f.getName()] = ''; // empty..
10008 if (!f.el.dom.checked) {
10012 v = f.el.dom.value;
10016 if(f.xtype == 'MoneyField'){
10017 ret[f.currencyName] = f.getCurrency();
10020 // not sure if this supported any more..
10021 if ((typeof(v) == 'object') && f.getRawValue) {
10022 v = f.getRawValue() ; // dates..
10024 // combo boxes where name != hiddenName...
10025 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10026 ret[f.name] = f.getRawValue();
10028 ret[f.getName()] = v;
10035 * Clears all invalid messages in this form.
10036 * @return {BasicForm} this
10038 clearInvalid : function(){
10039 var items = this.getItems();
10041 items.each(function(f){
10049 * Resets this form.
10050 * @return {BasicForm} this
10052 reset : function(){
10053 var items = this.getItems();
10054 items.each(function(f){
10058 Roo.each(this.childForms || [], function (f) {
10066 getItems : function()
10068 var r=new Roo.util.MixedCollection(false, function(o){
10069 return o.id || (o.id = Roo.id());
10071 var iter = function(el) {
10078 Roo.each(el.items,function(e) {
10087 hideFields : function(items)
10089 Roo.each(items, function(i){
10091 var f = this.findField(i);
10102 showFields : function(items)
10104 Roo.each(items, function(i){
10106 var f = this.findField(i);
10119 Roo.apply(Roo.bootstrap.Form, {
10135 intervalID : false,
10141 if(this.isApplied){
10146 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10147 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10148 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10149 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10152 this.maskEl.top.enableDisplayMode("block");
10153 this.maskEl.left.enableDisplayMode("block");
10154 this.maskEl.bottom.enableDisplayMode("block");
10155 this.maskEl.right.enableDisplayMode("block");
10157 this.toolTip = new Roo.bootstrap.Tooltip({
10158 cls : 'roo-form-error-popover',
10160 'left' : ['r-l', [-2,0], 'right'],
10161 'right' : ['l-r', [2,0], 'left'],
10162 'bottom' : ['tl-bl', [0,2], 'top'],
10163 'top' : [ 'bl-tl', [0,-2], 'bottom']
10167 this.toolTip.render(Roo.get(document.body));
10169 this.toolTip.el.enableDisplayMode("block");
10171 Roo.get(document.body).on('click', function(){
10175 Roo.get(document.body).on('touchstart', function(){
10179 this.isApplied = true
10182 mask : function(form, target)
10186 this.target = target;
10188 if(!this.form.errorMask || !target.el){
10192 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10194 Roo.log(scrollable);
10196 var ot = this.target.el.calcOffsetsTo(scrollable);
10198 var scrollTo = ot[1] - this.form.maskOffset;
10200 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10202 scrollable.scrollTo('top', scrollTo);
10204 var box = this.target.el.getBox();
10206 var zIndex = Roo.bootstrap.Modal.zIndex++;
10209 this.maskEl.top.setStyle('position', 'absolute');
10210 this.maskEl.top.setStyle('z-index', zIndex);
10211 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10212 this.maskEl.top.setLeft(0);
10213 this.maskEl.top.setTop(0);
10214 this.maskEl.top.show();
10216 this.maskEl.left.setStyle('position', 'absolute');
10217 this.maskEl.left.setStyle('z-index', zIndex);
10218 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10219 this.maskEl.left.setLeft(0);
10220 this.maskEl.left.setTop(box.y - this.padding);
10221 this.maskEl.left.show();
10223 this.maskEl.bottom.setStyle('position', 'absolute');
10224 this.maskEl.bottom.setStyle('z-index', zIndex);
10225 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10226 this.maskEl.bottom.setLeft(0);
10227 this.maskEl.bottom.setTop(box.bottom + this.padding);
10228 this.maskEl.bottom.show();
10230 this.maskEl.right.setStyle('position', 'absolute');
10231 this.maskEl.right.setStyle('z-index', zIndex);
10232 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10233 this.maskEl.right.setLeft(box.right + this.padding);
10234 this.maskEl.right.setTop(box.y - this.padding);
10235 this.maskEl.right.show();
10237 this.toolTip.bindEl = this.target.el;
10239 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10241 var tip = this.target.blankText;
10243 if(this.target.getValue() !== '' ) {
10245 if (this.target.invalidText.length) {
10246 tip = this.target.invalidText;
10247 } else if (this.target.regexText.length){
10248 tip = this.target.regexText;
10252 this.toolTip.show(tip);
10254 this.intervalID = window.setInterval(function() {
10255 Roo.bootstrap.Form.popover.unmask();
10258 window.onwheel = function(){ return false;};
10260 (function(){ this.isMasked = true; }).defer(500, this);
10264 unmask : function()
10266 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270 this.maskEl.top.setStyle('position', 'absolute');
10271 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10272 this.maskEl.top.hide();
10274 this.maskEl.left.setStyle('position', 'absolute');
10275 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10276 this.maskEl.left.hide();
10278 this.maskEl.bottom.setStyle('position', 'absolute');
10279 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10280 this.maskEl.bottom.hide();
10282 this.maskEl.right.setStyle('position', 'absolute');
10283 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10284 this.maskEl.right.hide();
10286 this.toolTip.hide();
10288 this.toolTip.el.hide();
10290 window.onwheel = function(){ return true;};
10292 if(this.intervalID){
10293 window.clearInterval(this.intervalID);
10294 this.intervalID = false;
10297 this.isMasked = false;
10307 * Ext JS Library 1.1.1
10308 * Copyright(c) 2006-2007, Ext JS, LLC.
10310 * Originally Released Under LGPL - original licence link has changed is not relivant.
10313 * <script type="text/javascript">
10316 * @class Roo.form.VTypes
10317 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10320 Roo.form.VTypes = function(){
10321 // closure these in so they are only created once.
10322 var alpha = /^[a-zA-Z_]+$/;
10323 var alphanum = /^[a-zA-Z0-9_]+$/;
10324 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10325 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10327 // All these messages and functions are configurable
10330 * The function used to validate email addresses
10331 * @param {String} value The email address
10333 'email' : function(v){
10334 return email.test(v);
10337 * The error text to display when the email validation function returns false
10340 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10342 * The keystroke filter mask to be applied on email input
10345 'emailMask' : /[a-z0-9_\.\-@]/i,
10348 * The function used to validate URLs
10349 * @param {String} value The URL
10351 'url' : function(v){
10352 return url.test(v);
10355 * The error text to display when the url validation function returns false
10358 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10361 * The function used to validate alpha values
10362 * @param {String} value The value
10364 'alpha' : function(v){
10365 return alpha.test(v);
10368 * The error text to display when the alpha validation function returns false
10371 'alphaText' : 'This field should only contain letters and _',
10373 * The keystroke filter mask to be applied on alpha input
10376 'alphaMask' : /[a-z_]/i,
10379 * The function used to validate alphanumeric values
10380 * @param {String} value The value
10382 'alphanum' : function(v){
10383 return alphanum.test(v);
10386 * The error text to display when the alphanumeric validation function returns false
10389 'alphanumText' : 'This field should only contain letters, numbers and _',
10391 * The keystroke filter mask to be applied on alphanumeric input
10394 'alphanumMask' : /[a-z0-9_]/i
10404 * @class Roo.bootstrap.Input
10405 * @extends Roo.bootstrap.Component
10406 * Bootstrap Input class
10407 * @cfg {Boolean} disabled is it disabled
10408 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10409 * @cfg {String} name name of the input
10410 * @cfg {string} fieldLabel - the label associated
10411 * @cfg {string} placeholder - placeholder to put in text.
10412 * @cfg {string} before - input group add on before
10413 * @cfg {string} after - input group add on after
10414 * @cfg {string} size - (lg|sm) or leave empty..
10415 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10416 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10417 * @cfg {Number} md colspan out of 12 for computer-sized screens
10418 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10419 * @cfg {string} value default value of the input
10420 * @cfg {Number} labelWidth set the width of label
10421 * @cfg {Number} labellg set the width of label (1-12)
10422 * @cfg {Number} labelmd set the width of label (1-12)
10423 * @cfg {Number} labelsm set the width of label (1-12)
10424 * @cfg {Number} labelxs set the width of label (1-12)
10425 * @cfg {String} labelAlign (top|left)
10426 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10427 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10428 * @cfg {String} indicatorpos (left|right) default left
10429 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10430 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10431 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10433 * @cfg {String} align (left|center|right) Default left
10434 * @cfg {Boolean} forceFeedback (true|false) Default false
10437 * Create a new Input
10438 * @param {Object} config The config object
10441 Roo.bootstrap.Input = function(config){
10443 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10448 * Fires when this field receives input focus.
10449 * @param {Roo.form.Field} this
10454 * Fires when this field loses input focus.
10455 * @param {Roo.form.Field} this
10459 * @event specialkey
10460 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10461 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10462 * @param {Roo.form.Field} this
10463 * @param {Roo.EventObject} e The event object
10468 * Fires just before the field blurs if the field value has changed.
10469 * @param {Roo.form.Field} this
10470 * @param {Mixed} newValue The new value
10471 * @param {Mixed} oldValue The original value
10476 * Fires after the field has been marked as invalid.
10477 * @param {Roo.form.Field} this
10478 * @param {String} msg The validation message
10483 * Fires after the field has been validated with no errors.
10484 * @param {Roo.form.Field} this
10489 * Fires after the key up
10490 * @param {Roo.form.Field} this
10491 * @param {Roo.EventObject} e The event Object
10497 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10499 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10500 automatic validation (defaults to "keyup").
10502 validationEvent : "keyup",
10504 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10506 validateOnBlur : true,
10508 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10510 validationDelay : 250,
10512 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10514 focusClass : "x-form-focus", // not needed???
10518 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10520 invalidClass : "has-warning",
10523 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525 validClass : "has-success",
10528 * @cfg {Boolean} hasFeedback (true|false) default true
10530 hasFeedback : true,
10533 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10535 invalidFeedbackClass : "glyphicon-warning-sign",
10538 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540 validFeedbackClass : "glyphicon-ok",
10543 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10545 selectOnFocus : false,
10548 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10557 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10559 disableKeyFilter : false,
10562 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10572 blankText : "Please complete this mandatory field",
10575 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10581 maxLength : Number.MAX_VALUE,
10583 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10585 minLengthText : "The minimum length for this field is {0}",
10587 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10589 maxLengthText : "The maximum length for this field is {0}",
10593 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10594 * If available, this function will be called only after the basic validators all return true, and will be passed the
10595 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10600 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10601 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10605 * @cfg {String} regexText -- Depricated - use Invalid Text
10610 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10616 autocomplete: false,
10620 inputType : 'text',
10623 placeholder: false,
10628 preventMark: false,
10629 isFormField : true,
10632 labelAlign : false,
10635 formatedValue : false,
10636 forceFeedback : false,
10638 indicatorpos : 'left',
10648 parentLabelAlign : function()
10651 while (parent.parent()) {
10652 parent = parent.parent();
10653 if (typeof(parent.labelAlign) !='undefined') {
10654 return parent.labelAlign;
10661 getAutoCreate : function()
10663 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10669 if(this.inputType != 'hidden'){
10670 cfg.cls = 'form-group' //input-group
10676 type : this.inputType,
10677 value : this.value,
10678 cls : 'form-control',
10679 placeholder : this.placeholder || '',
10680 autocomplete : this.autocomplete || 'new-password'
10682 if (this.inputType == 'file') {
10683 input.style = 'overflow:hidden'; // why not in CSS?
10686 if(this.capture.length){
10687 input.capture = this.capture;
10690 if(this.accept.length){
10691 input.accept = this.accept + "/*";
10695 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10698 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10699 input.maxLength = this.maxLength;
10702 if (this.disabled) {
10703 input.disabled=true;
10706 if (this.readOnly) {
10707 input.readonly=true;
10711 input.name = this.name;
10715 input.cls += ' input-' + this.size;
10719 ['xs','sm','md','lg'].map(function(size){
10720 if (settings[size]) {
10721 cfg.cls += ' col-' + size + '-' + settings[size];
10725 var inputblock = input;
10729 cls: 'glyphicon form-control-feedback'
10732 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10735 cls : 'has-feedback',
10743 if (this.before || this.after) {
10746 cls : 'input-group',
10750 if (this.before && typeof(this.before) == 'string') {
10752 inputblock.cn.push({
10754 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758 if (this.before && typeof(this.before) == 'object') {
10759 this.before = Roo.factory(this.before);
10761 inputblock.cn.push({
10763 cls : 'roo-input-before input-group-prepend input-group-' +
10764 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10768 inputblock.cn.push(input);
10770 if (this.after && typeof(this.after) == 'string') {
10771 inputblock.cn.push({
10773 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777 if (this.after && typeof(this.after) == 'object') {
10778 this.after = Roo.factory(this.after);
10780 inputblock.cn.push({
10782 cls : 'roo-input-after input-group-append input-group-' +
10783 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10788 inputblock.cls += ' has-feedback';
10789 inputblock.cn.push(feedback);
10794 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10795 tooltip : 'This field is required'
10797 if (this.allowBlank ) {
10798 indicator.style = this.allowBlank ? ' display:none' : '';
10800 if (align ==='left' && this.fieldLabel.length) {
10802 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10809 cls : 'control-label col-form-label',
10810 html : this.fieldLabel
10821 var labelCfg = cfg.cn[1];
10822 var contentCfg = cfg.cn[2];
10824 if(this.indicatorpos == 'right'){
10829 cls : 'control-label col-form-label',
10833 html : this.fieldLabel
10847 labelCfg = cfg.cn[0];
10848 contentCfg = cfg.cn[1];
10852 if(this.labelWidth > 12){
10853 labelCfg.style = "width: " + this.labelWidth + 'px';
10856 if(this.labelWidth < 13 && this.labelmd == 0){
10857 this.labelmd = this.labelWidth;
10860 if(this.labellg > 0){
10861 labelCfg.cls += ' col-lg-' + this.labellg;
10862 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10865 if(this.labelmd > 0){
10866 labelCfg.cls += ' col-md-' + this.labelmd;
10867 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10870 if(this.labelsm > 0){
10871 labelCfg.cls += ' col-sm-' + this.labelsm;
10872 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10875 if(this.labelxs > 0){
10876 labelCfg.cls += ' col-xs-' + this.labelxs;
10877 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881 } else if ( this.fieldLabel.length) {
10888 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10889 tooltip : 'This field is required',
10890 style : this.allowBlank ? ' display:none' : ''
10894 //cls : 'input-group-addon',
10895 html : this.fieldLabel
10903 if(this.indicatorpos == 'right'){
10908 //cls : 'input-group-addon',
10909 html : this.fieldLabel
10914 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10915 tooltip : 'This field is required',
10916 style : this.allowBlank ? ' display:none' : ''
10936 if (this.parentType === 'Navbar' && this.parent().bar) {
10937 cfg.cls += ' navbar-form';
10940 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10941 // on BS4 we do this only if not form
10942 cfg.cls += ' navbar-form';
10950 * return the real input element.
10952 inputEl: function ()
10954 return this.el.select('input.form-control',true).first();
10957 tooltipEl : function()
10959 return this.inputEl();
10962 indicatorEl : function()
10964 if (Roo.bootstrap.version == 4) {
10965 return false; // not enabled in v4 yet.
10968 var indicator = this.el.select('i.roo-required-indicator',true).first();
10978 setDisabled : function(v)
10980 var i = this.inputEl().dom;
10982 i.removeAttribute('disabled');
10986 i.setAttribute('disabled','true');
10988 initEvents : function()
10991 this.inputEl().on("keydown" , this.fireKey, this);
10992 this.inputEl().on("focus", this.onFocus, this);
10993 this.inputEl().on("blur", this.onBlur, this);
10995 this.inputEl().relayEvent('keyup', this);
10997 this.indicator = this.indicatorEl();
10999 if(this.indicator){
11000 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11003 // reference to original value for reset
11004 this.originalValue = this.getValue();
11005 //Roo.form.TextField.superclass.initEvents.call(this);
11006 if(this.validationEvent == 'keyup'){
11007 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11008 this.inputEl().on('keyup', this.filterValidation, this);
11010 else if(this.validationEvent !== false){
11011 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11014 if(this.selectOnFocus){
11015 this.on("focus", this.preFocus, this);
11018 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11019 this.inputEl().on("keypress", this.filterKeys, this);
11021 this.inputEl().relayEvent('keypress', this);
11024 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11025 this.el.on("click", this.autoSize, this);
11028 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11029 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11032 if (typeof(this.before) == 'object') {
11033 this.before.render(this.el.select('.roo-input-before',true).first());
11035 if (typeof(this.after) == 'object') {
11036 this.after.render(this.el.select('.roo-input-after',true).first());
11039 this.inputEl().on('change', this.onChange, this);
11042 filterValidation : function(e){
11043 if(!e.isNavKeyPress()){
11044 this.validationTask.delay(this.validationDelay);
11048 * Validates the field value
11049 * @return {Boolean} True if the value is valid, else false
11051 validate : function(){
11052 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11053 if(this.disabled || this.validateValue(this.getRawValue())){
11058 this.markInvalid();
11064 * Validates a value according to the field's validation rules and marks the field as invalid
11065 * if the validation fails
11066 * @param {Mixed} value The value to validate
11067 * @return {Boolean} True if the value is valid, else false
11069 validateValue : function(value)
11071 if(this.getVisibilityEl().hasClass('hidden')){
11075 if(value.length < 1) { // if it's blank
11076 if(this.allowBlank){
11082 if(value.length < this.minLength){
11085 if(value.length > this.maxLength){
11089 var vt = Roo.form.VTypes;
11090 if(!vt[this.vtype](value, this)){
11094 if(typeof this.validator == "function"){
11095 var msg = this.validator(value);
11099 if (typeof(msg) == 'string') {
11100 this.invalidText = msg;
11104 if(this.regex && !this.regex.test(value)){
11112 fireKey : function(e){
11113 //Roo.log('field ' + e.getKey());
11114 if(e.isNavKeyPress()){
11115 this.fireEvent("specialkey", this, e);
11118 focus : function (selectText){
11120 this.inputEl().focus();
11121 if(selectText === true){
11122 this.inputEl().dom.select();
11128 onFocus : function(){
11129 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11130 // this.el.addClass(this.focusClass);
11132 if(!this.hasFocus){
11133 this.hasFocus = true;
11134 this.startValue = this.getValue();
11135 this.fireEvent("focus", this);
11139 beforeBlur : Roo.emptyFn,
11143 onBlur : function(){
11145 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11146 //this.el.removeClass(this.focusClass);
11148 this.hasFocus = false;
11149 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11152 var v = this.getValue();
11153 if(String(v) !== String(this.startValue)){
11154 this.fireEvent('change', this, v, this.startValue);
11156 this.fireEvent("blur", this);
11159 onChange : function(e)
11161 var v = this.getValue();
11162 if(String(v) !== String(this.startValue)){
11163 this.fireEvent('change', this, v, this.startValue);
11169 * Resets the current field value to the originally loaded value and clears any validation messages
11171 reset : function(){
11172 this.setValue(this.originalValue);
11176 * Returns the name of the field
11177 * @return {Mixed} name The name field
11179 getName: function(){
11183 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11184 * @return {Mixed} value The field value
11186 getValue : function(){
11188 var v = this.inputEl().getValue();
11193 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11194 * @return {Mixed} value The field value
11196 getRawValue : function(){
11197 var v = this.inputEl().getValue();
11203 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11204 * @param {Mixed} value The value to set
11206 setRawValue : function(v){
11207 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11210 selectText : function(start, end){
11211 var v = this.getRawValue();
11213 start = start === undefined ? 0 : start;
11214 end = end === undefined ? v.length : end;
11215 var d = this.inputEl().dom;
11216 if(d.setSelectionRange){
11217 d.setSelectionRange(start, end);
11218 }else if(d.createTextRange){
11219 var range = d.createTextRange();
11220 range.moveStart("character", start);
11221 range.moveEnd("character", v.length-end);
11228 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11229 * @param {Mixed} value The value to set
11231 setValue : function(v){
11234 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11240 processValue : function(value){
11241 if(this.stripCharsRe){
11242 var newValue = value.replace(this.stripCharsRe, '');
11243 if(newValue !== value){
11244 this.setRawValue(newValue);
11251 preFocus : function(){
11253 if(this.selectOnFocus){
11254 this.inputEl().dom.select();
11257 filterKeys : function(e){
11258 var k = e.getKey();
11259 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11262 var c = e.getCharCode(), cc = String.fromCharCode(c);
11263 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11266 if(!this.maskRe.test(cc)){
11271 * Clear any invalid styles/messages for this field
11273 clearInvalid : function(){
11275 if(!this.el || this.preventMark){ // not rendered
11280 this.el.removeClass([this.invalidClass, 'is-invalid']);
11282 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11284 var feedback = this.el.select('.form-control-feedback', true).first();
11287 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11292 if(this.indicator){
11293 this.indicator.removeClass('visible');
11294 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11297 this.fireEvent('valid', this);
11301 * Mark this field as valid
11303 markValid : function()
11305 if(!this.el || this.preventMark){ // not rendered...
11309 this.el.removeClass([this.invalidClass, this.validClass]);
11310 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11312 var feedback = this.el.select('.form-control-feedback', true).first();
11315 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11318 if(this.indicator){
11319 this.indicator.removeClass('visible');
11320 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11328 if(this.allowBlank && !this.getRawValue().length){
11331 if (Roo.bootstrap.version == 3) {
11332 this.el.addClass(this.validClass);
11334 this.inputEl().addClass('is-valid');
11337 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11339 var feedback = this.el.select('.form-control-feedback', true).first();
11342 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11343 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11348 this.fireEvent('valid', this);
11352 * Mark this field as invalid
11353 * @param {String} msg The validation message
11355 markInvalid : function(msg)
11357 if(!this.el || this.preventMark){ // not rendered
11361 this.el.removeClass([this.invalidClass, this.validClass]);
11362 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11364 var feedback = this.el.select('.form-control-feedback', true).first();
11367 this.el.select('.form-control-feedback', true).first().removeClass(
11368 [this.invalidFeedbackClass, this.validFeedbackClass]);
11375 if(this.allowBlank && !this.getRawValue().length){
11379 if(this.indicator){
11380 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11381 this.indicator.addClass('visible');
11383 if (Roo.bootstrap.version == 3) {
11384 this.el.addClass(this.invalidClass);
11386 this.inputEl().addClass('is-invalid');
11391 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11393 var feedback = this.el.select('.form-control-feedback', true).first();
11396 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11398 if(this.getValue().length || this.forceFeedback){
11399 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406 this.fireEvent('invalid', this, msg);
11409 SafariOnKeyDown : function(event)
11411 // this is a workaround for a password hang bug on chrome/ webkit.
11412 if (this.inputEl().dom.type != 'password') {
11416 var isSelectAll = false;
11418 if(this.inputEl().dom.selectionEnd > 0){
11419 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11421 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11422 event.preventDefault();
11427 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11429 event.preventDefault();
11430 // this is very hacky as keydown always get's upper case.
11432 var cc = String.fromCharCode(event.getCharCode());
11433 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11437 adjustWidth : function(tag, w){
11438 tag = tag.toLowerCase();
11439 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11440 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11441 if(tag == 'input'){
11444 if(tag == 'textarea'){
11447 }else if(Roo.isOpera){
11448 if(tag == 'input'){
11451 if(tag == 'textarea'){
11459 setFieldLabel : function(v)
11461 if(!this.rendered){
11465 if(this.indicatorEl()){
11466 var ar = this.el.select('label > span',true);
11468 if (ar.elements.length) {
11469 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11470 this.fieldLabel = v;
11474 var br = this.el.select('label',true);
11476 if(br.elements.length) {
11477 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11478 this.fieldLabel = v;
11482 Roo.log('Cannot Found any of label > span || label in input');
11486 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11487 this.fieldLabel = v;
11502 * @class Roo.bootstrap.TextArea
11503 * @extends Roo.bootstrap.Input
11504 * Bootstrap TextArea class
11505 * @cfg {Number} cols Specifies the visible width of a text area
11506 * @cfg {Number} rows Specifies the visible number of lines in a text area
11507 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11508 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11509 * @cfg {string} html text
11512 * Create a new TextArea
11513 * @param {Object} config The config object
11516 Roo.bootstrap.TextArea = function(config){
11517 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11531 getAutoCreate : function(){
11533 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11539 if(this.inputType != 'hidden'){
11540 cfg.cls = 'form-group' //input-group
11548 value : this.value || '',
11549 html: this.html || '',
11550 cls : 'form-control',
11551 placeholder : this.placeholder || ''
11555 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11556 input.maxLength = this.maxLength;
11560 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564 input.cols = this.cols;
11567 if (this.readOnly) {
11568 input.readonly = true;
11572 input.name = this.name;
11576 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580 ['xs','sm','md','lg'].map(function(size){
11581 if (settings[size]) {
11582 cfg.cls += ' col-' + size + '-' + settings[size];
11586 var inputblock = input;
11588 if(this.hasFeedback && !this.allowBlank){
11592 cls: 'glyphicon form-control-feedback'
11596 cls : 'has-feedback',
11605 if (this.before || this.after) {
11608 cls : 'input-group',
11612 inputblock.cn.push({
11614 cls : 'input-group-addon',
11619 inputblock.cn.push(input);
11621 if(this.hasFeedback && !this.allowBlank){
11622 inputblock.cls += ' has-feedback';
11623 inputblock.cn.push(feedback);
11627 inputblock.cn.push({
11629 cls : 'input-group-addon',
11636 if (align ==='left' && this.fieldLabel.length) {
11641 cls : 'control-label',
11642 html : this.fieldLabel
11653 if(this.labelWidth > 12){
11654 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11657 if(this.labelWidth < 13 && this.labelmd == 0){
11658 this.labelmd = this.labelWidth;
11661 if(this.labellg > 0){
11662 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11663 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11666 if(this.labelmd > 0){
11667 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11668 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11671 if(this.labelsm > 0){
11672 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11673 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11676 if(this.labelxs > 0){
11677 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11678 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11681 } else if ( this.fieldLabel.length) {
11686 //cls : 'input-group-addon',
11687 html : this.fieldLabel
11705 if (this.disabled) {
11706 input.disabled=true;
11713 * return the real textarea element.
11715 inputEl: function ()
11717 return this.el.select('textarea.form-control',true).first();
11721 * Clear any invalid styles/messages for this field
11723 clearInvalid : function()
11726 if(!this.el || this.preventMark){ // not rendered
11730 var label = this.el.select('label', true).first();
11731 var icon = this.el.select('i.fa-star', true).first();
11736 this.el.removeClass( this.validClass);
11737 this.inputEl().removeClass('is-invalid');
11739 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11741 var feedback = this.el.select('.form-control-feedback', true).first();
11744 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11749 this.fireEvent('valid', this);
11753 * Mark this field as valid
11755 markValid : function()
11757 if(!this.el || this.preventMark){ // not rendered
11761 this.el.removeClass([this.invalidClass, this.validClass]);
11762 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11764 var feedback = this.el.select('.form-control-feedback', true).first();
11767 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11770 if(this.disabled || this.allowBlank){
11774 var label = this.el.select('label', true).first();
11775 var icon = this.el.select('i.fa-star', true).first();
11780 if (Roo.bootstrap.version == 3) {
11781 this.el.addClass(this.validClass);
11783 this.inputEl().addClass('is-valid');
11787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11789 var feedback = this.el.select('.form-control-feedback', true).first();
11792 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11793 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11798 this.fireEvent('valid', this);
11802 * Mark this field as invalid
11803 * @param {String} msg The validation message
11805 markInvalid : function(msg)
11807 if(!this.el || this.preventMark){ // not rendered
11811 this.el.removeClass([this.invalidClass, this.validClass]);
11812 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11814 var feedback = this.el.select('.form-control-feedback', true).first();
11817 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11820 if(this.disabled || this.allowBlank){
11824 var label = this.el.select('label', true).first();
11825 var icon = this.el.select('i.fa-star', true).first();
11827 if(!this.getValue().length && label && !icon){
11828 this.el.createChild({
11830 cls : 'text-danger fa fa-lg fa-star',
11831 tooltip : 'This field is required',
11832 style : 'margin-right:5px;'
11836 if (Roo.bootstrap.version == 3) {
11837 this.el.addClass(this.invalidClass);
11839 this.inputEl().addClass('is-invalid');
11842 // fixme ... this may be depricated need to test..
11843 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11845 var feedback = this.el.select('.form-control-feedback', true).first();
11848 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11850 if(this.getValue().length || this.forceFeedback){
11851 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858 this.fireEvent('invalid', this, msg);
11866 * trigger field - base class for combo..
11871 * @class Roo.bootstrap.TriggerField
11872 * @extends Roo.bootstrap.Input
11873 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11874 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11875 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11876 * for which you can provide a custom implementation. For example:
11878 var trigger = new Roo.bootstrap.TriggerField();
11879 trigger.onTriggerClick = myTriggerFn;
11880 trigger.applyTo('my-field');
11883 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11884 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11885 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11886 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11887 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11890 * Create a new TriggerField.
11891 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11892 * to the base TextField)
11894 Roo.bootstrap.TriggerField = function(config){
11895 this.mimicing = false;
11896 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11901 * @cfg {String} triggerClass A CSS class to apply to the trigger
11904 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11909 * @cfg {Boolean} removable (true|false) special filter default false
11913 /** @cfg {Boolean} grow @hide */
11914 /** @cfg {Number} growMin @hide */
11915 /** @cfg {Number} growMax @hide */
11921 autoSize: Roo.emptyFn,
11925 deferHeight : true,
11928 actionMode : 'wrap',
11933 getAutoCreate : function(){
11935 var align = this.labelAlign || this.parentLabelAlign();
11940 cls: 'form-group' //input-group
11947 type : this.inputType,
11948 cls : 'form-control',
11949 autocomplete: 'new-password',
11950 placeholder : this.placeholder || ''
11954 input.name = this.name;
11957 input.cls += ' input-' + this.size;
11960 if (this.disabled) {
11961 input.disabled=true;
11964 var inputblock = input;
11966 if(this.hasFeedback && !this.allowBlank){
11970 cls: 'glyphicon form-control-feedback'
11973 if(this.removable && !this.editable ){
11975 cls : 'has-feedback',
11981 cls : 'roo-combo-removable-btn close'
11988 cls : 'has-feedback',
11997 if(this.removable && !this.editable ){
11999 cls : 'roo-removable',
12005 cls : 'roo-combo-removable-btn close'
12012 if (this.before || this.after) {
12015 cls : 'input-group',
12019 inputblock.cn.push({
12021 cls : 'input-group-addon input-group-prepend input-group-text',
12026 inputblock.cn.push(input);
12028 if(this.hasFeedback && !this.allowBlank){
12029 inputblock.cls += ' has-feedback';
12030 inputblock.cn.push(feedback);
12034 inputblock.cn.push({
12036 cls : 'input-group-addon input-group-append input-group-text',
12045 var ibwrap = inputblock;
12050 cls: 'roo-select2-choices',
12054 cls: 'roo-select2-search-field',
12066 cls: 'roo-select2-container input-group',
12071 cls: 'form-hidden-field'
12077 if(!this.multiple && this.showToggleBtn){
12083 if (this.caret != false) {
12086 cls: 'fa fa-' + this.caret
12093 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12095 Roo.bootstrap.version == 3 ? caret : '',
12098 cls: 'combobox-clear',
12112 combobox.cls += ' roo-select2-container-multi';
12116 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12117 tooltip : 'This field is required'
12119 if (Roo.bootstrap.version == 4) {
12122 style : 'display:none'
12127 if (align ==='left' && this.fieldLabel.length) {
12129 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12136 cls : 'control-label',
12137 html : this.fieldLabel
12149 var labelCfg = cfg.cn[1];
12150 var contentCfg = cfg.cn[2];
12152 if(this.indicatorpos == 'right'){
12157 cls : 'control-label',
12161 html : this.fieldLabel
12175 labelCfg = cfg.cn[0];
12176 contentCfg = cfg.cn[1];
12179 if(this.labelWidth > 12){
12180 labelCfg.style = "width: " + this.labelWidth + 'px';
12183 if(this.labelWidth < 13 && this.labelmd == 0){
12184 this.labelmd = this.labelWidth;
12187 if(this.labellg > 0){
12188 labelCfg.cls += ' col-lg-' + this.labellg;
12189 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12192 if(this.labelmd > 0){
12193 labelCfg.cls += ' col-md-' + this.labelmd;
12194 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12197 if(this.labelsm > 0){
12198 labelCfg.cls += ' col-sm-' + this.labelsm;
12199 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12202 if(this.labelxs > 0){
12203 labelCfg.cls += ' col-xs-' + this.labelxs;
12204 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12207 } else if ( this.fieldLabel.length) {
12208 // Roo.log(" label");
12213 //cls : 'input-group-addon',
12214 html : this.fieldLabel
12222 if(this.indicatorpos == 'right'){
12230 html : this.fieldLabel
12244 // Roo.log(" no label && no align");
12251 ['xs','sm','md','lg'].map(function(size){
12252 if (settings[size]) {
12253 cfg.cls += ' col-' + size + '-' + settings[size];
12264 onResize : function(w, h){
12265 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12266 // if(typeof w == 'number'){
12267 // var x = w - this.trigger.getWidth();
12268 // this.inputEl().setWidth(this.adjustWidth('input', x));
12269 // this.trigger.setStyle('left', x+'px');
12274 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12277 getResizeEl : function(){
12278 return this.inputEl();
12282 getPositionEl : function(){
12283 return this.inputEl();
12287 alignErrorIcon : function(){
12288 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292 initEvents : function(){
12296 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12297 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12298 if(!this.multiple && this.showToggleBtn){
12299 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12300 if(this.hideTrigger){
12301 this.trigger.setDisplayed(false);
12303 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12310 if(this.removable && !this.editable && !this.tickable){
12311 var close = this.closeTriggerEl();
12314 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12315 close.on('click', this.removeBtnClick, this, close);
12319 //this.trigger.addClassOnOver('x-form-trigger-over');
12320 //this.trigger.addClassOnClick('x-form-trigger-click');
12323 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327 closeTriggerEl : function()
12329 var close = this.el.select('.roo-combo-removable-btn', true).first();
12330 return close ? close : false;
12333 removeBtnClick : function(e, h, el)
12335 e.preventDefault();
12337 if(this.fireEvent("remove", this) !== false){
12339 this.fireEvent("afterremove", this)
12343 createList : function()
12345 this.list = Roo.get(document.body).createChild({
12346 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12347 cls: 'typeahead typeahead-long dropdown-menu shadow',
12348 style: 'display:none'
12351 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12356 initTrigger : function(){
12361 onDestroy : function(){
12363 this.trigger.removeAllListeners();
12364 // this.trigger.remove();
12367 // this.wrap.remove();
12369 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373 onFocus : function(){
12374 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12376 if(!this.mimicing){
12377 this.wrap.addClass('x-trigger-wrap-focus');
12378 this.mimicing = true;
12379 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12380 if(this.monitorTab){
12381 this.el.on("keydown", this.checkTab, this);
12388 checkTab : function(e){
12389 if(e.getKey() == e.TAB){
12390 this.triggerBlur();
12395 onBlur : function(){
12400 mimicBlur : function(e, t){
12402 if(!this.wrap.contains(t) && this.validateBlur()){
12403 this.triggerBlur();
12409 triggerBlur : function(){
12410 this.mimicing = false;
12411 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12412 if(this.monitorTab){
12413 this.el.un("keydown", this.checkTab, this);
12415 //this.wrap.removeClass('x-trigger-wrap-focus');
12416 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12421 validateBlur : function(e, t){
12426 onDisable : function(){
12427 this.inputEl().dom.disabled = true;
12428 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12430 // this.wrap.addClass('x-item-disabled');
12435 onEnable : function(){
12436 this.inputEl().dom.disabled = false;
12437 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12439 // this.el.removeClass('x-item-disabled');
12444 onShow : function(){
12445 var ae = this.getActionEl();
12448 ae.dom.style.display = '';
12449 ae.dom.style.visibility = 'visible';
12455 onHide : function(){
12456 var ae = this.getActionEl();
12457 ae.dom.style.display = 'none';
12461 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12462 * by an implementing function.
12464 * @param {EventObject} e
12466 onTriggerClick : Roo.emptyFn
12474 * @class Roo.bootstrap.CardUploader
12475 * @extends Roo.bootstrap.Button
12476 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12477 * @cfg {Number} errorTimeout default 3000
12478 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12479 * @cfg {Array} html The button text.
12483 * Create a new CardUploader
12484 * @param {Object} config The config object
12487 Roo.bootstrap.CardUploader = function(config){
12491 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12494 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12501 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12504 errorTimeout : 3000,
12508 fileCollection : false,
12511 getAutoCreate : function()
12515 cls :'form-group' ,
12520 //cls : 'input-group-addon',
12521 html : this.fieldLabel
12528 value : this.value,
12529 cls : 'd-none form-control'
12534 multiple : 'multiple',
12536 cls : 'd-none roo-card-upload-selector'
12540 cls : 'roo-card-uploader-button-container w-100 mb-2'
12543 cls : 'card-columns roo-card-uploader-container'
12553 getChildContainer : function() /// what children are added to.
12555 return this.containerEl;
12558 getButtonContainer : function() /// what children are added to.
12560 return this.el.select(".roo-card-uploader-button-container").first();
12563 initEvents : function()
12566 Roo.bootstrap.Input.prototype.initEvents.call(this);
12570 xns: Roo.bootstrap,
12573 container_method : 'getButtonContainer' ,
12574 html : this.html, // fix changable?
12577 'click' : function(btn, e) {
12586 this.urlAPI = (window.createObjectURL && window) ||
12587 (window.URL && URL.revokeObjectURL && URL) ||
12588 (window.webkitURL && webkitURL);
12593 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12595 this.selectorEl.on('change', this.onFileSelected, this);
12598 this.images.forEach(function(img) {
12601 this.images = false;
12603 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12609 onClick : function(e)
12611 e.preventDefault();
12613 this.selectorEl.dom.click();
12617 onFileSelected : function(e)
12619 e.preventDefault();
12621 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625 Roo.each(this.selectorEl.dom.files, function(file){
12626 this.addFile(file);
12635 addFile : function(file)
12638 if(typeof(file) === 'string'){
12639 throw "Add file by name?"; // should not happen
12643 if(!file || !this.urlAPI){
12653 var url = _this.urlAPI.createObjectURL( file);
12656 id : Roo.bootstrap.CardUploader.ID--,
12657 is_uploaded : false,
12660 mimetype : file.type,
12667 addCard : function (data)
12669 // hidden input element?
12670 // if the file is not an image...
12671 //then we need to use something other that and header_image
12676 xns : Roo.bootstrap,
12677 xtype : 'CardFooter',
12680 xns : Roo.bootstrap,
12686 xns : Roo.bootstrap,
12688 html : String.format("<small>{0}</small>", data.title),
12689 cls : 'col-11 text-left',
12694 click : function() {
12695 this.downloadCard(data.id)
12701 xns : Roo.bootstrap,
12709 click : function() {
12710 t.removeCard(data.id)
12722 var cn = this.addxtype(
12725 xns : Roo.bootstrap,
12728 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12729 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12730 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12735 initEvents : function() {
12736 Roo.bootstrap.Card.prototype.initEvents.call(this);
12737 this.imgEl = this.el.select('.card-img-top').first();
12739 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12740 this.imgEl.set({ 'pointer' : 'cursor' });
12749 // dont' really need ot update items.
12750 // this.items.push(cn);
12751 this.fileCollection.add(cn);
12752 this.updateInput();
12755 removeCard : function(id)
12758 var card = this.fileCollection.get(id);
12759 card.data.is_deleted = 1;
12760 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12761 this.fileCollection.remove(card);
12762 //this.items = this.items.filter(function(e) { return e != card });
12763 // dont' really need ot update items.
12764 card.el.dom.parentNode.removeChild(card.el.dom);
12769 this.fileCollection.each(function(card) {
12770 card.el.dom.parentNode.removeChild(card.el.dom);
12772 this.fileCollection.clear();
12773 this.updateInput();
12776 updateInput : function()
12779 this.fileCollection.each(function(e) {
12783 this.inputEl().dom.value = JSON.stringify(data);
12790 Roo.bootstrap.CardUploader.ID = -1;/*
12792 * Ext JS Library 1.1.1
12793 * Copyright(c) 2006-2007, Ext JS, LLC.
12795 * Originally Released Under LGPL - original licence link has changed is not relivant.
12798 * <script type="text/javascript">
12803 * @class Roo.data.SortTypes
12805 * Defines the default sorting (casting?) comparison functions used when sorting data.
12807 Roo.data.SortTypes = {
12809 * Default sort that does nothing
12810 * @param {Mixed} s The value being converted
12811 * @return {Mixed} The comparison value
12813 none : function(s){
12818 * The regular expression used to strip tags
12822 stripTagsRE : /<\/?[^>]+>/gi,
12825 * Strips all HTML tags to sort on text only
12826 * @param {Mixed} s The value being converted
12827 * @return {String} The comparison value
12829 asText : function(s){
12830 return String(s).replace(this.stripTagsRE, "");
12834 * Strips all HTML tags to sort on text only - Case insensitive
12835 * @param {Mixed} s The value being converted
12836 * @return {String} The comparison value
12838 asUCText : function(s){
12839 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843 * Case insensitive string
12844 * @param {Mixed} s The value being converted
12845 * @return {String} The comparison value
12847 asUCString : function(s) {
12848 return String(s).toUpperCase();
12853 * @param {Mixed} s The value being converted
12854 * @return {Number} The comparison value
12856 asDate : function(s) {
12860 if(s instanceof Date){
12861 return s.getTime();
12863 return Date.parse(String(s));
12868 * @param {Mixed} s The value being converted
12869 * @return {Float} The comparison value
12871 asFloat : function(s) {
12872 var val = parseFloat(String(s).replace(/,/g, ""));
12881 * @param {Mixed} s The value being converted
12882 * @return {Number} The comparison value
12884 asInt : function(s) {
12885 var val = parseInt(String(s).replace(/,/g, ""));
12893 * Ext JS Library 1.1.1
12894 * Copyright(c) 2006-2007, Ext JS, LLC.
12896 * Originally Released Under LGPL - original licence link has changed is not relivant.
12899 * <script type="text/javascript">
12903 * @class Roo.data.Record
12904 * Instances of this class encapsulate both record <em>definition</em> information, and record
12905 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12906 * to access Records cached in an {@link Roo.data.Store} object.<br>
12908 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12909 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12912 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12914 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12915 * {@link #create}. The parameters are the same.
12916 * @param {Array} data An associative Array of data values keyed by the field name.
12917 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12918 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12919 * not specified an integer id is generated.
12921 Roo.data.Record = function(data, id){
12922 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12927 * Generate a constructor for a specific record layout.
12928 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12929 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12930 * Each field definition object may contain the following properties: <ul>
12931 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12932 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12933 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12934 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12935 * is being used, then this is a string containing the javascript expression to reference the data relative to
12936 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12937 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12938 * this may be omitted.</p></li>
12939 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12940 * <ul><li>auto (Default, implies no conversion)</li>
12945 * <li>date</li></ul></p></li>
12946 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12947 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12948 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12949 * by the Reader into an object that will be stored in the Record. It is passed the
12950 * following parameters:<ul>
12951 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12953 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12955 * <br>usage:<br><pre><code>
12956 var TopicRecord = Roo.data.Record.create(
12957 {name: 'title', mapping: 'topic_title'},
12958 {name: 'author', mapping: 'username'},
12959 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12960 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12961 {name: 'lastPoster', mapping: 'user2'},
12962 {name: 'excerpt', mapping: 'post_text'}
12965 var myNewRecord = new TopicRecord({
12966 title: 'Do my job please',
12969 lastPost: new Date(),
12970 lastPoster: 'Animal',
12971 excerpt: 'No way dude!'
12973 myStore.add(myNewRecord);
12978 Roo.data.Record.create = function(o){
12979 var f = function(){
12980 f.superclass.constructor.apply(this, arguments);
12982 Roo.extend(f, Roo.data.Record);
12983 var p = f.prototype;
12984 p.fields = new Roo.util.MixedCollection(false, function(field){
12987 for(var i = 0, len = o.length; i < len; i++){
12988 p.fields.add(new Roo.data.Field(o[i]));
12990 f.getField = function(name){
12991 return p.fields.get(name);
12996 Roo.data.Record.AUTO_ID = 1000;
12997 Roo.data.Record.EDIT = 'edit';
12998 Roo.data.Record.REJECT = 'reject';
12999 Roo.data.Record.COMMIT = 'commit';
13001 Roo.data.Record.prototype = {
13003 * Readonly flag - true if this record has been modified.
13012 join : function(store){
13013 this.store = store;
13017 * Set the named field to the specified value.
13018 * @param {String} name The name of the field to set.
13019 * @param {Object} value The value to set the field to.
13021 set : function(name, value){
13022 if(this.data[name] == value){
13026 if(!this.modified){
13027 this.modified = {};
13029 if(typeof this.modified[name] == 'undefined'){
13030 this.modified[name] = this.data[name];
13032 this.data[name] = value;
13033 if(!this.editing && this.store){
13034 this.store.afterEdit(this);
13039 * Get the value of the named field.
13040 * @param {String} name The name of the field to get the value of.
13041 * @return {Object} The value of the field.
13043 get : function(name){
13044 return this.data[name];
13048 beginEdit : function(){
13049 this.editing = true;
13050 this.modified = {};
13054 cancelEdit : function(){
13055 this.editing = false;
13056 delete this.modified;
13060 endEdit : function(){
13061 this.editing = false;
13062 if(this.dirty && this.store){
13063 this.store.afterEdit(this);
13068 * Usually called by the {@link Roo.data.Store} which owns the Record.
13069 * Rejects all changes made to the Record since either creation, or the last commit operation.
13070 * Modified fields are reverted to their original values.
13072 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13073 * of reject operations.
13075 reject : function(){
13076 var m = this.modified;
13078 if(typeof m[n] != "function"){
13079 this.data[n] = m[n];
13082 this.dirty = false;
13083 delete this.modified;
13084 this.editing = false;
13086 this.store.afterReject(this);
13091 * Usually called by the {@link Roo.data.Store} which owns the Record.
13092 * Commits all changes made to the Record since either creation, or the last commit operation.
13094 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095 * of commit operations.
13097 commit : function(){
13098 this.dirty = false;
13099 delete this.modified;
13100 this.editing = false;
13102 this.store.afterCommit(this);
13107 hasError : function(){
13108 return this.error != null;
13112 clearError : function(){
13117 * Creates a copy of this record.
13118 * @param {String} id (optional) A new record id if you don't want to use this record's id
13121 copy : function(newId) {
13122 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126 * Ext JS Library 1.1.1
13127 * Copyright(c) 2006-2007, Ext JS, LLC.
13129 * Originally Released Under LGPL - original licence link has changed is not relivant.
13132 * <script type="text/javascript">
13138 * @class Roo.data.Store
13139 * @extends Roo.util.Observable
13140 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13141 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13143 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13144 * has no knowledge of the format of the data returned by the Proxy.<br>
13146 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13147 * instances from the data object. These records are cached and made available through accessor functions.
13149 * Creates a new Store.
13150 * @param {Object} config A config object containing the objects needed for the Store to access data,
13151 * and read the data into Records.
13153 Roo.data.Store = function(config){
13154 this.data = new Roo.util.MixedCollection(false);
13155 this.data.getKey = function(o){
13158 this.baseParams = {};
13160 this.paramNames = {
13165 "multisort" : "_multisort"
13168 if(config && config.data){
13169 this.inlineData = config.data;
13170 delete config.data;
13173 Roo.apply(this, config);
13175 if(this.reader){ // reader passed
13176 this.reader = Roo.factory(this.reader, Roo.data);
13177 this.reader.xmodule = this.xmodule || false;
13178 if(!this.recordType){
13179 this.recordType = this.reader.recordType;
13181 if(this.reader.onMetaChange){
13182 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186 if(this.recordType){
13187 this.fields = this.recordType.prototype.fields;
13189 this.modified = [];
13193 * @event datachanged
13194 * Fires when the data cache has changed, and a widget which is using this Store
13195 * as a Record cache should refresh its view.
13196 * @param {Store} this
13198 datachanged : true,
13200 * @event metachange
13201 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13202 * @param {Store} this
13203 * @param {Object} meta The JSON metadata
13208 * Fires when Records have been added to the Store
13209 * @param {Store} this
13210 * @param {Roo.data.Record[]} records The array of Records added
13211 * @param {Number} index The index at which the record(s) were added
13216 * Fires when a Record has been removed from the Store
13217 * @param {Store} this
13218 * @param {Roo.data.Record} record The Record that was removed
13219 * @param {Number} index The index at which the record was removed
13224 * Fires when a Record has been updated
13225 * @param {Store} this
13226 * @param {Roo.data.Record} record The Record that was updated
13227 * @param {String} operation The update operation being performed. Value may be one of:
13229 Roo.data.Record.EDIT
13230 Roo.data.Record.REJECT
13231 Roo.data.Record.COMMIT
13237 * Fires when the data cache has been cleared.
13238 * @param {Store} this
13242 * @event beforeload
13243 * Fires before a request is made for a new data object. If the beforeload handler returns false
13244 * the load action will be canceled.
13245 * @param {Store} this
13246 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250 * @event beforeloadadd
13251 * Fires after a new set of Records has been loaded.
13252 * @param {Store} this
13253 * @param {Roo.data.Record[]} records The Records that were loaded
13254 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13256 beforeloadadd : true,
13259 * Fires after a new set of Records has been loaded, before they are added to the store.
13260 * @param {Store} this
13261 * @param {Roo.data.Record[]} records The Records that were loaded
13262 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13263 * @params {Object} return from reader
13267 * @event loadexception
13268 * Fires if an exception occurs in the Proxy during loading.
13269 * Called with the signature of the Proxy's "loadexception" event.
13270 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13273 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13274 * @param {Object} load options
13275 * @param {Object} jsonData from your request (normally this contains the Exception)
13277 loadexception : true
13281 this.proxy = Roo.factory(this.proxy, Roo.data);
13282 this.proxy.xmodule = this.xmodule || false;
13283 this.relayEvents(this.proxy, ["loadexception"]);
13285 this.sortToggle = {};
13286 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13288 Roo.data.Store.superclass.constructor.call(this);
13290 if(this.inlineData){
13291 this.loadData(this.inlineData);
13292 delete this.inlineData;
13296 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13298 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13299 * without a remote query - used by combo/forms at present.
13303 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13306 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13309 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13310 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13313 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13314 * on any HTTP request
13317 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13320 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13325 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13327 remoteSort : false,
13330 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13331 * loaded or when a record is removed. (defaults to false).
13333 pruneModifiedRecords : false,
13336 lastOptions : null,
13339 * Add Records to the Store and fires the add event.
13340 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13342 add : function(records){
13343 records = [].concat(records);
13344 for(var i = 0, len = records.length; i < len; i++){
13345 records[i].join(this);
13347 var index = this.data.length;
13348 this.data.addAll(records);
13349 this.fireEvent("add", this, records, index);
13353 * Remove a Record from the Store and fires the remove event.
13354 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13356 remove : function(record){
13357 var index = this.data.indexOf(record);
13358 this.data.removeAt(index);
13360 if(this.pruneModifiedRecords){
13361 this.modified.remove(record);
13363 this.fireEvent("remove", this, record, index);
13367 * Remove all Records from the Store and fires the clear event.
13369 removeAll : function(){
13371 if(this.pruneModifiedRecords){
13372 this.modified = [];
13374 this.fireEvent("clear", this);
13378 * Inserts Records to the Store at the given index and fires the add event.
13379 * @param {Number} index The start index at which to insert the passed Records.
13380 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13382 insert : function(index, records){
13383 records = [].concat(records);
13384 for(var i = 0, len = records.length; i < len; i++){
13385 this.data.insert(index, records[i]);
13386 records[i].join(this);
13388 this.fireEvent("add", this, records, index);
13392 * Get the index within the cache of the passed Record.
13393 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13394 * @return {Number} The index of the passed Record. Returns -1 if not found.
13396 indexOf : function(record){
13397 return this.data.indexOf(record);
13401 * Get the index within the cache of the Record with the passed id.
13402 * @param {String} id The id of the Record to find.
13403 * @return {Number} The index of the Record. Returns -1 if not found.
13405 indexOfId : function(id){
13406 return this.data.indexOfKey(id);
13410 * Get the Record with the specified id.
13411 * @param {String} id The id of the Record to find.
13412 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13414 getById : function(id){
13415 return this.data.key(id);
13419 * Get the Record at the specified index.
13420 * @param {Number} index The index of the Record to find.
13421 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13423 getAt : function(index){
13424 return this.data.itemAt(index);
13428 * Returns a range of Records between specified indices.
13429 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13430 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13431 * @return {Roo.data.Record[]} An array of Records
13433 getRange : function(start, end){
13434 return this.data.getRange(start, end);
13438 storeOptions : function(o){
13439 o = Roo.apply({}, o);
13442 this.lastOptions = o;
13446 * Loads the Record cache from the configured Proxy using the configured Reader.
13448 * If using remote paging, then the first load call must specify the <em>start</em>
13449 * and <em>limit</em> properties in the options.params property to establish the initial
13450 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13452 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13453 * and this call will return before the new data has been loaded. Perform any post-processing
13454 * in a callback function, or in a "load" event handler.</strong>
13456 * @param {Object} options An object containing properties which control loading options:<ul>
13457 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13458 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13459 * passed the following arguments:<ul>
13460 * <li>r : Roo.data.Record[]</li>
13461 * <li>options: Options object from the load call</li>
13462 * <li>success: Boolean success indicator</li></ul></li>
13463 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13464 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13467 load : function(options){
13468 options = options || {};
13469 if(this.fireEvent("beforeload", this, options) !== false){
13470 this.storeOptions(options);
13471 var p = Roo.apply(options.params || {}, this.baseParams);
13472 // if meta was not loaded from remote source.. try requesting it.
13473 if (!this.reader.metaFromRemote) {
13474 p._requestMeta = 1;
13476 if(this.sortInfo && this.remoteSort){
13477 var pn = this.paramNames;
13478 p[pn["sort"]] = this.sortInfo.field;
13479 p[pn["dir"]] = this.sortInfo.direction;
13481 if (this.multiSort) {
13482 var pn = this.paramNames;
13483 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13486 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13491 * Reloads the Record cache from the configured Proxy using the configured Reader and
13492 * the options from the last load operation performed.
13493 * @param {Object} options (optional) An object containing properties which may override the options
13494 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13495 * the most recently used options are reused).
13497 reload : function(options){
13498 this.load(Roo.applyIf(options||{}, this.lastOptions));
13502 // Called as a callback by the Reader during a load operation.
13503 loadRecords : function(o, options, success){
13504 if(!o || success === false){
13505 if(success !== false){
13506 this.fireEvent("load", this, [], options, o);
13508 if(options.callback){
13509 options.callback.call(options.scope || this, [], options, false);
13513 // if data returned failure - throw an exception.
13514 if (o.success === false) {
13515 // show a message if no listener is registered.
13516 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13517 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13519 // loadmask wil be hooked into this..
13520 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13523 var r = o.records, t = o.totalRecords || r.length;
13525 this.fireEvent("beforeloadadd", this, r, options, o);
13527 if(!options || options.add !== true){
13528 if(this.pruneModifiedRecords){
13529 this.modified = [];
13531 for(var i = 0, len = r.length; i < len; i++){
13535 this.data = this.snapshot;
13536 delete this.snapshot;
13539 this.data.addAll(r);
13540 this.totalLength = t;
13542 this.fireEvent("datachanged", this);
13544 this.totalLength = Math.max(t, this.data.length+r.length);
13548 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13550 var e = new Roo.data.Record({});
13552 e.set(this.parent.displayField, this.parent.emptyTitle);
13553 e.set(this.parent.valueField, '');
13558 this.fireEvent("load", this, r, options, o);
13559 if(options.callback){
13560 options.callback.call(options.scope || this, r, options, true);
13566 * Loads data from a passed data block. A Reader which understands the format of the data
13567 * must have been configured in the constructor.
13568 * @param {Object} data The data block from which to read the Records. The format of the data expected
13569 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13570 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13572 loadData : function(o, append){
13573 var r = this.reader.readRecords(o);
13574 this.loadRecords(r, {add: append}, true);
13578 * using 'cn' the nested child reader read the child array into it's child stores.
13579 * @param {Object} rec The record with a 'children array
13581 loadDataFromChildren : function(rec)
13583 this.loadData(this.reader.toLoadData(rec));
13588 * Gets the number of cached records.
13590 * <em>If using paging, this may not be the total size of the dataset. If the data object
13591 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13592 * the data set size</em>
13594 getCount : function(){
13595 return this.data.length || 0;
13599 * Gets the total number of records in the dataset as returned by the server.
13601 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13602 * the dataset size</em>
13604 getTotalCount : function(){
13605 return this.totalLength || 0;
13609 * Returns the sort state of the Store as an object with two properties:
13611 field {String} The name of the field by which the Records are sorted
13612 direction {String} The sort order, "ASC" or "DESC"
13615 getSortState : function(){
13616 return this.sortInfo;
13620 applySort : function(){
13621 if(this.sortInfo && !this.remoteSort){
13622 var s = this.sortInfo, f = s.field;
13623 var st = this.fields.get(f).sortType;
13624 var fn = function(r1, r2){
13625 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13626 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13628 this.data.sort(s.direction, fn);
13629 if(this.snapshot && this.snapshot != this.data){
13630 this.snapshot.sort(s.direction, fn);
13636 * Sets the default sort column and order to be used by the next load operation.
13637 * @param {String} fieldName The name of the field to sort by.
13638 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13640 setDefaultSort : function(field, dir){
13641 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645 * Sort the Records.
13646 * If remote sorting is used, the sort is performed on the server, and the cache is
13647 * reloaded. If local sorting is used, the cache is sorted internally.
13648 * @param {String} fieldName The name of the field to sort by.
13649 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13651 sort : function(fieldName, dir){
13652 var f = this.fields.get(fieldName);
13654 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13656 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13657 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13662 this.sortToggle[f.name] = dir;
13663 this.sortInfo = {field: f.name, direction: dir};
13664 if(!this.remoteSort){
13666 this.fireEvent("datachanged", this);
13668 this.load(this.lastOptions);
13673 * Calls the specified function for each of the Records in the cache.
13674 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13675 * Returning <em>false</em> aborts and exits the iteration.
13676 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13678 each : function(fn, scope){
13679 this.data.each(fn, scope);
13683 * Gets all records modified since the last commit. Modified records are persisted across load operations
13684 * (e.g., during paging).
13685 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13687 getModifiedRecords : function(){
13688 return this.modified;
13692 createFilterFn : function(property, value, anyMatch){
13693 if(!value.exec){ // not a regex
13694 value = String(value);
13695 if(value.length == 0){
13698 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13700 return function(r){
13701 return value.test(r.data[property]);
13706 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13707 * @param {String} property A field on your records
13708 * @param {Number} start The record index to start at (defaults to 0)
13709 * @param {Number} end The last record index to include (defaults to length - 1)
13710 * @return {Number} The sum
13712 sum : function(property, start, end){
13713 var rs = this.data.items, v = 0;
13714 start = start || 0;
13715 end = (end || end === 0) ? end : rs.length-1;
13717 for(var i = start; i <= end; i++){
13718 v += (rs[i].data[property] || 0);
13724 * Filter the records by a specified property.
13725 * @param {String} field A field on your records
13726 * @param {String/RegExp} value Either a string that the field
13727 * should start with or a RegExp to test against the field
13728 * @param {Boolean} anyMatch True to match any part not just the beginning
13730 filter : function(property, value, anyMatch){
13731 var fn = this.createFilterFn(property, value, anyMatch);
13732 return fn ? this.filterBy(fn) : this.clearFilter();
13736 * Filter by a function. The specified function will be called with each
13737 * record in this data source. If the function returns true the record is included,
13738 * otherwise it is filtered.
13739 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13740 * @param {Object} scope (optional) The scope of the function (defaults to this)
13742 filterBy : function(fn, scope){
13743 this.snapshot = this.snapshot || this.data;
13744 this.data = this.queryBy(fn, scope||this);
13745 this.fireEvent("datachanged", this);
13749 * Query the records by a specified property.
13750 * @param {String} field A field on your records
13751 * @param {String/RegExp} value Either a string that the field
13752 * should start with or a RegExp to test against the field
13753 * @param {Boolean} anyMatch True to match any part not just the beginning
13754 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13756 query : function(property, value, anyMatch){
13757 var fn = this.createFilterFn(property, value, anyMatch);
13758 return fn ? this.queryBy(fn) : this.data.clone();
13762 * Query by a function. The specified function will be called with each
13763 * record in this data source. If the function returns true the record is included
13765 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766 * @param {Object} scope (optional) The scope of the function (defaults to this)
13767 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13769 queryBy : function(fn, scope){
13770 var data = this.snapshot || this.data;
13771 return data.filterBy(fn, scope||this);
13775 * Collects unique values for a particular dataIndex from this store.
13776 * @param {String} dataIndex The property to collect
13777 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13778 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13779 * @return {Array} An array of the unique values
13781 collect : function(dataIndex, allowNull, bypassFilter){
13782 var d = (bypassFilter === true && this.snapshot) ?
13783 this.snapshot.items : this.data.items;
13784 var v, sv, r = [], l = {};
13785 for(var i = 0, len = d.length; i < len; i++){
13786 v = d[i].data[dataIndex];
13788 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13797 * Revert to a view of the Record cache with no filtering applied.
13798 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13800 clearFilter : function(suppressEvent){
13801 if(this.snapshot && this.snapshot != this.data){
13802 this.data = this.snapshot;
13803 delete this.snapshot;
13804 if(suppressEvent !== true){
13805 this.fireEvent("datachanged", this);
13811 afterEdit : function(record){
13812 if(this.modified.indexOf(record) == -1){
13813 this.modified.push(record);
13815 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819 afterReject : function(record){
13820 this.modified.remove(record);
13821 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825 afterCommit : function(record){
13826 this.modified.remove(record);
13827 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13832 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13834 commitChanges : function(){
13835 var m = this.modified.slice(0);
13836 this.modified = [];
13837 for(var i = 0, len = m.length; i < len; i++){
13843 * Cancel outstanding changes on all changed records.
13845 rejectChanges : function(){
13846 var m = this.modified.slice(0);
13847 this.modified = [];
13848 for(var i = 0, len = m.length; i < len; i++){
13853 onMetaChange : function(meta, rtype, o){
13854 this.recordType = rtype;
13855 this.fields = rtype.prototype.fields;
13856 delete this.snapshot;
13857 this.sortInfo = meta.sortInfo || this.sortInfo;
13858 this.modified = [];
13859 this.fireEvent('metachange', this, this.reader.meta);
13862 moveIndex : function(data, type)
13864 var index = this.indexOf(data);
13866 var newIndex = index + type;
13870 this.insert(newIndex, data);
13875 * Ext JS Library 1.1.1
13876 * Copyright(c) 2006-2007, Ext JS, LLC.
13878 * Originally Released Under LGPL - original licence link has changed is not relivant.
13881 * <script type="text/javascript">
13885 * @class Roo.data.SimpleStore
13886 * @extends Roo.data.Store
13887 * Small helper class to make creating Stores from Array data easier.
13888 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13889 * @cfg {Array} fields An array of field definition objects, or field name strings.
13890 * @cfg {Object} an existing reader (eg. copied from another store)
13891 * @cfg {Array} data The multi-dimensional array of data
13893 * @param {Object} config
13895 Roo.data.SimpleStore = function(config)
13897 Roo.data.SimpleStore.superclass.constructor.call(this, {
13899 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13902 Roo.data.Record.create(config.fields)
13904 proxy : new Roo.data.MemoryProxy(config.data)
13908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13910 * Ext JS Library 1.1.1
13911 * Copyright(c) 2006-2007, Ext JS, LLC.
13913 * Originally Released Under LGPL - original licence link has changed is not relivant.
13916 * <script type="text/javascript">
13921 * @extends Roo.data.Store
13922 * @class Roo.data.JsonStore
13923 * Small helper class to make creating Stores for JSON data easier. <br/>
13925 var store = new Roo.data.JsonStore({
13926 url: 'get-images.php',
13928 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13931 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13932 * JsonReader and HttpProxy (unless inline data is provided).</b>
13933 * @cfg {Array} fields An array of field definition objects, or field name strings.
13935 * @param {Object} config
13937 Roo.data.JsonStore = function(c){
13938 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13939 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13940 reader: new Roo.data.JsonReader(c, c.fields)
13943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13945 * Ext JS Library 1.1.1
13946 * Copyright(c) 2006-2007, Ext JS, LLC.
13948 * Originally Released Under LGPL - original licence link has changed is not relivant.
13951 * <script type="text/javascript">
13955 Roo.data.Field = function(config){
13956 if(typeof config == "string"){
13957 config = {name: config};
13959 Roo.apply(this, config);
13962 this.type = "auto";
13965 var st = Roo.data.SortTypes;
13966 // named sortTypes are supported, here we look them up
13967 if(typeof this.sortType == "string"){
13968 this.sortType = st[this.sortType];
13971 // set default sortType for strings and dates
13972 if(!this.sortType){
13975 this.sortType = st.asUCString;
13978 this.sortType = st.asDate;
13981 this.sortType = st.none;
13986 var stripRe = /[\$,%]/g;
13988 // prebuilt conversion function for this field, instead of
13989 // switching every time we're reading a value
13991 var cv, dateFormat = this.dateFormat;
13996 cv = function(v){ return v; };
13999 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003 return v !== undefined && v !== null && v !== '' ?
14004 parseInt(String(v).replace(stripRe, ""), 10) : '';
14009 return v !== undefined && v !== null && v !== '' ?
14010 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14015 cv = function(v){ return v === true || v === "true" || v == 1; };
14022 if(v instanceof Date){
14026 if(dateFormat == "timestamp"){
14027 return new Date(v*1000);
14029 return Date.parseDate(v, dateFormat);
14031 var parsed = Date.parse(v);
14032 return parsed ? new Date(parsed) : null;
14041 Roo.data.Field.prototype = {
14049 * Ext JS Library 1.1.1
14050 * Copyright(c) 2006-2007, Ext JS, LLC.
14052 * Originally Released Under LGPL - original licence link has changed is not relivant.
14055 * <script type="text/javascript">
14058 // Base class for reading structured data from a data source. This class is intended to be
14059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14062 * @class Roo.data.DataReader
14063 * Base class for reading structured data from a data source. This class is intended to be
14064 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14067 Roo.data.DataReader = function(meta, recordType){
14071 this.recordType = recordType instanceof Array ?
14072 Roo.data.Record.create(recordType) : recordType;
14075 Roo.data.DataReader.prototype = {
14078 readerType : 'Data',
14080 * Create an empty record
14081 * @param {Object} data (optional) - overlay some values
14082 * @return {Roo.data.Record} record created.
14084 newRow : function(d) {
14086 this.recordType.prototype.fields.each(function(c) {
14088 case 'int' : da[c.name] = 0; break;
14089 case 'date' : da[c.name] = new Date(); break;
14090 case 'float' : da[c.name] = 0.0; break;
14091 case 'boolean' : da[c.name] = false; break;
14092 default : da[c.name] = ""; break;
14096 return new this.recordType(Roo.apply(da, d));
14102 * Ext JS Library 1.1.1
14103 * Copyright(c) 2006-2007, Ext JS, LLC.
14105 * Originally Released Under LGPL - original licence link has changed is not relivant.
14108 * <script type="text/javascript">
14112 * @class Roo.data.DataProxy
14113 * @extends Roo.data.Observable
14114 * This class is an abstract base class for implementations which provide retrieval of
14115 * unformatted data objects.<br>
14117 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14118 * (of the appropriate type which knows how to parse the data object) to provide a block of
14119 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14121 * Custom implementations must implement the load method as described in
14122 * {@link Roo.data.HttpProxy#load}.
14124 Roo.data.DataProxy = function(){
14127 * @event beforeload
14128 * Fires before a network request is made to retrieve a data object.
14129 * @param {Object} This DataProxy object.
14130 * @param {Object} params The params parameter to the load function.
14135 * Fires before the load method's callback is called.
14136 * @param {Object} This DataProxy object.
14137 * @param {Object} o The data object.
14138 * @param {Object} arg The callback argument object passed to the load function.
14142 * @event loadexception
14143 * Fires if an Exception occurs during data retrieval.
14144 * @param {Object} This DataProxy object.
14145 * @param {Object} o The data object.
14146 * @param {Object} arg The callback argument object passed to the load function.
14147 * @param {Object} e The Exception.
14149 loadexception : true
14151 Roo.data.DataProxy.superclass.constructor.call(this);
14154 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14157 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161 * Ext JS Library 1.1.1
14162 * Copyright(c) 2006-2007, Ext JS, LLC.
14164 * Originally Released Under LGPL - original licence link has changed is not relivant.
14167 * <script type="text/javascript">
14170 * @class Roo.data.MemoryProxy
14171 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14172 * to the Reader when its load method is called.
14174 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14176 Roo.data.MemoryProxy = function(data){
14180 Roo.data.MemoryProxy.superclass.constructor.call(this);
14184 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14187 * Load data from the requested source (in this case an in-memory
14188 * data object passed to the constructor), read the data object into
14189 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14190 * process that block using the passed callback.
14191 * @param {Object} params This parameter is not used by the MemoryProxy class.
14192 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14193 * object into a block of Roo.data.Records.
14194 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14195 * The function must be passed <ul>
14196 * <li>The Record block object</li>
14197 * <li>The "arg" argument from the load function</li>
14198 * <li>A boolean success indicator</li>
14200 * @param {Object} scope The scope in which to call the callback
14201 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14203 load : function(params, reader, callback, scope, arg){
14204 params = params || {};
14207 result = reader.readRecords(params.data ? params.data :this.data);
14209 this.fireEvent("loadexception", this, arg, null, e);
14210 callback.call(scope, null, arg, false);
14213 callback.call(scope, result, arg, true);
14217 update : function(params, records){
14222 * Ext JS Library 1.1.1
14223 * Copyright(c) 2006-2007, Ext JS, LLC.
14225 * Originally Released Under LGPL - original licence link has changed is not relivant.
14228 * <script type="text/javascript">
14231 * @class Roo.data.HttpProxy
14232 * @extends Roo.data.DataProxy
14233 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14234 * configured to reference a certain URL.<br><br>
14236 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14237 * from which the running page was served.<br><br>
14239 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14241 * Be aware that to enable the browser to parse an XML document, the server must set
14242 * the Content-Type header in the HTTP response to "text/xml".
14244 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14245 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14246 * will be used to make the request.
14248 Roo.data.HttpProxy = function(conn){
14249 Roo.data.HttpProxy.superclass.constructor.call(this);
14250 // is conn a conn config or a real conn?
14252 this.useAjax = !conn || !conn.events;
14256 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14257 // thse are take from connection...
14260 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14263 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14264 * extra parameters to each request made by this object. (defaults to undefined)
14267 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14268 * to each request made by this object. (defaults to undefined)
14271 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14274 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14277 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14283 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14288 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14289 * a finer-grained basis than the DataProxy events.
14291 getConnection : function(){
14292 return this.useAjax ? Roo.Ajax : this.conn;
14296 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14297 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14298 * process that block using the passed callback.
14299 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14300 * for the request to the remote server.
14301 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14302 * object into a block of Roo.data.Records.
14303 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14304 * The function must be passed <ul>
14305 * <li>The Record block object</li>
14306 * <li>The "arg" argument from the load function</li>
14307 * <li>A boolean success indicator</li>
14309 * @param {Object} scope The scope in which to call the callback
14310 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14312 load : function(params, reader, callback, scope, arg){
14313 if(this.fireEvent("beforeload", this, params) !== false){
14315 params : params || {},
14317 callback : callback,
14322 callback : this.loadResponse,
14326 Roo.applyIf(o, this.conn);
14327 if(this.activeRequest){
14328 Roo.Ajax.abort(this.activeRequest);
14330 this.activeRequest = Roo.Ajax.request(o);
14332 this.conn.request(o);
14335 callback.call(scope||this, null, arg, false);
14340 loadResponse : function(o, success, response){
14341 delete this.activeRequest;
14343 this.fireEvent("loadexception", this, o, response);
14344 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14349 result = o.reader.read(response);
14351 this.fireEvent("loadexception", this, o, response, e);
14352 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356 this.fireEvent("load", this, o, o.request.arg);
14357 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361 update : function(dataSet){
14366 updateResponse : function(dataSet){
14371 * Ext JS Library 1.1.1
14372 * Copyright(c) 2006-2007, Ext JS, LLC.
14374 * Originally Released Under LGPL - original licence link has changed is not relivant.
14377 * <script type="text/javascript">
14381 * @class Roo.data.ScriptTagProxy
14382 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14383 * other than the originating domain of the running page.<br><br>
14385 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14386 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14388 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14389 * source code that is used as the source inside a <script> tag.<br><br>
14391 * In order for the browser to process the returned data, the server must wrap the data object
14392 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14393 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14394 * depending on whether the callback name was passed:
14397 boolean scriptTag = false;
14398 String cb = request.getParameter("callback");
14401 response.setContentType("text/javascript");
14403 response.setContentType("application/x-json");
14405 Writer out = response.getWriter();
14407 out.write(cb + "(");
14409 out.print(dataBlock.toJsonString());
14416 * @param {Object} config A configuration object.
14418 Roo.data.ScriptTagProxy = function(config){
14419 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14420 Roo.apply(this, config);
14421 this.head = document.getElementsByTagName("head")[0];
14424 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14426 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14428 * @cfg {String} url The URL from which to request the data object.
14431 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14436 * the server the name of the callback function set up by the load call to process the returned data object.
14437 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14438 * javascript output which calls this named function passing the data object as its only parameter.
14440 callbackParam : "callback",
14442 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14443 * name to the request.
14448 * Load data from the configured URL, read the data object into
14449 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14450 * process that block using the passed callback.
14451 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14452 * for the request to the remote server.
14453 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14454 * object into a block of Roo.data.Records.
14455 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14456 * The function must be passed <ul>
14457 * <li>The Record block object</li>
14458 * <li>The "arg" argument from the load function</li>
14459 * <li>A boolean success indicator</li>
14461 * @param {Object} scope The scope in which to call the callback
14462 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14464 load : function(params, reader, callback, scope, arg){
14465 if(this.fireEvent("beforeload", this, params) !== false){
14467 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14469 var url = this.url;
14470 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14472 url += "&_dc=" + (new Date().getTime());
14474 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14477 cb : "stcCallback"+transId,
14478 scriptId : "stcScript"+transId,
14482 callback : callback,
14488 window[trans.cb] = function(o){
14489 conn.handleResponse(o, trans);
14492 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14494 if(this.autoAbort !== false){
14498 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14500 var script = document.createElement("script");
14501 script.setAttribute("src", url);
14502 script.setAttribute("type", "text/javascript");
14503 script.setAttribute("id", trans.scriptId);
14504 this.head.appendChild(script);
14506 this.trans = trans;
14508 callback.call(scope||this, null, arg, false);
14513 isLoading : function(){
14514 return this.trans ? true : false;
14518 * Abort the current server request.
14520 abort : function(){
14521 if(this.isLoading()){
14522 this.destroyTrans(this.trans);
14527 destroyTrans : function(trans, isLoaded){
14528 this.head.removeChild(document.getElementById(trans.scriptId));
14529 clearTimeout(trans.timeoutId);
14531 window[trans.cb] = undefined;
14533 delete window[trans.cb];
14536 // if hasn't been loaded, wait for load to remove it to prevent script error
14537 window[trans.cb] = function(){
14538 window[trans.cb] = undefined;
14540 delete window[trans.cb];
14547 handleResponse : function(o, trans){
14548 this.trans = false;
14549 this.destroyTrans(trans, true);
14552 result = trans.reader.readRecords(o);
14554 this.fireEvent("loadexception", this, o, trans.arg, e);
14555 trans.callback.call(trans.scope||window, null, trans.arg, false);
14558 this.fireEvent("load", this, o, trans.arg);
14559 trans.callback.call(trans.scope||window, result, trans.arg, true);
14563 handleFailure : function(trans){
14564 this.trans = false;
14565 this.destroyTrans(trans, false);
14566 this.fireEvent("loadexception", this, null, trans.arg);
14567 trans.callback.call(trans.scope||window, null, trans.arg, false);
14571 * Ext JS Library 1.1.1
14572 * Copyright(c) 2006-2007, Ext JS, LLC.
14574 * Originally Released Under LGPL - original licence link has changed is not relivant.
14577 * <script type="text/javascript">
14581 * @class Roo.data.JsonReader
14582 * @extends Roo.data.DataReader
14583 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14584 * based on mappings in a provided Roo.data.Record constructor.
14586 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14587 * in the reply previously.
14592 var RecordDef = Roo.data.Record.create([
14593 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14594 {name: 'occupation'} // This field will use "occupation" as the mapping.
14596 var myReader = new Roo.data.JsonReader({
14597 totalProperty: "results", // The property which contains the total dataset size (optional)
14598 root: "rows", // The property which contains an Array of row objects
14599 id: "id" // The property within each row object that provides an ID for the record (optional)
14603 * This would consume a JSON file like this:
14605 { 'results': 2, 'rows': [
14606 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14607 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14610 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14611 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14612 * paged from the remote server.
14613 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14614 * @cfg {String} root name of the property which contains the Array of row objects.
14615 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14616 * @cfg {Array} fields Array of field definition objects
14618 * Create a new JsonReader
14619 * @param {Object} meta Metadata configuration options
14620 * @param {Object} recordType Either an Array of field definition objects,
14621 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14623 Roo.data.JsonReader = function(meta, recordType){
14626 // set some defaults:
14627 Roo.applyIf(meta, {
14628 totalProperty: 'total',
14629 successProperty : 'success',
14634 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14638 readerType : 'Json',
14641 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14642 * Used by Store query builder to append _requestMeta to params.
14645 metaFromRemote : false,
14647 * This method is only used by a DataProxy which has retrieved data from a remote server.
14648 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14649 * @return {Object} data A data block which is used by an Roo.data.Store object as
14650 * a cache of Roo.data.Records.
14652 read : function(response){
14653 var json = response.responseText;
14655 var o = /* eval:var:o */ eval("("+json+")");
14657 throw {message: "JsonReader.read: Json object not found"};
14663 this.metaFromRemote = true;
14664 this.meta = o.metaData;
14665 this.recordType = Roo.data.Record.create(o.metaData.fields);
14666 this.onMetaChange(this.meta, this.recordType, o);
14668 return this.readRecords(o);
14671 // private function a store will implement
14672 onMetaChange : function(meta, recordType, o){
14679 simpleAccess: function(obj, subsc) {
14686 getJsonAccessor: function(){
14688 return function(expr) {
14690 return(re.test(expr))
14691 ? new Function("obj", "return obj." + expr)
14696 return Roo.emptyFn;
14701 * Create a data block containing Roo.data.Records from an XML document.
14702 * @param {Object} o An object which contains an Array of row objects in the property specified
14703 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14704 * which contains the total size of the dataset.
14705 * @return {Object} data A data block which is used by an Roo.data.Store object as
14706 * a cache of Roo.data.Records.
14708 readRecords : function(o){
14710 * After any data loads, the raw JSON data is available for further custom processing.
14714 var s = this.meta, Record = this.recordType,
14715 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14717 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14719 if(s.totalProperty) {
14720 this.getTotal = this.getJsonAccessor(s.totalProperty);
14722 if(s.successProperty) {
14723 this.getSuccess = this.getJsonAccessor(s.successProperty);
14725 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14727 var g = this.getJsonAccessor(s.id);
14728 this.getId = function(rec) {
14730 return (r === undefined || r === "") ? null : r;
14733 this.getId = function(){return null;};
14736 for(var jj = 0; jj < fl; jj++){
14738 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14739 this.ef[jj] = this.getJsonAccessor(map);
14743 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14744 if(s.totalProperty){
14745 var vt = parseInt(this.getTotal(o), 10);
14750 if(s.successProperty){
14751 var vs = this.getSuccess(o);
14752 if(vs === false || vs === 'false'){
14757 for(var i = 0; i < c; i++){
14760 var id = this.getId(n);
14761 for(var j = 0; j < fl; j++){
14763 var v = this.ef[j](n);
14765 Roo.log('missing convert for ' + f.name);
14769 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14771 var record = new Record(values, id);
14773 records[i] = record;
14779 totalRecords : totalRecords
14782 // used when loading children.. @see loadDataFromChildren
14783 toLoadData: function(rec)
14785 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14786 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14787 return { data : data, total : data.length };
14792 * Ext JS Library 1.1.1
14793 * Copyright(c) 2006-2007, Ext JS, LLC.
14795 * Originally Released Under LGPL - original licence link has changed is not relivant.
14798 * <script type="text/javascript">
14802 * @class Roo.data.ArrayReader
14803 * @extends Roo.data.DataReader
14804 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14805 * Each element of that Array represents a row of data fields. The
14806 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14807 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811 var RecordDef = Roo.data.Record.create([
14812 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14813 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14815 var myReader = new Roo.data.ArrayReader({
14816 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14820 * This would consume an Array like this:
14822 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826 * Create a new JsonReader
14827 * @param {Object} meta Metadata configuration options.
14828 * @param {Object|Array} recordType Either an Array of field definition objects
14830 * @cfg {Array} fields Array of field definition objects
14831 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14832 * as specified to {@link Roo.data.Record#create},
14833 * or an {@link Roo.data.Record} object
14836 * created using {@link Roo.data.Record#create}.
14838 Roo.data.ArrayReader = function(meta, recordType)
14840 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14843 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14846 * Create a data block containing Roo.data.Records from an XML document.
14847 * @param {Object} o An Array of row objects which represents the dataset.
14848 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14849 * a cache of Roo.data.Records.
14851 readRecords : function(o)
14853 var sid = this.meta ? this.meta.id : null;
14854 var recordType = this.recordType, fields = recordType.prototype.fields;
14857 for(var i = 0; i < root.length; i++){
14860 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14861 for(var j = 0, jlen = fields.length; j < jlen; j++){
14862 var f = fields.items[j];
14863 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14864 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14866 values[f.name] = v;
14868 var record = new recordType(values, id);
14870 records[records.length] = record;
14874 totalRecords : records.length
14877 // used when loading children.. @see loadDataFromChildren
14878 toLoadData: function(rec)
14880 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14881 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14892 * @class Roo.bootstrap.ComboBox
14893 * @extends Roo.bootstrap.TriggerField
14894 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14895 * @cfg {Boolean} append (true|false) default false
14896 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14897 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14898 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14899 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14900 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14901 * @cfg {Boolean} animate default true
14902 * @cfg {Boolean} emptyResultText only for touch device
14903 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14904 * @cfg {String} emptyTitle default ''
14905 * @cfg {Number} width fixed with? experimental
14907 * Create a new ComboBox.
14908 * @param {Object} config Configuration options
14910 Roo.bootstrap.ComboBox = function(config){
14911 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915 * Fires when the dropdown list is expanded
14916 * @param {Roo.bootstrap.ComboBox} combo This combo box
14921 * Fires when the dropdown list is collapsed
14922 * @param {Roo.bootstrap.ComboBox} combo This combo box
14926 * @event beforeselect
14927 * Fires before a list item is selected. Return false to cancel the selection.
14928 * @param {Roo.bootstrap.ComboBox} combo This combo box
14929 * @param {Roo.data.Record} record The data record returned from the underlying store
14930 * @param {Number} index The index of the selected item in the dropdown list
14932 'beforeselect' : true,
14935 * Fires when a list item is selected
14936 * @param {Roo.bootstrap.ComboBox} combo This combo box
14937 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14938 * @param {Number} index The index of the selected item in the dropdown list
14942 * @event beforequery
14943 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14944 * The event object passed has these properties:
14945 * @param {Roo.bootstrap.ComboBox} combo This combo box
14946 * @param {String} query The query
14947 * @param {Boolean} forceAll true to force "all" query
14948 * @param {Boolean} cancel true to cancel the query
14949 * @param {Object} e The query event object
14951 'beforequery': true,
14954 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14955 * @param {Roo.bootstrap.ComboBox} combo This combo box
14960 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14961 * @param {Roo.bootstrap.ComboBox} combo This combo box
14962 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14967 * Fires when the remove value from the combobox array
14968 * @param {Roo.bootstrap.ComboBox} combo This combo box
14972 * @event afterremove
14973 * Fires when the remove value from the combobox array
14974 * @param {Roo.bootstrap.ComboBox} combo This combo box
14976 'afterremove' : true,
14978 * @event specialfilter
14979 * Fires when specialfilter
14980 * @param {Roo.bootstrap.ComboBox} combo This combo box
14982 'specialfilter' : true,
14985 * Fires when tick the element
14986 * @param {Roo.bootstrap.ComboBox} combo This combo box
14990 * @event touchviewdisplay
14991 * Fires when touch view require special display (default is using displayField)
14992 * @param {Roo.bootstrap.ComboBox} combo This combo box
14993 * @param {Object} cfg set html .
14995 'touchviewdisplay' : true
15000 this.tickItems = [];
15002 this.selectedIndex = -1;
15003 if(this.mode == 'local'){
15004 if(config.queryDelay === undefined){
15005 this.queryDelay = 10;
15007 if(config.minChars === undefined){
15013 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15016 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15017 * rendering into an Roo.Editor, defaults to false)
15020 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15021 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15024 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15027 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15028 * the dropdown list (defaults to undefined, with no header element)
15032 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15036 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15038 listWidth: undefined,
15040 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15041 * mode = 'remote' or 'text' if mode = 'local')
15043 displayField: undefined,
15046 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15047 * mode = 'remote' or 'value' if mode = 'local').
15048 * Note: use of a valueField requires the user make a selection
15049 * in order for a value to be mapped.
15051 valueField: undefined,
15053 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15058 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15059 * field's data value (defaults to the underlying DOM element's name)
15061 hiddenName: undefined,
15063 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15069 selectedClass: 'active',
15072 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15077 * anchor positions (defaults to 'tl-bl')
15079 listAlign: 'tl-bl?',
15081 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15086 * query specified by the allQuery config option (defaults to 'query')
15088 triggerAction: 'query',
15090 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15091 * (defaults to 4, does not apply if editable = false)
15095 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15096 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15101 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15106 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15110 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15111 * when editable = true (defaults to false)
15113 selectOnFocus:false,
15115 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15117 queryParam: 'query',
15119 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15120 * when mode = 'remote' (defaults to 'Loading...')
15122 loadingText: 'Loading...',
15124 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15133 * traditional select (defaults to true)
15137 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15146 * listWidth has a higher value)
15150 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15151 * allow the user to set arbitrary text into the field (defaults to false)
15153 forceSelection:false,
15155 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15156 * if typeAhead = true (defaults to 250)
15158 typeAheadDelay : 250,
15160 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15161 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15163 valueNotFoundText : undefined,
15165 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15167 blockFocus : false,
15170 * @cfg {Boolean} disableClear Disable showing of clear button.
15172 disableClear : false,
15174 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15176 alwaysQuery : false,
15179 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15184 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15186 invalidClass : "has-warning",
15189 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15191 validClass : "has-success",
15194 * @cfg {Boolean} specialFilter (true|false) special filter default false
15196 specialFilter : false,
15199 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15201 mobileTouchView : true,
15204 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15206 useNativeIOS : false,
15209 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15211 mobile_restrict_height : false,
15213 ios_options : false,
15225 btnPosition : 'right',
15226 triggerList : true,
15227 showToggleBtn : true,
15229 emptyResultText: 'Empty',
15230 triggerText : 'Select',
15234 // element that contains real text value.. (when hidden is used..)
15236 getAutoCreate : function()
15241 * Render classic select for iso
15244 if(Roo.isIOS && this.useNativeIOS){
15245 cfg = this.getAutoCreateNativeIOS();
15253 if(Roo.isTouch && this.mobileTouchView){
15254 cfg = this.getAutoCreateTouchView();
15261 if(!this.tickable){
15262 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15267 * ComboBox with tickable selections
15270 var align = this.labelAlign || this.parentLabelAlign();
15273 cls : 'form-group roo-combobox-tickable' //input-group
15276 var btn_text_select = '';
15277 var btn_text_done = '';
15278 var btn_text_cancel = '';
15280 if (this.btn_text_show) {
15281 btn_text_select = 'Select';
15282 btn_text_done = 'Done';
15283 btn_text_cancel = 'Cancel';
15288 cls : 'tickable-buttons',
15293 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15294 //html : this.triggerText
15295 html: btn_text_select
15301 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15303 html: btn_text_done
15309 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15311 html: btn_text_cancel
15317 buttons.cn.unshift({
15319 cls: 'roo-select2-search-field-input'
15325 Roo.each(buttons.cn, function(c){
15327 c.cls += ' btn-' + _this.size;
15330 if (_this.disabled) {
15337 style : 'display: contents',
15342 cls: 'form-hidden-field'
15346 cls: 'roo-select2-choices',
15350 cls: 'roo-select2-search-field',
15361 cls: 'roo-select2-container input-group roo-select2-container-multi',
15367 // cls: 'typeahead typeahead-long dropdown-menu',
15368 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15373 if(this.hasFeedback && !this.allowBlank){
15377 cls: 'glyphicon form-control-feedback'
15380 combobox.cn.push(feedback);
15387 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15388 tooltip : 'This field is required'
15390 if (Roo.bootstrap.version == 4) {
15393 style : 'display:none'
15396 if (align ==='left' && this.fieldLabel.length) {
15398 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15405 cls : 'control-label col-form-label',
15406 html : this.fieldLabel
15418 var labelCfg = cfg.cn[1];
15419 var contentCfg = cfg.cn[2];
15422 if(this.indicatorpos == 'right'){
15428 cls : 'control-label col-form-label',
15432 html : this.fieldLabel
15448 labelCfg = cfg.cn[0];
15449 contentCfg = cfg.cn[1];
15453 if(this.labelWidth > 12){
15454 labelCfg.style = "width: " + this.labelWidth + 'px';
15456 if(this.width * 1 > 0){
15457 contentCfg.style = "width: " + this.width + 'px';
15459 if(this.labelWidth < 13 && this.labelmd == 0){
15460 this.labelmd = this.labelWidth;
15463 if(this.labellg > 0){
15464 labelCfg.cls += ' col-lg-' + this.labellg;
15465 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15468 if(this.labelmd > 0){
15469 labelCfg.cls += ' col-md-' + this.labelmd;
15470 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15473 if(this.labelsm > 0){
15474 labelCfg.cls += ' col-sm-' + this.labelsm;
15475 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15478 if(this.labelxs > 0){
15479 labelCfg.cls += ' col-xs-' + this.labelxs;
15480 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484 } else if ( this.fieldLabel.length) {
15485 // Roo.log(" label");
15490 //cls : 'input-group-addon',
15491 html : this.fieldLabel
15496 if(this.indicatorpos == 'right'){
15500 //cls : 'input-group-addon',
15501 html : this.fieldLabel
15511 // Roo.log(" no label && no align");
15518 ['xs','sm','md','lg'].map(function(size){
15519 if (settings[size]) {
15520 cfg.cls += ' col-' + size + '-' + settings[size];
15528 _initEventsCalled : false,
15531 initEvents: function()
15533 if (this._initEventsCalled) { // as we call render... prevent looping...
15536 this._initEventsCalled = true;
15539 throw "can not find store for combo";
15542 this.indicator = this.indicatorEl();
15544 this.store = Roo.factory(this.store, Roo.data);
15545 this.store.parent = this;
15547 // if we are building from html. then this element is so complex, that we can not really
15548 // use the rendered HTML.
15549 // so we have to trash and replace the previous code.
15550 if (Roo.XComponent.build_from_html) {
15551 // remove this element....
15552 var e = this.el.dom, k=0;
15553 while (e ) { e = e.previousSibling; ++k;}
15558 this.rendered = false;
15560 this.render(this.parent().getChildContainer(true), k);
15563 if(Roo.isIOS && this.useNativeIOS){
15564 this.initIOSView();
15572 if(Roo.isTouch && this.mobileTouchView){
15573 this.initTouchView();
15578 this.initTickableEvents();
15582 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15584 if(this.hiddenName){
15586 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15588 this.hiddenField.dom.value =
15589 this.hiddenValue !== undefined ? this.hiddenValue :
15590 this.value !== undefined ? this.value : '';
15592 // prevent input submission
15593 this.el.dom.removeAttribute('name');
15594 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15599 // this.el.dom.setAttribute('autocomplete', 'off');
15602 var cls = 'x-combo-list';
15604 //this.list = new Roo.Layer({
15605 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15611 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15612 _this.list.setWidth(lw);
15615 this.list.on('mouseover', this.onViewOver, this);
15616 this.list.on('mousemove', this.onViewMove, this);
15617 this.list.on('scroll', this.onViewScroll, this);
15620 this.list.swallowEvent('mousewheel');
15621 this.assetHeight = 0;
15624 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15625 this.assetHeight += this.header.getHeight();
15628 this.innerList = this.list.createChild({cls:cls+'-inner'});
15629 this.innerList.on('mouseover', this.onViewOver, this);
15630 this.innerList.on('mousemove', this.onViewMove, this);
15631 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15633 if(this.allowBlank && !this.pageSize && !this.disableClear){
15634 this.footer = this.list.createChild({cls:cls+'-ft'});
15635 this.pageTb = new Roo.Toolbar(this.footer);
15639 this.footer = this.list.createChild({cls:cls+'-ft'});
15640 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15641 {pageSize: this.pageSize});
15645 if (this.pageTb && this.allowBlank && !this.disableClear) {
15647 this.pageTb.add(new Roo.Toolbar.Fill(), {
15648 cls: 'x-btn-icon x-btn-clear',
15650 handler: function()
15653 _this.clearValue();
15654 _this.onSelect(false, -1);
15659 this.assetHeight += this.footer.getHeight();
15664 this.tpl = Roo.bootstrap.version == 4 ?
15665 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15666 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15669 this.view = new Roo.View(this.list, this.tpl, {
15670 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15672 //this.view.wrapEl.setDisplayed(false);
15673 this.view.on('click', this.onViewClick, this);
15676 this.store.on('beforeload', this.onBeforeLoad, this);
15677 this.store.on('load', this.onLoad, this);
15678 this.store.on('loadexception', this.onLoadException, this);
15680 if(this.resizable){
15681 this.resizer = new Roo.Resizable(this.list, {
15682 pinned:true, handles:'se'
15684 this.resizer.on('resize', function(r, w, h){
15685 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15686 this.listWidth = w;
15687 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15688 this.restrictHeight();
15690 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15693 if(!this.editable){
15694 this.editable = true;
15695 this.setEditable(false);
15700 if (typeof(this.events.add.listeners) != 'undefined') {
15702 this.addicon = this.wrap.createChild(
15703 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15705 this.addicon.on('click', function(e) {
15706 this.fireEvent('add', this);
15709 if (typeof(this.events.edit.listeners) != 'undefined') {
15711 this.editicon = this.wrap.createChild(
15712 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15713 if (this.addicon) {
15714 this.editicon.setStyle('margin-left', '40px');
15716 this.editicon.on('click', function(e) {
15718 // we fire even if inothing is selected..
15719 this.fireEvent('edit', this, this.lastData );
15725 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15726 "up" : function(e){
15727 this.inKeyMode = true;
15731 "down" : function(e){
15732 if(!this.isExpanded()){
15733 this.onTriggerClick();
15735 this.inKeyMode = true;
15740 "enter" : function(e){
15741 // this.onViewClick();
15745 if(this.fireEvent("specialkey", this, e)){
15746 this.onViewClick(false);
15752 "esc" : function(e){
15756 "tab" : function(e){
15759 if(this.fireEvent("specialkey", this, e)){
15760 this.onViewClick(false);
15768 doRelay : function(foo, bar, hname){
15769 if(hname == 'down' || this.scope.isExpanded()){
15770 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15779 this.queryDelay = Math.max(this.queryDelay || 10,
15780 this.mode == 'local' ? 10 : 250);
15783 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15785 if(this.typeAhead){
15786 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788 if(this.editable !== false){
15789 this.inputEl().on("keyup", this.onKeyUp, this);
15791 if(this.forceSelection){
15792 this.inputEl().on('blur', this.doForce, this);
15796 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15797 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801 initTickableEvents: function()
15805 if(this.hiddenName){
15807 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15809 this.hiddenField.dom.value =
15810 this.hiddenValue !== undefined ? this.hiddenValue :
15811 this.value !== undefined ? this.value : '';
15813 // prevent input submission
15814 this.el.dom.removeAttribute('name');
15815 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15820 // this.list = this.el.select('ul.dropdown-menu',true).first();
15822 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15824 if(this.triggerList){
15825 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15828 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15829 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15831 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15832 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15834 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15835 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15837 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15838 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15839 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842 this.cancelBtn.hide();
15847 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15848 _this.list.setWidth(lw);
15851 this.list.on('mouseover', this.onViewOver, this);
15852 this.list.on('mousemove', this.onViewMove, this);
15854 this.list.on('scroll', this.onViewScroll, this);
15857 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15858 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15861 this.view = new Roo.View(this.list, this.tpl, {
15866 selectedClass: this.selectedClass
15869 //this.view.wrapEl.setDisplayed(false);
15870 this.view.on('click', this.onViewClick, this);
15874 this.store.on('beforeload', this.onBeforeLoad, this);
15875 this.store.on('load', this.onLoad, this);
15876 this.store.on('loadexception', this.onLoadException, this);
15879 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15880 "up" : function(e){
15881 this.inKeyMode = true;
15885 "down" : function(e){
15886 this.inKeyMode = true;
15890 "enter" : function(e){
15891 if(this.fireEvent("specialkey", this, e)){
15892 this.onViewClick(false);
15898 "esc" : function(e){
15899 this.onTickableFooterButtonClick(e, false, false);
15902 "tab" : function(e){
15903 this.fireEvent("specialkey", this, e);
15905 this.onTickableFooterButtonClick(e, false, false);
15912 doRelay : function(e, fn, key){
15913 if(this.scope.isExpanded()){
15914 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15923 this.queryDelay = Math.max(this.queryDelay || 10,
15924 this.mode == 'local' ? 10 : 250);
15927 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15929 if(this.typeAhead){
15930 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15933 if(this.editable !== false){
15934 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15937 this.indicator = this.indicatorEl();
15939 if(this.indicator){
15940 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15941 this.indicator.hide();
15946 onDestroy : function(){
15948 this.view.setStore(null);
15949 this.view.el.removeAllListeners();
15950 this.view.el.remove();
15951 this.view.purgeListeners();
15954 this.list.dom.innerHTML = '';
15958 this.store.un('beforeload', this.onBeforeLoad, this);
15959 this.store.un('load', this.onLoad, this);
15960 this.store.un('loadexception', this.onLoadException, this);
15962 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966 fireKey : function(e){
15967 if(e.isNavKeyPress() && !this.list.isVisible()){
15968 this.fireEvent("specialkey", this, e);
15973 onResize: function(w, h)
15977 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15979 // if(typeof w != 'number'){
15980 // // we do not handle it!?!?
15983 // var tw = this.trigger.getWidth();
15984 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15985 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15987 // this.inputEl().setWidth( this.adjustWidth('input', x));
15989 // //this.trigger.setStyle('left', x+'px');
15991 // if(this.list && this.listWidth === undefined){
15992 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15993 // this.list.setWidth(lw);
15994 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16002 * Allow or prevent the user from directly editing the field text. If false is passed,
16003 * the user will only be able to select from the items defined in the dropdown list. This method
16004 * is the runtime equivalent of setting the 'editable' config option at config time.
16005 * @param {Boolean} value True to allow the user to directly edit the field text
16007 setEditable : function(value){
16008 if(value == this.editable){
16011 this.editable = value;
16013 this.inputEl().dom.setAttribute('readOnly', true);
16014 this.inputEl().on('mousedown', this.onTriggerClick, this);
16015 this.inputEl().addClass('x-combo-noedit');
16017 this.inputEl().dom.setAttribute('readOnly', false);
16018 this.inputEl().un('mousedown', this.onTriggerClick, this);
16019 this.inputEl().removeClass('x-combo-noedit');
16025 onBeforeLoad : function(combo,opts){
16026 if(!this.hasFocus){
16030 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16032 this.restrictHeight();
16033 this.selectedIndex = -1;
16037 onLoad : function(){
16039 this.hasQuery = false;
16041 if(!this.hasFocus){
16045 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16046 this.loading.hide();
16049 if(this.store.getCount() > 0){
16052 this.restrictHeight();
16053 if(this.lastQuery == this.allQuery){
16054 if(this.editable && !this.tickable){
16055 this.inputEl().dom.select();
16059 !this.selectByValue(this.value, true) &&
16062 !this.store.lastOptions ||
16063 typeof(this.store.lastOptions.add) == 'undefined' ||
16064 this.store.lastOptions.add != true
16067 this.select(0, true);
16070 if(this.autoFocus){
16073 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16074 this.taTask.delay(this.typeAheadDelay);
16078 this.onEmptyResults();
16084 onLoadException : function()
16086 this.hasQuery = false;
16088 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16089 this.loading.hide();
16092 if(this.tickable && this.editable){
16097 // only causes errors at present
16098 //Roo.log(this.store.reader.jsonData);
16099 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16101 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16107 onTypeAhead : function(){
16108 if(this.store.getCount() > 0){
16109 var r = this.store.getAt(0);
16110 var newValue = r.data[this.displayField];
16111 var len = newValue.length;
16112 var selStart = this.getRawValue().length;
16114 if(selStart != len){
16115 this.setRawValue(newValue);
16116 this.selectText(selStart, newValue.length);
16122 onSelect : function(record, index){
16124 if(this.fireEvent('beforeselect', this, record, index) !== false){
16126 this.setFromData(index > -1 ? record.data : false);
16129 this.fireEvent('select', this, record, index);
16134 * Returns the currently selected field value or empty string if no value is set.
16135 * @return {String} value The selected value
16137 getValue : function()
16139 if(Roo.isIOS && this.useNativeIOS){
16140 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16147 if(this.valueField){
16148 return typeof this.value != 'undefined' ? this.value : '';
16150 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154 getRawValue : function()
16156 if(Roo.isIOS && this.useNativeIOS){
16157 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16160 var v = this.inputEl().getValue();
16166 * Clears any text/value currently set in the field
16168 clearValue : function(){
16170 if(this.hiddenField){
16171 this.hiddenField.dom.value = '';
16174 this.setRawValue('');
16175 this.lastSelectionText = '';
16176 this.lastData = false;
16178 var close = this.closeTriggerEl();
16189 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16190 * will be displayed in the field. If the value does not match the data value of an existing item,
16191 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16192 * Otherwise the field will be blank (although the value will still be set).
16193 * @param {String} value The value to match
16195 setValue : function(v)
16197 if(Roo.isIOS && this.useNativeIOS){
16198 this.setIOSValue(v);
16208 if(this.valueField){
16209 var r = this.findRecord(this.valueField, v);
16211 text = r.data[this.displayField];
16212 }else if(this.valueNotFoundText !== undefined){
16213 text = this.valueNotFoundText;
16216 this.lastSelectionText = text;
16217 if(this.hiddenField){
16218 this.hiddenField.dom.value = v;
16220 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16223 var close = this.closeTriggerEl();
16226 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16232 * @property {Object} the last set data for the element
16237 * Sets the value of the field based on a object which is related to the record format for the store.
16238 * @param {Object} value the value to set as. or false on reset?
16240 setFromData : function(o){
16247 var dv = ''; // display value
16248 var vv = ''; // value value..
16250 if (this.displayField) {
16251 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16253 // this is an error condition!!!
16254 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16257 if(this.valueField){
16258 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16261 var close = this.closeTriggerEl();
16264 if(dv.length || vv * 1 > 0){
16266 this.blockFocus=true;
16272 if(this.hiddenField){
16273 this.hiddenField.dom.value = vv;
16275 this.lastSelectionText = dv;
16276 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280 // no hidden field.. - we store the value in 'value', but still display
16281 // display field!!!!
16282 this.lastSelectionText = dv;
16283 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16290 reset : function(){
16291 // overridden so that last data is reset..
16298 this.setValue(this.originalValue);
16299 //this.clearInvalid();
16300 this.lastData = false;
16302 this.view.clearSelections();
16308 findRecord : function(prop, value){
16310 if(this.store.getCount() > 0){
16311 this.store.each(function(r){
16312 if(r.data[prop] == value){
16322 getName: function()
16324 // returns hidden if it's set..
16325 if (!this.rendered) {return ''};
16326 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16330 onViewMove : function(e, t){
16331 this.inKeyMode = false;
16335 onViewOver : function(e, t){
16336 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16339 var item = this.view.findItemFromChild(t);
16342 var index = this.view.indexOf(item);
16343 this.select(index, false);
16348 onViewClick : function(view, doFocus, el, e)
16350 var index = this.view.getSelectedIndexes()[0];
16352 var r = this.store.getAt(index);
16356 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16363 Roo.each(this.tickItems, function(v,k){
16365 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16367 _this.tickItems.splice(k, 1);
16369 if(typeof(e) == 'undefined' && view == false){
16370 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16382 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16383 this.tickItems.push(r.data);
16386 if(typeof(e) == 'undefined' && view == false){
16387 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16394 this.onSelect(r, index);
16396 if(doFocus !== false && !this.blockFocus){
16397 this.inputEl().focus();
16402 restrictHeight : function(){
16403 //this.innerList.dom.style.height = '';
16404 //var inner = this.innerList.dom;
16405 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16406 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16407 //this.list.beginUpdate();
16408 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16409 this.list.alignTo(this.inputEl(), this.listAlign);
16410 this.list.alignTo(this.inputEl(), this.listAlign);
16411 //this.list.endUpdate();
16415 onEmptyResults : function(){
16417 if(this.tickable && this.editable){
16418 this.hasFocus = false;
16419 this.restrictHeight();
16427 * Returns true if the dropdown list is expanded, else false.
16429 isExpanded : function(){
16430 return this.list.isVisible();
16434 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16435 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16436 * @param {String} value The data value of the item to select
16437 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16438 * selected item if it is not currently in view (defaults to true)
16439 * @return {Boolean} True if the value matched an item in the list, else false
16441 selectByValue : function(v, scrollIntoView){
16442 if(v !== undefined && v !== null){
16443 var r = this.findRecord(this.valueField || this.displayField, v);
16445 this.select(this.store.indexOf(r), scrollIntoView);
16453 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16454 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16455 * @param {Number} index The zero-based index of the list item to select
16456 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16457 * selected item if it is not currently in view (defaults to true)
16459 select : function(index, scrollIntoView){
16460 this.selectedIndex = index;
16461 this.view.select(index);
16462 if(scrollIntoView !== false){
16463 var el = this.view.getNode(index);
16465 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16468 this.list.scrollChildIntoView(el, false);
16474 selectNext : function(){
16475 var ct = this.store.getCount();
16477 if(this.selectedIndex == -1){
16479 }else if(this.selectedIndex < ct-1){
16480 this.select(this.selectedIndex+1);
16486 selectPrev : function(){
16487 var ct = this.store.getCount();
16489 if(this.selectedIndex == -1){
16491 }else if(this.selectedIndex != 0){
16492 this.select(this.selectedIndex-1);
16498 onKeyUp : function(e){
16499 if(this.editable !== false && !e.isSpecialKey()){
16500 this.lastKey = e.getKey();
16501 this.dqTask.delay(this.queryDelay);
16506 validateBlur : function(){
16507 return !this.list || !this.list.isVisible();
16511 initQuery : function(){
16513 var v = this.getRawValue();
16515 if(this.tickable && this.editable){
16516 v = this.tickableInputEl().getValue();
16523 doForce : function(){
16524 if(this.inputEl().dom.value.length > 0){
16525 this.inputEl().dom.value =
16526 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16532 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16533 * query allowing the query action to be canceled if needed.
16534 * @param {String} query The SQL query to execute
16535 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16536 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16537 * saved in the current store (defaults to false)
16539 doQuery : function(q, forceAll){
16541 if(q === undefined || q === null){
16546 forceAll: forceAll,
16550 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16555 forceAll = qe.forceAll;
16556 if(forceAll === true || (q.length >= this.minChars)){
16558 this.hasQuery = true;
16560 if(this.lastQuery != q || this.alwaysQuery){
16561 this.lastQuery = q;
16562 if(this.mode == 'local'){
16563 this.selectedIndex = -1;
16565 this.store.clearFilter();
16568 if(this.specialFilter){
16569 this.fireEvent('specialfilter', this);
16574 this.store.filter(this.displayField, q);
16577 this.store.fireEvent("datachanged", this.store);
16584 this.store.baseParams[this.queryParam] = q;
16586 var options = {params : this.getParams(q)};
16589 options.add = true;
16590 options.params.start = this.page * this.pageSize;
16593 this.store.load(options);
16596 * this code will make the page width larger, at the beginning, the list not align correctly,
16597 * we should expand the list on onLoad
16598 * so command out it
16603 this.selectedIndex = -1;
16608 this.loadNext = false;
16612 getParams : function(q){
16614 //p[this.queryParam] = q;
16618 p.limit = this.pageSize;
16624 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16626 collapse : function(){
16627 if(!this.isExpanded()){
16633 this.hasFocus = false;
16637 this.cancelBtn.hide();
16638 this.trigger.show();
16641 this.tickableInputEl().dom.value = '';
16642 this.tickableInputEl().blur();
16647 Roo.get(document).un('mousedown', this.collapseIf, this);
16648 Roo.get(document).un('mousewheel', this.collapseIf, this);
16649 if (!this.editable) {
16650 Roo.get(document).un('keydown', this.listKeyPress, this);
16652 this.fireEvent('collapse', this);
16658 collapseIf : function(e){
16659 var in_combo = e.within(this.el);
16660 var in_list = e.within(this.list);
16661 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16663 if (in_combo || in_list || is_list) {
16664 //e.stopPropagation();
16669 this.onTickableFooterButtonClick(e, false, false);
16677 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16679 expand : function(){
16681 if(this.isExpanded() || !this.hasFocus){
16685 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16686 this.list.setWidth(lw);
16692 this.restrictHeight();
16696 this.tickItems = Roo.apply([], this.item);
16699 this.cancelBtn.show();
16700 this.trigger.hide();
16703 this.tickableInputEl().focus();
16708 Roo.get(document).on('mousedown', this.collapseIf, this);
16709 Roo.get(document).on('mousewheel', this.collapseIf, this);
16710 if (!this.editable) {
16711 Roo.get(document).on('keydown', this.listKeyPress, this);
16714 this.fireEvent('expand', this);
16718 // Implements the default empty TriggerField.onTriggerClick function
16719 onTriggerClick : function(e)
16721 Roo.log('trigger click');
16723 if(this.disabled || !this.triggerList){
16728 this.loadNext = false;
16730 if(this.isExpanded()){
16732 if (!this.blockFocus) {
16733 this.inputEl().focus();
16737 this.hasFocus = true;
16738 if(this.triggerAction == 'all') {
16739 this.doQuery(this.allQuery, true);
16741 this.doQuery(this.getRawValue());
16743 if (!this.blockFocus) {
16744 this.inputEl().focus();
16749 onTickableTriggerClick : function(e)
16756 this.loadNext = false;
16757 this.hasFocus = true;
16759 if(this.triggerAction == 'all') {
16760 this.doQuery(this.allQuery, true);
16762 this.doQuery(this.getRawValue());
16766 onSearchFieldClick : function(e)
16768 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16769 this.onTickableFooterButtonClick(e, false, false);
16773 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16778 this.loadNext = false;
16779 this.hasFocus = true;
16781 if(this.triggerAction == 'all') {
16782 this.doQuery(this.allQuery, true);
16784 this.doQuery(this.getRawValue());
16788 listKeyPress : function(e)
16790 //Roo.log('listkeypress');
16791 // scroll to first matching element based on key pres..
16792 if (e.isSpecialKey()) {
16795 var k = String.fromCharCode(e.getKey()).toUpperCase();
16798 var csel = this.view.getSelectedNodes();
16799 var cselitem = false;
16801 var ix = this.view.indexOf(csel[0]);
16802 cselitem = this.store.getAt(ix);
16803 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16809 this.store.each(function(v) {
16811 // start at existing selection.
16812 if (cselitem.id == v.id) {
16818 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16819 match = this.store.indexOf(v);
16825 if (match === false) {
16826 return true; // no more action?
16829 this.view.select(match);
16830 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16831 sn.scrollIntoView(sn.dom.parentNode, false);
16834 onViewScroll : function(e, t){
16836 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16840 this.hasQuery = true;
16842 this.loading = this.list.select('.loading', true).first();
16844 if(this.loading === null){
16845 this.list.createChild({
16847 cls: 'loading roo-select2-more-results roo-select2-active',
16848 html: 'Loading more results...'
16851 this.loading = this.list.select('.loading', true).first();
16853 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16855 this.loading.hide();
16858 this.loading.show();
16863 this.loadNext = true;
16865 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16870 addItem : function(o)
16872 var dv = ''; // display value
16874 if (this.displayField) {
16875 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16877 // this is an error condition!!!
16878 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16885 var choice = this.choices.createChild({
16887 cls: 'roo-select2-search-choice',
16896 cls: 'roo-select2-search-choice-close fa fa-times',
16901 }, this.searchField);
16903 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16905 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16913 this.inputEl().dom.value = '';
16918 onRemoveItem : function(e, _self, o)
16920 e.preventDefault();
16922 this.lastItem = Roo.apply([], this.item);
16924 var index = this.item.indexOf(o.data) * 1;
16927 Roo.log('not this item?!');
16931 this.item.splice(index, 1);
16936 this.fireEvent('remove', this, e);
16942 syncValue : function()
16944 if(!this.item.length){
16951 Roo.each(this.item, function(i){
16952 if(_this.valueField){
16953 value.push(i[_this.valueField]);
16960 this.value = value.join(',');
16962 if(this.hiddenField){
16963 this.hiddenField.dom.value = this.value;
16966 this.store.fireEvent("datachanged", this.store);
16971 clearItem : function()
16973 if(!this.multiple){
16979 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16987 if(this.tickable && !Roo.isTouch){
16988 this.view.refresh();
16992 inputEl: function ()
16994 if(Roo.isIOS && this.useNativeIOS){
16995 return this.el.select('select.roo-ios-select', true).first();
16998 if(Roo.isTouch && this.mobileTouchView){
16999 return this.el.select('input.form-control',true).first();
17003 return this.searchField;
17006 return this.el.select('input.form-control',true).first();
17009 onTickableFooterButtonClick : function(e, btn, el)
17011 e.preventDefault();
17013 this.lastItem = Roo.apply([], this.item);
17015 if(btn && btn.name == 'cancel'){
17016 this.tickItems = Roo.apply([], this.item);
17025 Roo.each(this.tickItems, function(o){
17033 validate : function()
17035 if(this.getVisibilityEl().hasClass('hidden')){
17039 var v = this.getRawValue();
17042 v = this.getValue();
17045 if(this.disabled || this.allowBlank || v.length){
17050 this.markInvalid();
17054 tickableInputEl : function()
17056 if(!this.tickable || !this.editable){
17057 return this.inputEl();
17060 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064 getAutoCreateTouchView : function()
17069 cls: 'form-group' //input-group
17075 type : this.inputType,
17076 cls : 'form-control x-combo-noedit',
17077 autocomplete: 'new-password',
17078 placeholder : this.placeholder || '',
17083 input.name = this.name;
17087 input.cls += ' input-' + this.size;
17090 if (this.disabled) {
17091 input.disabled = true;
17095 cls : 'roo-combobox-wrap',
17102 inputblock.cls += ' input-group';
17104 inputblock.cn.unshift({
17106 cls : 'input-group-addon input-group-prepend input-group-text',
17111 if(this.removable && !this.multiple){
17112 inputblock.cls += ' roo-removable';
17114 inputblock.cn.push({
17117 cls : 'roo-combo-removable-btn close'
17121 if(this.hasFeedback && !this.allowBlank){
17123 inputblock.cls += ' has-feedback';
17125 inputblock.cn.push({
17127 cls: 'glyphicon form-control-feedback'
17134 inputblock.cls += (this.before) ? '' : ' input-group';
17136 inputblock.cn.push({
17138 cls : 'input-group-addon input-group-append input-group-text',
17144 var ibwrap = inputblock;
17149 cls: 'roo-select2-choices',
17153 cls: 'roo-select2-search-field',
17166 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17171 cls: 'form-hidden-field'
17177 if(!this.multiple && this.showToggleBtn){
17183 if (this.caret != false) {
17186 cls: 'fa fa-' + this.caret
17193 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17195 Roo.bootstrap.version == 3 ? caret : '',
17198 cls: 'combobox-clear',
17212 combobox.cls += ' roo-select2-container-multi';
17215 var align = this.labelAlign || this.parentLabelAlign();
17217 if (align ==='left' && this.fieldLabel.length) {
17222 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17223 tooltip : 'This field is required'
17227 cls : 'control-label col-form-label',
17228 html : this.fieldLabel
17232 cls : 'roo-combobox-wrap ',
17239 var labelCfg = cfg.cn[1];
17240 var contentCfg = cfg.cn[2];
17243 if(this.indicatorpos == 'right'){
17248 cls : 'control-label col-form-label',
17252 html : this.fieldLabel
17256 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17257 tooltip : 'This field is required'
17262 cls : "roo-combobox-wrap ",
17270 labelCfg = cfg.cn[0];
17271 contentCfg = cfg.cn[1];
17276 if(this.labelWidth > 12){
17277 labelCfg.style = "width: " + this.labelWidth + 'px';
17280 if(this.labelWidth < 13 && this.labelmd == 0){
17281 this.labelmd = this.labelWidth;
17284 if(this.labellg > 0){
17285 labelCfg.cls += ' col-lg-' + this.labellg;
17286 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17289 if(this.labelmd > 0){
17290 labelCfg.cls += ' col-md-' + this.labelmd;
17291 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17294 if(this.labelsm > 0){
17295 labelCfg.cls += ' col-sm-' + this.labelsm;
17296 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17299 if(this.labelxs > 0){
17300 labelCfg.cls += ' col-xs-' + this.labelxs;
17301 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305 } else if ( this.fieldLabel.length) {
17309 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17310 tooltip : 'This field is required'
17314 cls : 'control-label',
17315 html : this.fieldLabel
17326 if(this.indicatorpos == 'right'){
17330 cls : 'control-label',
17331 html : this.fieldLabel,
17335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17336 tooltip : 'This field is required'
17353 var settings = this;
17355 ['xs','sm','md','lg'].map(function(size){
17356 if (settings[size]) {
17357 cfg.cls += ' col-' + size + '-' + settings[size];
17364 initTouchView : function()
17366 this.renderTouchView();
17368 this.touchViewEl.on('scroll', function(){
17369 this.el.dom.scrollTop = 0;
17372 this.originalValue = this.getValue();
17374 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17376 this.inputEl().on("click", this.showTouchView, this);
17377 if (this.triggerEl) {
17378 this.triggerEl.on("click", this.showTouchView, this);
17382 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17383 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17385 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17387 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17388 this.store.on('load', this.onTouchViewLoad, this);
17389 this.store.on('loadexception', this.onTouchViewLoadException, this);
17391 if(this.hiddenName){
17393 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17395 this.hiddenField.dom.value =
17396 this.hiddenValue !== undefined ? this.hiddenValue :
17397 this.value !== undefined ? this.value : '';
17399 this.el.dom.removeAttribute('name');
17400 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17405 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17408 if(this.removable && !this.multiple){
17409 var close = this.closeTriggerEl();
17411 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17412 close.on('click', this.removeBtnClick, this, close);
17416 * fix the bug in Safari iOS8
17418 this.inputEl().on("focus", function(e){
17419 document.activeElement.blur();
17422 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17429 renderTouchView : function()
17431 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17432 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17435 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17438 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439 this.touchViewBodyEl.setStyle('overflow', 'auto');
17441 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17442 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17445 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449 showTouchView : function()
17455 this.touchViewHeaderEl.hide();
17457 if(this.modalTitle.length){
17458 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17459 this.touchViewHeaderEl.show();
17462 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17463 this.touchViewEl.show();
17465 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17467 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17468 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17470 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17472 if(this.modalTitle.length){
17473 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17476 this.touchViewBodyEl.setHeight(bodyHeight);
17480 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17482 this.touchViewEl.addClass(['in','show']);
17485 if(this._touchViewMask){
17486 Roo.get(document.body).addClass("x-body-masked");
17487 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17488 this._touchViewMask.setStyle('z-index', 10000);
17489 this._touchViewMask.addClass('show');
17492 this.doTouchViewQuery();
17496 hideTouchView : function()
17498 this.touchViewEl.removeClass(['in','show']);
17502 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17504 this.touchViewEl.setStyle('display', 'none');
17507 if(this._touchViewMask){
17508 this._touchViewMask.removeClass('show');
17509 Roo.get(document.body).removeClass("x-body-masked");
17513 setTouchViewValue : function()
17520 Roo.each(this.tickItems, function(o){
17525 this.hideTouchView();
17528 doTouchViewQuery : function()
17537 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541 if(!this.alwaysQuery || this.mode == 'local'){
17542 this.onTouchViewLoad();
17549 onTouchViewBeforeLoad : function(combo,opts)
17555 onTouchViewLoad : function()
17557 if(this.store.getCount() < 1){
17558 this.onTouchViewEmptyResults();
17562 this.clearTouchView();
17564 var rawValue = this.getRawValue();
17566 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17568 this.tickItems = [];
17570 this.store.data.each(function(d, rowIndex){
17571 var row = this.touchViewListGroup.createChild(template);
17573 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17574 row.addClass(d.data.cls);
17577 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17580 html : d.data[this.displayField]
17583 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17584 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17587 row.removeClass('selected');
17588 if(!this.multiple && this.valueField &&
17589 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17592 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17593 row.addClass('selected');
17596 if(this.multiple && this.valueField &&
17597 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17602 this.tickItems.push(d.data);
17605 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17611 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17613 if(this.modalTitle.length){
17614 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17617 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17619 if(this.mobile_restrict_height && listHeight < bodyHeight){
17620 this.touchViewBodyEl.setHeight(listHeight);
17625 if(firstChecked && listHeight > bodyHeight){
17626 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17631 onTouchViewLoadException : function()
17633 this.hideTouchView();
17636 onTouchViewEmptyResults : function()
17638 this.clearTouchView();
17640 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17642 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646 clearTouchView : function()
17648 this.touchViewListGroup.dom.innerHTML = '';
17651 onTouchViewClick : function(e, el, o)
17653 e.preventDefault();
17656 var rowIndex = o.rowIndex;
17658 var r = this.store.getAt(rowIndex);
17660 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17662 if(!this.multiple){
17663 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17664 c.dom.removeAttribute('checked');
17667 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17669 this.setFromData(r.data);
17671 var close = this.closeTriggerEl();
17677 this.hideTouchView();
17679 this.fireEvent('select', this, r, rowIndex);
17684 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17685 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17686 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691 this.addItem(r.data);
17692 this.tickItems.push(r.data);
17696 getAutoCreateNativeIOS : function()
17699 cls: 'form-group' //input-group,
17704 cls : 'roo-ios-select'
17708 combobox.name = this.name;
17711 if (this.disabled) {
17712 combobox.disabled = true;
17715 var settings = this;
17717 ['xs','sm','md','lg'].map(function(size){
17718 if (settings[size]) {
17719 cfg.cls += ' col-' + size + '-' + settings[size];
17729 initIOSView : function()
17731 this.store.on('load', this.onIOSViewLoad, this);
17736 onIOSViewLoad : function()
17738 if(this.store.getCount() < 1){
17742 this.clearIOSView();
17744 if(this.allowBlank) {
17746 var default_text = '-- SELECT --';
17748 if(this.placeholder.length){
17749 default_text = this.placeholder;
17752 if(this.emptyTitle.length){
17753 default_text += ' - ' + this.emptyTitle + ' -';
17756 var opt = this.inputEl().createChild({
17759 html : default_text
17763 o[this.valueField] = 0;
17764 o[this.displayField] = default_text;
17766 this.ios_options.push({
17773 this.store.data.each(function(d, rowIndex){
17777 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17778 html = d.data[this.displayField];
17783 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17784 value = d.data[this.valueField];
17793 if(this.value == d.data[this.valueField]){
17794 option['selected'] = true;
17797 var opt = this.inputEl().createChild(option);
17799 this.ios_options.push({
17806 this.inputEl().on('change', function(){
17807 this.fireEvent('select', this);
17812 clearIOSView: function()
17814 this.inputEl().dom.innerHTML = '';
17816 this.ios_options = [];
17819 setIOSValue: function(v)
17823 if(!this.ios_options){
17827 Roo.each(this.ios_options, function(opts){
17829 opts.el.dom.removeAttribute('selected');
17831 if(opts.data[this.valueField] != v){
17835 opts.el.dom.setAttribute('selected', true);
17841 * @cfg {Boolean} grow
17845 * @cfg {Number} growMin
17849 * @cfg {Number} growMax
17858 Roo.apply(Roo.bootstrap.ComboBox, {
17862 cls: 'modal-header',
17884 cls: 'list-group-item',
17888 cls: 'roo-combobox-list-group-item-value'
17892 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17906 listItemCheckbox : {
17908 cls: 'list-group-item',
17912 cls: 'roo-combobox-list-group-item-value'
17916 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17932 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17937 cls: 'modal-footer',
17945 cls: 'col-xs-6 text-left',
17948 cls: 'btn btn-danger roo-touch-view-cancel',
17954 cls: 'col-xs-6 text-right',
17957 cls: 'btn btn-success roo-touch-view-ok',
17968 Roo.apply(Roo.bootstrap.ComboBox, {
17970 touchViewTemplate : {
17972 cls: 'modal fade roo-combobox-touch-view',
17976 cls: 'modal-dialog',
17977 style : 'position:fixed', // we have to fix position....
17981 cls: 'modal-content',
17983 Roo.bootstrap.ComboBox.header,
17984 Roo.bootstrap.ComboBox.body,
17985 Roo.bootstrap.ComboBox.footer
17994 * Ext JS Library 1.1.1
17995 * Copyright(c) 2006-2007, Ext JS, LLC.
17997 * Originally Released Under LGPL - original licence link has changed is not relivant.
18000 * <script type="text/javascript">
18005 * @extends Roo.util.Observable
18006 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18007 * This class also supports single and multi selection modes. <br>
18008 * Create a data model bound view:
18010 var store = new Roo.data.Store(...);
18012 var view = new Roo.View({
18014 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18016 singleSelect: true,
18017 selectedClass: "ydataview-selected",
18021 // listen for node click?
18022 view.on("click", function(vw, index, node, e){
18023 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027 dataModel.load("foobar.xml");
18029 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18031 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18032 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18034 * Note: old style constructor is still suported (container, template, config)
18037 * Create a new View
18038 * @param {Object} config The config object
18041 Roo.View = function(config, depreciated_tpl, depreciated_config){
18043 this.parent = false;
18045 if (typeof(depreciated_tpl) == 'undefined') {
18046 // new way.. - universal constructor.
18047 Roo.apply(this, config);
18048 this.el = Roo.get(this.el);
18051 this.el = Roo.get(config);
18052 this.tpl = depreciated_tpl;
18053 Roo.apply(this, depreciated_config);
18055 this.wrapEl = this.el.wrap().wrap();
18056 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18059 if(typeof(this.tpl) == "string"){
18060 this.tpl = new Roo.Template(this.tpl);
18062 // support xtype ctors..
18063 this.tpl = new Roo.factory(this.tpl, Roo);
18067 this.tpl.compile();
18072 * @event beforeclick
18073 * Fires before a click is processed. Returns false to cancel the default action.
18074 * @param {Roo.View} this
18075 * @param {Number} index The index of the target node
18076 * @param {HTMLElement} node The target node
18077 * @param {Roo.EventObject} e The raw event object
18079 "beforeclick" : true,
18082 * Fires when a template node is clicked.
18083 * @param {Roo.View} this
18084 * @param {Number} index The index of the target node
18085 * @param {HTMLElement} node The target node
18086 * @param {Roo.EventObject} e The raw event object
18091 * Fires when a template node is double clicked.
18092 * @param {Roo.View} this
18093 * @param {Number} index The index of the target node
18094 * @param {HTMLElement} node The target node
18095 * @param {Roo.EventObject} e The raw event object
18099 * @event contextmenu
18100 * Fires when a template node is right clicked.
18101 * @param {Roo.View} this
18102 * @param {Number} index The index of the target node
18103 * @param {HTMLElement} node The target node
18104 * @param {Roo.EventObject} e The raw event object
18106 "contextmenu" : true,
18108 * @event selectionchange
18109 * Fires when the selected nodes change.
18110 * @param {Roo.View} this
18111 * @param {Array} selections Array of the selected nodes
18113 "selectionchange" : true,
18116 * @event beforeselect
18117 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18118 * @param {Roo.View} this
18119 * @param {HTMLElement} node The node to be selected
18120 * @param {Array} selections Array of currently selected nodes
18122 "beforeselect" : true,
18124 * @event preparedata
18125 * Fires on every row to render, to allow you to change the data.
18126 * @param {Roo.View} this
18127 * @param {Object} data to be rendered (change this)
18129 "preparedata" : true
18137 "click": this.onClick,
18138 "dblclick": this.onDblClick,
18139 "contextmenu": this.onContextMenu,
18143 this.selections = [];
18145 this.cmp = new Roo.CompositeElementLite([]);
18147 this.store = Roo.factory(this.store, Roo.data);
18148 this.setStore(this.store, true);
18151 if ( this.footer && this.footer.xtype) {
18153 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18155 this.footer.dataSource = this.store;
18156 this.footer.container = fctr;
18157 this.footer = Roo.factory(this.footer, Roo);
18158 fctr.insertFirst(this.el);
18160 // this is a bit insane - as the paging toolbar seems to detach the el..
18161 // dom.parentNode.parentNode.parentNode
18162 // they get detached?
18166 Roo.View.superclass.constructor.call(this);
18171 Roo.extend(Roo.View, Roo.util.Observable, {
18174 * @cfg {Roo.data.Store} store Data store to load data from.
18179 * @cfg {String|Roo.Element} el The container element.
18184 * @cfg {String|Roo.Template} tpl The template used by this View
18188 * @cfg {String} dataName the named area of the template to use as the data area
18189 * Works with domtemplates roo-name="name"
18193 * @cfg {String} selectedClass The css class to add to selected nodes
18195 selectedClass : "x-view-selected",
18197 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18202 * @cfg {String} text to display on mask (default Loading)
18206 * @cfg {Boolean} multiSelect Allow multiple selection
18208 multiSelect : false,
18210 * @cfg {Boolean} singleSelect Allow single selection
18212 singleSelect: false,
18215 * @cfg {Boolean} toggleSelect - selecting
18217 toggleSelect : false,
18220 * @cfg {Boolean} tickable - selecting
18225 * Returns the element this view is bound to.
18226 * @return {Roo.Element}
18228 getEl : function(){
18229 return this.wrapEl;
18235 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18237 refresh : function(){
18238 //Roo.log('refresh');
18241 // if we are using something like 'domtemplate', then
18242 // the what gets used is:
18243 // t.applySubtemplate(NAME, data, wrapping data..)
18244 // the outer template then get' applied with
18245 // the store 'extra data'
18246 // and the body get's added to the
18247 // roo-name="data" node?
18248 // <span class='roo-tpl-{name}'></span> ?????
18252 this.clearSelections();
18253 this.el.update("");
18255 var records = this.store.getRange();
18256 if(records.length < 1) {
18258 // is this valid?? = should it render a template??
18260 this.el.update(this.emptyText);
18264 if (this.dataName) {
18265 this.el.update(t.apply(this.store.meta)); //????
18266 el = this.el.child('.roo-tpl-' + this.dataName);
18269 for(var i = 0, len = records.length; i < len; i++){
18270 var data = this.prepareData(records[i].data, i, records[i]);
18271 this.fireEvent("preparedata", this, data, i, records[i]);
18273 var d = Roo.apply({}, data);
18276 Roo.apply(d, {'roo-id' : Roo.id()});
18280 Roo.each(this.parent.item, function(item){
18281 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18284 Roo.apply(d, {'roo-data-checked' : 'checked'});
18288 html[html.length] = Roo.util.Format.trim(
18290 t.applySubtemplate(this.dataName, d, this.store.meta) :
18297 el.update(html.join(""));
18298 this.nodes = el.dom.childNodes;
18299 this.updateIndexes(0);
18304 * Function to override to reformat the data that is sent to
18305 * the template for each node.
18306 * DEPRICATED - use the preparedata event handler.
18307 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18308 * a JSON object for an UpdateManager bound view).
18310 prepareData : function(data, index, record)
18312 this.fireEvent("preparedata", this, data, index, record);
18316 onUpdate : function(ds, record){
18317 // Roo.log('on update');
18318 this.clearSelections();
18319 var index = this.store.indexOf(record);
18320 var n = this.nodes[index];
18321 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18322 n.parentNode.removeChild(n);
18323 this.updateIndexes(index, index);
18329 onAdd : function(ds, records, index)
18331 //Roo.log(['on Add', ds, records, index] );
18332 this.clearSelections();
18333 if(this.nodes.length == 0){
18337 var n = this.nodes[index];
18338 for(var i = 0, len = records.length; i < len; i++){
18339 var d = this.prepareData(records[i].data, i, records[i]);
18341 this.tpl.insertBefore(n, d);
18344 this.tpl.append(this.el, d);
18347 this.updateIndexes(index);
18350 onRemove : function(ds, record, index){
18351 // Roo.log('onRemove');
18352 this.clearSelections();
18353 var el = this.dataName ?
18354 this.el.child('.roo-tpl-' + this.dataName) :
18357 el.dom.removeChild(this.nodes[index]);
18358 this.updateIndexes(index);
18362 * Refresh an individual node.
18363 * @param {Number} index
18365 refreshNode : function(index){
18366 this.onUpdate(this.store, this.store.getAt(index));
18369 updateIndexes : function(startIndex, endIndex){
18370 var ns = this.nodes;
18371 startIndex = startIndex || 0;
18372 endIndex = endIndex || ns.length - 1;
18373 for(var i = startIndex; i <= endIndex; i++){
18374 ns[i].nodeIndex = i;
18379 * Changes the data store this view uses and refresh the view.
18380 * @param {Store} store
18382 setStore : function(store, initial){
18383 if(!initial && this.store){
18384 this.store.un("datachanged", this.refresh);
18385 this.store.un("add", this.onAdd);
18386 this.store.un("remove", this.onRemove);
18387 this.store.un("update", this.onUpdate);
18388 this.store.un("clear", this.refresh);
18389 this.store.un("beforeload", this.onBeforeLoad);
18390 this.store.un("load", this.onLoad);
18391 this.store.un("loadexception", this.onLoad);
18395 store.on("datachanged", this.refresh, this);
18396 store.on("add", this.onAdd, this);
18397 store.on("remove", this.onRemove, this);
18398 store.on("update", this.onUpdate, this);
18399 store.on("clear", this.refresh, this);
18400 store.on("beforeload", this.onBeforeLoad, this);
18401 store.on("load", this.onLoad, this);
18402 store.on("loadexception", this.onLoad, this);
18410 * onbeforeLoad - masks the loading area.
18413 onBeforeLoad : function(store,opts)
18415 //Roo.log('onBeforeLoad');
18417 this.el.update("");
18419 this.el.mask(this.mask ? this.mask : "Loading" );
18421 onLoad : function ()
18428 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18429 * @param {HTMLElement} node
18430 * @return {HTMLElement} The template node
18432 findItemFromChild : function(node){
18433 var el = this.dataName ?
18434 this.el.child('.roo-tpl-' + this.dataName,true) :
18437 if(!node || node.parentNode == el){
18440 var p = node.parentNode;
18441 while(p && p != el){
18442 if(p.parentNode == el){
18451 onClick : function(e){
18452 var item = this.findItemFromChild(e.getTarget());
18454 var index = this.indexOf(item);
18455 if(this.onItemClick(item, index, e) !== false){
18456 this.fireEvent("click", this, index, item, e);
18459 this.clearSelections();
18464 onContextMenu : function(e){
18465 var item = this.findItemFromChild(e.getTarget());
18467 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18472 onDblClick : function(e){
18473 var item = this.findItemFromChild(e.getTarget());
18475 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479 onItemClick : function(item, index, e)
18481 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18484 if (this.toggleSelect) {
18485 var m = this.isSelected(item) ? 'unselect' : 'select';
18488 _t[m](item, true, false);
18491 if(this.multiSelect || this.singleSelect){
18492 if(this.multiSelect && e.shiftKey && this.lastSelection){
18493 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18495 this.select(item, this.multiSelect && e.ctrlKey);
18496 this.lastSelection = item;
18499 if(!this.tickable){
18500 e.preventDefault();
18508 * Get the number of selected nodes.
18511 getSelectionCount : function(){
18512 return this.selections.length;
18516 * Get the currently selected nodes.
18517 * @return {Array} An array of HTMLElements
18519 getSelectedNodes : function(){
18520 return this.selections;
18524 * Get the indexes of the selected nodes.
18527 getSelectedIndexes : function(){
18528 var indexes = [], s = this.selections;
18529 for(var i = 0, len = s.length; i < len; i++){
18530 indexes.push(s[i].nodeIndex);
18536 * Clear all selections
18537 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18539 clearSelections : function(suppressEvent){
18540 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18541 this.cmp.elements = this.selections;
18542 this.cmp.removeClass(this.selectedClass);
18543 this.selections = [];
18544 if(!suppressEvent){
18545 this.fireEvent("selectionchange", this, this.selections);
18551 * Returns true if the passed node is selected
18552 * @param {HTMLElement/Number} node The node or node index
18553 * @return {Boolean}
18555 isSelected : function(node){
18556 var s = this.selections;
18560 node = this.getNode(node);
18561 return s.indexOf(node) !== -1;
18566 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18567 * @param {Boolean} keepExisting (optional) true to keep existing selections
18568 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18570 select : function(nodeInfo, keepExisting, suppressEvent){
18571 if(nodeInfo instanceof Array){
18573 this.clearSelections(true);
18575 for(var i = 0, len = nodeInfo.length; i < len; i++){
18576 this.select(nodeInfo[i], true, true);
18580 var node = this.getNode(nodeInfo);
18581 if(!node || this.isSelected(node)){
18582 return; // already selected.
18585 this.clearSelections(true);
18588 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18589 Roo.fly(node).addClass(this.selectedClass);
18590 this.selections.push(node);
18591 if(!suppressEvent){
18592 this.fireEvent("selectionchange", this, this.selections);
18600 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18601 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18602 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18604 unselect : function(nodeInfo, keepExisting, suppressEvent)
18606 if(nodeInfo instanceof Array){
18607 Roo.each(this.selections, function(s) {
18608 this.unselect(s, nodeInfo);
18612 var node = this.getNode(nodeInfo);
18613 if(!node || !this.isSelected(node)){
18614 //Roo.log("not selected");
18615 return; // not selected.
18619 Roo.each(this.selections, function(s) {
18621 Roo.fly(node).removeClass(this.selectedClass);
18628 this.selections= ns;
18629 this.fireEvent("selectionchange", this, this.selections);
18633 * Gets a template node.
18634 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18635 * @return {HTMLElement} The node or null if it wasn't found
18637 getNode : function(nodeInfo){
18638 if(typeof nodeInfo == "string"){
18639 return document.getElementById(nodeInfo);
18640 }else if(typeof nodeInfo == "number"){
18641 return this.nodes[nodeInfo];
18647 * Gets a range template nodes.
18648 * @param {Number} startIndex
18649 * @param {Number} endIndex
18650 * @return {Array} An array of nodes
18652 getNodes : function(start, end){
18653 var ns = this.nodes;
18654 start = start || 0;
18655 end = typeof end == "undefined" ? ns.length - 1 : end;
18658 for(var i = start; i <= end; i++){
18662 for(var i = start; i >= end; i--){
18670 * Finds the index of the passed node
18671 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18672 * @return {Number} The index of the node or -1
18674 indexOf : function(node){
18675 node = this.getNode(node);
18676 if(typeof node.nodeIndex == "number"){
18677 return node.nodeIndex;
18679 var ns = this.nodes;
18680 for(var i = 0, len = ns.length; i < len; i++){
18691 * based on jquery fullcalendar
18695 Roo.bootstrap = Roo.bootstrap || {};
18697 * @class Roo.bootstrap.Calendar
18698 * @extends Roo.bootstrap.Component
18699 * Bootstrap Calendar class
18700 * @cfg {Boolean} loadMask (true|false) default false
18701 * @cfg {Object} header generate the user specific header of the calendar, default false
18704 * Create a new Container
18705 * @param {Object} config The config object
18710 Roo.bootstrap.Calendar = function(config){
18711 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715 * Fires when a date is selected
18716 * @param {DatePicker} this
18717 * @param {Date} date The selected date
18721 * @event monthchange
18722 * Fires when the displayed month changes
18723 * @param {DatePicker} this
18724 * @param {Date} date The selected month
18726 'monthchange': true,
18728 * @event evententer
18729 * Fires when mouse over an event
18730 * @param {Calendar} this
18731 * @param {event} Event
18733 'evententer': true,
18735 * @event eventleave
18736 * Fires when the mouse leaves an
18737 * @param {Calendar} this
18740 'eventleave': true,
18742 * @event eventclick
18743 * Fires when the mouse click an
18744 * @param {Calendar} this
18753 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18756 * @cfg {Number} startDay
18757 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18765 getAutoCreate : function(){
18768 var fc_button = function(name, corner, style, content ) {
18769 return Roo.apply({},{
18771 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18773 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18776 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18787 style : 'width:100%',
18794 cls : 'fc-header-left',
18796 fc_button('prev', 'left', 'arrow', '‹' ),
18797 fc_button('next', 'right', 'arrow', '›' ),
18798 { tag: 'span', cls: 'fc-header-space' },
18799 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18807 cls : 'fc-header-center',
18811 cls: 'fc-header-title',
18814 html : 'month / year'
18822 cls : 'fc-header-right',
18824 /* fc_button('month', 'left', '', 'month' ),
18825 fc_button('week', '', '', 'week' ),
18826 fc_button('day', 'right', '', 'day' )
18838 header = this.header;
18841 var cal_heads = function() {
18843 // fixme - handle this.
18845 for (var i =0; i < Date.dayNames.length; i++) {
18846 var d = Date.dayNames[i];
18849 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18850 html : d.substring(0,3)
18854 ret[0].cls += ' fc-first';
18855 ret[6].cls += ' fc-last';
18858 var cal_cell = function(n) {
18861 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18866 cls: 'fc-day-number',
18870 cls: 'fc-day-content',
18874 style: 'position: relative;' // height: 17px;
18886 var cal_rows = function() {
18889 for (var r = 0; r < 6; r++) {
18896 for (var i =0; i < Date.dayNames.length; i++) {
18897 var d = Date.dayNames[i];
18898 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18901 row.cn[0].cls+=' fc-first';
18902 row.cn[0].cn[0].style = 'min-height:90px';
18903 row.cn[6].cls+=' fc-last';
18907 ret[0].cls += ' fc-first';
18908 ret[4].cls += ' fc-prev-last';
18909 ret[5].cls += ' fc-last';
18916 cls: 'fc-border-separate',
18917 style : 'width:100%',
18925 cls : 'fc-first fc-last',
18943 cls : 'fc-content',
18944 style : "position: relative;",
18947 cls : 'fc-view fc-view-month fc-grid',
18948 style : 'position: relative',
18949 unselectable : 'on',
18952 cls : 'fc-event-container',
18953 style : 'position:absolute;z-index:8;top:0;left:0;'
18971 initEvents : function()
18974 throw "can not find store for calendar";
18980 style: "text-align:center",
18984 style: "background-color:white;width:50%;margin:250 auto",
18988 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18999 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19001 var size = this.el.select('.fc-content', true).first().getSize();
19002 this.maskEl.setSize(size.width, size.height);
19003 this.maskEl.enableDisplayMode("block");
19004 if(!this.loadMask){
19005 this.maskEl.hide();
19008 this.store = Roo.factory(this.store, Roo.data);
19009 this.store.on('load', this.onLoad, this);
19010 this.store.on('beforeload', this.onBeforeLoad, this);
19014 this.cells = this.el.select('.fc-day',true);
19015 //Roo.log(this.cells);
19016 this.textNodes = this.el.query('.fc-day-number');
19017 this.cells.addClassOnOver('fc-state-hover');
19019 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19020 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19021 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19022 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19024 this.on('monthchange', this.onMonthChange, this);
19026 this.update(new Date().clearTime());
19029 resize : function() {
19030 var sz = this.el.getSize();
19032 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19033 this.el.select('.fc-day-content div',true).setHeight(34);
19038 showPrevMonth : function(e){
19039 this.update(this.activeDate.add("mo", -1));
19041 showToday : function(e){
19042 this.update(new Date().clearTime());
19045 showNextMonth : function(e){
19046 this.update(this.activeDate.add("mo", 1));
19050 showPrevYear : function(){
19051 this.update(this.activeDate.add("y", -1));
19055 showNextYear : function(){
19056 this.update(this.activeDate.add("y", 1));
19061 update : function(date)
19063 var vd = this.activeDate;
19064 this.activeDate = date;
19065 // if(vd && this.el){
19066 // var t = date.getTime();
19067 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19068 // Roo.log('using add remove');
19070 // this.fireEvent('monthchange', this, date);
19072 // this.cells.removeClass("fc-state-highlight");
19073 // this.cells.each(function(c){
19074 // if(c.dateValue == t){
19075 // c.addClass("fc-state-highlight");
19076 // setTimeout(function(){
19077 // try{c.dom.firstChild.focus();}catch(e){}
19087 var days = date.getDaysInMonth();
19089 var firstOfMonth = date.getFirstDateOfMonth();
19090 var startingPos = firstOfMonth.getDay()-this.startDay;
19092 if(startingPos < this.startDay){
19096 var pm = date.add(Date.MONTH, -1);
19097 var prevStart = pm.getDaysInMonth()-startingPos;
19099 this.cells = this.el.select('.fc-day',true);
19100 this.textNodes = this.el.query('.fc-day-number');
19101 this.cells.addClassOnOver('fc-state-hover');
19103 var cells = this.cells.elements;
19104 var textEls = this.textNodes;
19106 Roo.each(cells, function(cell){
19107 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19110 days += startingPos;
19112 // convert everything to numbers so it's fast
19113 var day = 86400000;
19114 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19117 //Roo.log(prevStart);
19119 var today = new Date().clearTime().getTime();
19120 var sel = date.clearTime().getTime();
19121 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19122 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19123 var ddMatch = this.disabledDatesRE;
19124 var ddText = this.disabledDatesText;
19125 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19126 var ddaysText = this.disabledDaysText;
19127 var format = this.format;
19129 var setCellClass = function(cal, cell){
19133 //Roo.log('set Cell Class');
19135 var t = d.getTime();
19139 cell.dateValue = t;
19141 cell.className += " fc-today";
19142 cell.className += " fc-state-highlight";
19143 cell.title = cal.todayText;
19146 // disable highlight in other month..
19147 //cell.className += " fc-state-highlight";
19152 cell.className = " fc-state-disabled";
19153 cell.title = cal.minText;
19157 cell.className = " fc-state-disabled";
19158 cell.title = cal.maxText;
19162 if(ddays.indexOf(d.getDay()) != -1){
19163 cell.title = ddaysText;
19164 cell.className = " fc-state-disabled";
19167 if(ddMatch && format){
19168 var fvalue = d.dateFormat(format);
19169 if(ddMatch.test(fvalue)){
19170 cell.title = ddText.replace("%0", fvalue);
19171 cell.className = " fc-state-disabled";
19175 if (!cell.initialClassName) {
19176 cell.initialClassName = cell.dom.className;
19179 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19184 for(; i < startingPos; i++) {
19185 textEls[i].innerHTML = (++prevStart);
19186 d.setDate(d.getDate()+1);
19188 cells[i].className = "fc-past fc-other-month";
19189 setCellClass(this, cells[i]);
19194 for(; i < days; i++){
19195 intDay = i - startingPos + 1;
19196 textEls[i].innerHTML = (intDay);
19197 d.setDate(d.getDate()+1);
19199 cells[i].className = ''; // "x-date-active";
19200 setCellClass(this, cells[i]);
19204 for(; i < 42; i++) {
19205 textEls[i].innerHTML = (++extraDays);
19206 d.setDate(d.getDate()+1);
19208 cells[i].className = "fc-future fc-other-month";
19209 setCellClass(this, cells[i]);
19212 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19214 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19216 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19217 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19219 if(totalRows != 6){
19220 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19221 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19224 this.fireEvent('monthchange', this, date);
19228 if(!this.internalRender){
19229 var main = this.el.dom.firstChild;
19230 var w = main.offsetWidth;
19231 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19232 Roo.fly(main).setWidth(w);
19233 this.internalRender = true;
19234 // opera does not respect the auto grow header center column
19235 // then, after it gets a width opera refuses to recalculate
19236 // without a second pass
19237 if(Roo.isOpera && !this.secondPass){
19238 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19239 this.secondPass = true;
19240 this.update.defer(10, this, [date]);
19247 findCell : function(dt) {
19248 dt = dt.clearTime().getTime();
19250 this.cells.each(function(c){
19251 //Roo.log("check " +c.dateValue + '?=' + dt);
19252 if(c.dateValue == dt){
19262 findCells : function(ev) {
19263 var s = ev.start.clone().clearTime().getTime();
19265 var e= ev.end.clone().clearTime().getTime();
19268 this.cells.each(function(c){
19269 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19271 if(c.dateValue > e){
19274 if(c.dateValue < s){
19283 // findBestRow: function(cells)
19287 // for (var i =0 ; i < cells.length;i++) {
19288 // ret = Math.max(cells[i].rows || 0,ret);
19295 addItem : function(ev)
19297 // look for vertical location slot in
19298 var cells = this.findCells(ev);
19300 // ev.row = this.findBestRow(cells);
19302 // work out the location.
19306 for(var i =0; i < cells.length; i++) {
19308 cells[i].row = cells[0].row;
19311 cells[i].row = cells[i].row + 1;
19321 if (crow.start.getY() == cells[i].getY()) {
19323 crow.end = cells[i];
19340 cells[0].events.push(ev);
19342 this.calevents.push(ev);
19345 clearEvents: function() {
19347 if(!this.calevents){
19351 Roo.each(this.cells.elements, function(c){
19357 Roo.each(this.calevents, function(e) {
19358 Roo.each(e.els, function(el) {
19359 el.un('mouseenter' ,this.onEventEnter, this);
19360 el.un('mouseleave' ,this.onEventLeave, this);
19365 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19371 renderEvents: function()
19375 this.cells.each(function(c) {
19384 if(c.row != c.events.length){
19385 r = 4 - (4 - (c.row - c.events.length));
19388 c.events = ev.slice(0, r);
19389 c.more = ev.slice(r);
19391 if(c.more.length && c.more.length == 1){
19392 c.events.push(c.more.pop());
19395 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399 this.cells.each(function(c) {
19401 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19404 for (var e = 0; e < c.events.length; e++){
19405 var ev = c.events[e];
19406 var rows = ev.rows;
19408 for(var i = 0; i < rows.length; i++) {
19410 // how many rows should it span..
19413 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19414 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19416 unselectable : "on",
19419 cls: 'fc-event-inner',
19423 // cls: 'fc-event-time',
19424 // html : cells.length > 1 ? '' : ev.time
19428 cls: 'fc-event-title',
19429 html : String.format('{0}', ev.title)
19436 cls: 'ui-resizable-handle ui-resizable-e',
19437 html : '  '
19444 cfg.cls += ' fc-event-start';
19446 if ((i+1) == rows.length) {
19447 cfg.cls += ' fc-event-end';
19450 var ctr = _this.el.select('.fc-event-container',true).first();
19451 var cg = ctr.createChild(cfg);
19453 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19454 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19456 var r = (c.more.length) ? 1 : 0;
19457 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19458 cg.setWidth(ebox.right - sbox.x -2);
19460 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19461 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19462 cg.on('click', _this.onEventClick, _this, ev);
19473 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19474 style : 'position: absolute',
19475 unselectable : "on",
19478 cls: 'fc-event-inner',
19482 cls: 'fc-event-title',
19490 cls: 'ui-resizable-handle ui-resizable-e',
19491 html : '  '
19497 var ctr = _this.el.select('.fc-event-container',true).first();
19498 var cg = ctr.createChild(cfg);
19500 var sbox = c.select('.fc-day-content',true).first().getBox();
19501 var ebox = c.select('.fc-day-content',true).first().getBox();
19503 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19504 cg.setWidth(ebox.right - sbox.x -2);
19506 cg.on('click', _this.onMoreEventClick, _this, c.more);
19516 onEventEnter: function (e, el,event,d) {
19517 this.fireEvent('evententer', this, el, event);
19520 onEventLeave: function (e, el,event,d) {
19521 this.fireEvent('eventleave', this, el, event);
19524 onEventClick: function (e, el,event,d) {
19525 this.fireEvent('eventclick', this, el, event);
19528 onMonthChange: function () {
19532 onMoreEventClick: function(e, el, more)
19536 this.calpopover.placement = 'right';
19537 this.calpopover.setTitle('More');
19539 this.calpopover.setContent('');
19541 var ctr = this.calpopover.el.select('.popover-content', true).first();
19543 Roo.each(more, function(m){
19545 cls : 'fc-event-hori fc-event-draggable',
19548 var cg = ctr.createChild(cfg);
19550 cg.on('click', _this.onEventClick, _this, m);
19553 this.calpopover.show(el);
19558 onLoad: function ()
19560 this.calevents = [];
19563 if(this.store.getCount() > 0){
19564 this.store.data.each(function(d){
19567 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19568 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19569 time : d.data.start_time,
19570 title : d.data.title,
19571 description : d.data.description,
19572 venue : d.data.venue
19577 this.renderEvents();
19579 if(this.calevents.length && this.loadMask){
19580 this.maskEl.hide();
19584 onBeforeLoad: function()
19586 this.clearEvents();
19588 this.maskEl.show();
19602 * @class Roo.bootstrap.Popover
19603 * @extends Roo.bootstrap.Component
19604 * Bootstrap Popover class
19605 * @cfg {String} html contents of the popover (or false to use children..)
19606 * @cfg {String} title of popover (or false to hide)
19607 * @cfg {String} placement how it is placed
19608 * @cfg {String} trigger click || hover (or false to trigger manually)
19609 * @cfg {String} over what (parent or false to trigger manually.)
19610 * @cfg {Number} delay - delay before showing
19613 * Create a new Popover
19614 * @param {Object} config The config object
19617 Roo.bootstrap.Popover = function(config){
19618 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19624 * After the popover show
19626 * @param {Roo.bootstrap.Popover} this
19631 * After the popover hide
19633 * @param {Roo.bootstrap.Popover} this
19639 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19641 title: 'Fill in a title',
19644 placement : 'right',
19645 trigger : 'hover', // hover
19651 can_build_overlaid : false,
19653 getChildContainer : function()
19655 return this.el.select('.popover-content',true).first();
19658 getAutoCreate : function(){
19661 cls : 'popover roo-dynamic',
19662 style: 'display:block',
19668 cls : 'popover-inner',
19672 cls: 'popover-title popover-header',
19676 cls : 'popover-content popover-body',
19687 setTitle: function(str)
19690 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19692 setContent: function(str)
19695 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19697 // as it get's added to the bottom of the page.
19698 onRender : function(ct, position)
19700 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19702 var cfg = Roo.apply({}, this.getAutoCreate());
19706 cfg.cls += ' ' + this.cls;
19709 cfg.style = this.style;
19711 //Roo.log("adding to ");
19712 this.el = Roo.get(document.body).createChild(cfg, position);
19713 // Roo.log(this.el);
19718 initEvents : function()
19720 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19721 this.el.enableDisplayMode('block');
19723 if (this.over === false) {
19726 if (this.triggers === false) {
19729 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19730 var triggers = this.trigger ? this.trigger.split(' ') : [];
19731 Roo.each(triggers, function(trigger) {
19733 if (trigger == 'click') {
19734 on_el.on('click', this.toggle, this);
19735 } else if (trigger != 'manual') {
19736 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19737 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19739 on_el.on(eventIn ,this.enter, this);
19740 on_el.on(eventOut, this.leave, this);
19751 toggle : function () {
19752 this.hoverState == 'in' ? this.leave() : this.enter();
19755 enter : function () {
19757 clearTimeout(this.timeout);
19759 this.hoverState = 'in';
19761 if (!this.delay || !this.delay.show) {
19766 this.timeout = setTimeout(function () {
19767 if (_t.hoverState == 'in') {
19770 }, this.delay.show)
19773 leave : function() {
19774 clearTimeout(this.timeout);
19776 this.hoverState = 'out';
19778 if (!this.delay || !this.delay.hide) {
19783 this.timeout = setTimeout(function () {
19784 if (_t.hoverState == 'out') {
19787 }, this.delay.hide)
19790 show : function (on_el)
19793 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19797 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19798 if (this.html !== false) {
19799 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19801 this.el.removeClass([
19802 'fade','top','bottom', 'left', 'right','in',
19803 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19805 if (!this.title.length) {
19806 this.el.select('.popover-title',true).hide();
19809 var placement = typeof this.placement == 'function' ?
19810 this.placement.call(this, this.el, on_el) :
19813 var autoToken = /\s?auto?\s?/i;
19814 var autoPlace = autoToken.test(placement);
19816 placement = placement.replace(autoToken, '') || 'top';
19820 //this.el.setXY([0,0]);
19822 this.el.dom.style.display='block';
19823 this.el.addClass(placement);
19825 //this.el.appendTo(on_el);
19827 var p = this.getPosition();
19828 var box = this.el.getBox();
19833 var align = Roo.bootstrap.Popover.alignment[placement];
19836 this.el.alignTo(on_el, align[0],align[1]);
19837 //var arrow = this.el.select('.arrow',true).first();
19838 //arrow.set(align[2],
19840 this.el.addClass('in');
19843 if (this.el.hasClass('fade')) {
19847 this.hoverState = 'in';
19849 this.fireEvent('show', this);
19854 this.el.setXY([0,0]);
19855 this.el.removeClass('in');
19857 this.hoverState = null;
19859 this.fireEvent('hide', this);
19864 Roo.bootstrap.Popover.alignment = {
19865 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19866 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19867 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19868 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19879 * @class Roo.bootstrap.Progress
19880 * @extends Roo.bootstrap.Component
19881 * Bootstrap Progress class
19882 * @cfg {Boolean} striped striped of the progress bar
19883 * @cfg {Boolean} active animated of the progress bar
19887 * Create a new Progress
19888 * @param {Object} config The config object
19891 Roo.bootstrap.Progress = function(config){
19892 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19895 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19900 getAutoCreate : function(){
19908 cfg.cls += ' progress-striped';
19912 cfg.cls += ' active';
19931 * @class Roo.bootstrap.ProgressBar
19932 * @extends Roo.bootstrap.Component
19933 * Bootstrap ProgressBar class
19934 * @cfg {Number} aria_valuenow aria-value now
19935 * @cfg {Number} aria_valuemin aria-value min
19936 * @cfg {Number} aria_valuemax aria-value max
19937 * @cfg {String} label label for the progress bar
19938 * @cfg {String} panel (success | info | warning | danger )
19939 * @cfg {String} role role of the progress bar
19940 * @cfg {String} sr_only text
19944 * Create a new ProgressBar
19945 * @param {Object} config The config object
19948 Roo.bootstrap.ProgressBar = function(config){
19949 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19952 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19956 aria_valuemax : 100,
19962 getAutoCreate : function()
19967 cls: 'progress-bar',
19968 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19980 cfg.role = this.role;
19983 if(this.aria_valuenow){
19984 cfg['aria-valuenow'] = this.aria_valuenow;
19987 if(this.aria_valuemin){
19988 cfg['aria-valuemin'] = this.aria_valuemin;
19991 if(this.aria_valuemax){
19992 cfg['aria-valuemax'] = this.aria_valuemax;
19995 if(this.label && !this.sr_only){
19996 cfg.html = this.label;
20000 cfg.cls += ' progress-bar-' + this.panel;
20006 update : function(aria_valuenow)
20008 this.aria_valuenow = aria_valuenow;
20010 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20025 * @class Roo.bootstrap.TabGroup
20026 * @extends Roo.bootstrap.Column
20027 * Bootstrap Column class
20028 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20029 * @cfg {Boolean} carousel true to make the group behave like a carousel
20030 * @cfg {Boolean} bullets show bullets for the panels
20031 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20032 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20033 * @cfg {Boolean} showarrow (true|false) show arrow default true
20036 * Create a new TabGroup
20037 * @param {Object} config The config object
20040 Roo.bootstrap.TabGroup = function(config){
20041 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20043 this.navId = Roo.id();
20046 Roo.bootstrap.TabGroup.register(this);
20050 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20053 transition : false,
20058 slideOnTouch : false,
20061 getAutoCreate : function()
20063 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20065 cfg.cls += ' tab-content';
20067 if (this.carousel) {
20068 cfg.cls += ' carousel slide';
20071 cls : 'carousel-inner',
20075 if(this.bullets && !Roo.isTouch){
20078 cls : 'carousel-bullets',
20082 if(this.bullets_cls){
20083 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20090 cfg.cn[0].cn.push(bullets);
20093 if(this.showarrow){
20094 cfg.cn[0].cn.push({
20096 class : 'carousel-arrow',
20100 class : 'carousel-prev',
20104 class : 'fa fa-chevron-left'
20110 class : 'carousel-next',
20114 class : 'fa fa-chevron-right'
20127 initEvents: function()
20129 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20130 // this.el.on("touchstart", this.onTouchStart, this);
20133 if(this.autoslide){
20136 this.slideFn = window.setInterval(function() {
20137 _this.showPanelNext();
20141 if(this.showarrow){
20142 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20143 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20149 // onTouchStart : function(e, el, o)
20151 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20155 // this.showPanelNext();
20159 getChildContainer : function()
20161 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20165 * register a Navigation item
20166 * @param {Roo.bootstrap.NavItem} the navitem to add
20168 register : function(item)
20170 this.tabs.push( item);
20171 item.navId = this.navId; // not really needed..
20176 getActivePanel : function()
20179 Roo.each(this.tabs, function(t) {
20189 getPanelByName : function(n)
20192 Roo.each(this.tabs, function(t) {
20193 if (t.tabId == n) {
20201 indexOfPanel : function(p)
20204 Roo.each(this.tabs, function(t,i) {
20205 if (t.tabId == p.tabId) {
20214 * show a specific panel
20215 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20216 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20218 showPanel : function (pan)
20220 if(this.transition || typeof(pan) == 'undefined'){
20221 Roo.log("waiting for the transitionend");
20225 if (typeof(pan) == 'number') {
20226 pan = this.tabs[pan];
20229 if (typeof(pan) == 'string') {
20230 pan = this.getPanelByName(pan);
20233 var cur = this.getActivePanel();
20236 Roo.log('pan or acitve pan is undefined');
20240 if (pan.tabId == this.getActivePanel().tabId) {
20244 if (false === cur.fireEvent('beforedeactivate')) {
20248 if(this.bullets > 0 && !Roo.isTouch){
20249 this.setActiveBullet(this.indexOfPanel(pan));
20252 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20254 //class="carousel-item carousel-item-next carousel-item-left"
20256 this.transition = true;
20257 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20258 var lr = dir == 'next' ? 'left' : 'right';
20259 pan.el.addClass(dir); // or prev
20260 pan.el.addClass('carousel-item-' + dir); // or prev
20261 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20262 cur.el.addClass(lr); // or right
20263 pan.el.addClass(lr);
20264 cur.el.addClass('carousel-item-' +lr); // or right
20265 pan.el.addClass('carousel-item-' +lr);
20269 cur.el.on('transitionend', function() {
20270 Roo.log("trans end?");
20272 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20273 pan.setActive(true);
20275 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20276 cur.setActive(false);
20278 _this.transition = false;
20280 }, this, { single: true } );
20285 cur.setActive(false);
20286 pan.setActive(true);
20291 showPanelNext : function()
20293 var i = this.indexOfPanel(this.getActivePanel());
20295 if (i >= this.tabs.length - 1 && !this.autoslide) {
20299 if (i >= this.tabs.length - 1 && this.autoslide) {
20303 this.showPanel(this.tabs[i+1]);
20306 showPanelPrev : function()
20308 var i = this.indexOfPanel(this.getActivePanel());
20310 if (i < 1 && !this.autoslide) {
20314 if (i < 1 && this.autoslide) {
20315 i = this.tabs.length;
20318 this.showPanel(this.tabs[i-1]);
20322 addBullet: function()
20324 if(!this.bullets || Roo.isTouch){
20327 var ctr = this.el.select('.carousel-bullets',true).first();
20328 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20329 var bullet = ctr.createChild({
20330 cls : 'bullet bullet-' + i
20331 },ctr.dom.lastChild);
20336 bullet.on('click', (function(e, el, o, ii, t){
20338 e.preventDefault();
20340 this.showPanel(ii);
20342 if(this.autoslide && this.slideFn){
20343 clearInterval(this.slideFn);
20344 this.slideFn = window.setInterval(function() {
20345 _this.showPanelNext();
20349 }).createDelegate(this, [i, bullet], true));
20354 setActiveBullet : function(i)
20360 Roo.each(this.el.select('.bullet', true).elements, function(el){
20361 el.removeClass('selected');
20364 var bullet = this.el.select('.bullet-' + i, true).first();
20370 bullet.addClass('selected');
20381 Roo.apply(Roo.bootstrap.TabGroup, {
20385 * register a Navigation Group
20386 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20388 register : function(navgrp)
20390 this.groups[navgrp.navId] = navgrp;
20394 * fetch a Navigation Group based on the navigation ID
20395 * if one does not exist , it will get created.
20396 * @param {string} the navgroup to add
20397 * @returns {Roo.bootstrap.NavGroup} the navgroup
20399 get: function(navId) {
20400 if (typeof(this.groups[navId]) == 'undefined') {
20401 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20403 return this.groups[navId] ;
20418 * @class Roo.bootstrap.TabPanel
20419 * @extends Roo.bootstrap.Component
20420 * Bootstrap TabPanel class
20421 * @cfg {Boolean} active panel active
20422 * @cfg {String} html panel content
20423 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20424 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20425 * @cfg {String} href click to link..
20426 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20430 * Create a new TabPanel
20431 * @param {Object} config The config object
20434 Roo.bootstrap.TabPanel = function(config){
20435 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20439 * Fires when the active status changes
20440 * @param {Roo.bootstrap.TabPanel} this
20441 * @param {Boolean} state the new state
20446 * @event beforedeactivate
20447 * Fires before a tab is de-activated - can be used to do validation on a form.
20448 * @param {Roo.bootstrap.TabPanel} this
20449 * @return {Boolean} false if there is an error
20452 'beforedeactivate': true
20455 this.tabId = this.tabId || Roo.id();
20459 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20466 touchSlide : false,
20467 getAutoCreate : function(){
20472 // item is needed for carousel - not sure if it has any effect otherwise
20473 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20474 html: this.html || ''
20478 cfg.cls += ' active';
20482 cfg.tabId = this.tabId;
20490 initEvents: function()
20492 var p = this.parent();
20494 this.navId = this.navId || p.navId;
20496 if (typeof(this.navId) != 'undefined') {
20497 // not really needed.. but just in case.. parent should be a NavGroup.
20498 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20502 var i = tg.tabs.length - 1;
20504 if(this.active && tg.bullets > 0 && i < tg.bullets){
20505 tg.setActiveBullet(i);
20509 this.el.on('click', this.onClick, this);
20511 if(Roo.isTouch && this.touchSlide){
20512 this.el.on("touchstart", this.onTouchStart, this);
20513 this.el.on("touchmove", this.onTouchMove, this);
20514 this.el.on("touchend", this.onTouchEnd, this);
20519 onRender : function(ct, position)
20521 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20524 setActive : function(state)
20526 Roo.log("panel - set active " + this.tabId + "=" + state);
20528 this.active = state;
20530 this.el.removeClass('active');
20532 } else if (!this.el.hasClass('active')) {
20533 this.el.addClass('active');
20536 this.fireEvent('changed', this, state);
20539 onClick : function(e)
20541 e.preventDefault();
20543 if(!this.href.length){
20547 window.location.href = this.href;
20556 onTouchStart : function(e)
20558 this.swiping = false;
20560 this.startX = e.browserEvent.touches[0].clientX;
20561 this.startY = e.browserEvent.touches[0].clientY;
20564 onTouchMove : function(e)
20566 this.swiping = true;
20568 this.endX = e.browserEvent.touches[0].clientX;
20569 this.endY = e.browserEvent.touches[0].clientY;
20572 onTouchEnd : function(e)
20579 var tabGroup = this.parent();
20581 if(this.endX > this.startX){ // swiping right
20582 tabGroup.showPanelPrev();
20586 if(this.startX > this.endX){ // swiping left
20587 tabGroup.showPanelNext();
20606 * @class Roo.bootstrap.DateField
20607 * @extends Roo.bootstrap.Input
20608 * Bootstrap DateField class
20609 * @cfg {Number} weekStart default 0
20610 * @cfg {String} viewMode default empty, (months|years)
20611 * @cfg {String} minViewMode default empty, (months|years)
20612 * @cfg {Number} startDate default -Infinity
20613 * @cfg {Number} endDate default Infinity
20614 * @cfg {Boolean} todayHighlight default false
20615 * @cfg {Boolean} todayBtn default false
20616 * @cfg {Boolean} calendarWeeks default false
20617 * @cfg {Object} daysOfWeekDisabled default empty
20618 * @cfg {Boolean} singleMode default false (true | false)
20620 * @cfg {Boolean} keyboardNavigation default true
20621 * @cfg {String} language default en
20624 * Create a new DateField
20625 * @param {Object} config The config object
20628 Roo.bootstrap.DateField = function(config){
20629 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20633 * Fires when this field show.
20634 * @param {Roo.bootstrap.DateField} this
20635 * @param {Mixed} date The date value
20640 * Fires when this field hide.
20641 * @param {Roo.bootstrap.DateField} this
20642 * @param {Mixed} date The date value
20647 * Fires when select a date.
20648 * @param {Roo.bootstrap.DateField} this
20649 * @param {Mixed} date The date value
20653 * @event beforeselect
20654 * Fires when before select a date.
20655 * @param {Roo.bootstrap.DateField} this
20656 * @param {Mixed} date The date value
20658 beforeselect : true
20662 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20665 * @cfg {String} format
20666 * The default date format string which can be overriden for localization support. The format must be
20667 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20671 * @cfg {String} altFormats
20672 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20673 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20675 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20683 todayHighlight : false,
20689 keyboardNavigation: true,
20691 calendarWeeks: false,
20693 startDate: -Infinity,
20697 daysOfWeekDisabled: [],
20701 singleMode : false,
20703 UTCDate: function()
20705 return new Date(Date.UTC.apply(Date, arguments));
20708 UTCToday: function()
20710 var today = new Date();
20711 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20714 getDate: function() {
20715 var d = this.getUTCDate();
20716 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20719 getUTCDate: function() {
20723 setDate: function(d) {
20724 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20727 setUTCDate: function(d) {
20729 this.setValue(this.formatDate(this.date));
20732 onRender: function(ct, position)
20735 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20737 this.language = this.language || 'en';
20738 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20739 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20741 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20742 this.format = this.format || 'm/d/y';
20743 this.isInline = false;
20744 this.isInput = true;
20745 this.component = this.el.select('.add-on', true).first() || false;
20746 this.component = (this.component && this.component.length === 0) ? false : this.component;
20747 this.hasInput = this.component && this.inputEl().length;
20749 if (typeof(this.minViewMode === 'string')) {
20750 switch (this.minViewMode) {
20752 this.minViewMode = 1;
20755 this.minViewMode = 2;
20758 this.minViewMode = 0;
20763 if (typeof(this.viewMode === 'string')) {
20764 switch (this.viewMode) {
20777 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20779 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20781 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20783 this.picker().on('mousedown', this.onMousedown, this);
20784 this.picker().on('click', this.onClick, this);
20786 this.picker().addClass('datepicker-dropdown');
20788 this.startViewMode = this.viewMode;
20790 if(this.singleMode){
20791 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20792 v.setVisibilityMode(Roo.Element.DISPLAY);
20796 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20797 v.setStyle('width', '189px');
20801 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20802 if(!this.calendarWeeks){
20807 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20808 v.attr('colspan', function(i, val){
20809 return parseInt(val) + 1;
20814 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20816 this.setStartDate(this.startDate);
20817 this.setEndDate(this.endDate);
20819 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20826 if(this.isInline) {
20831 picker : function()
20833 return this.pickerEl;
20834 // return this.el.select('.datepicker', true).first();
20837 fillDow: function()
20839 var dowCnt = this.weekStart;
20848 if(this.calendarWeeks){
20856 while (dowCnt < this.weekStart + 7) {
20860 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20864 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20867 fillMonths: function()
20870 var months = this.picker().select('>.datepicker-months td', true).first();
20872 months.dom.innerHTML = '';
20878 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20881 months.createChild(month);
20888 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;
20890 if (this.date < this.startDate) {
20891 this.viewDate = new Date(this.startDate);
20892 } else if (this.date > this.endDate) {
20893 this.viewDate = new Date(this.endDate);
20895 this.viewDate = new Date(this.date);
20903 var d = new Date(this.viewDate),
20904 year = d.getUTCFullYear(),
20905 month = d.getUTCMonth(),
20906 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20907 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20908 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20909 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20910 currentDate = this.date && this.date.valueOf(),
20911 today = this.UTCToday();
20913 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20915 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20917 // this.picker.select('>tfoot th.today').
20918 // .text(dates[this.language].today)
20919 // .toggle(this.todayBtn !== false);
20921 this.updateNavArrows();
20924 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20926 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20928 prevMonth.setUTCDate(day);
20930 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20932 var nextMonth = new Date(prevMonth);
20934 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20936 nextMonth = nextMonth.valueOf();
20938 var fillMonths = false;
20940 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20942 while(prevMonth.valueOf() <= nextMonth) {
20945 if (prevMonth.getUTCDay() === this.weekStart) {
20947 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20955 if(this.calendarWeeks){
20956 // ISO 8601: First week contains first thursday.
20957 // ISO also states week starts on Monday, but we can be more abstract here.
20959 // Start of current week: based on weekstart/current date
20960 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20961 // Thursday of this week
20962 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20963 // First Thursday of year, year from thursday
20964 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20965 // Calendar week: ms between thursdays, div ms per day, div 7 days
20966 calWeek = (th - yth) / 864e5 / 7 + 1;
20968 fillMonths.cn.push({
20976 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20978 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20981 if (this.todayHighlight &&
20982 prevMonth.getUTCFullYear() == today.getFullYear() &&
20983 prevMonth.getUTCMonth() == today.getMonth() &&
20984 prevMonth.getUTCDate() == today.getDate()) {
20985 clsName += ' today';
20988 if (currentDate && prevMonth.valueOf() === currentDate) {
20989 clsName += ' active';
20992 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20993 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20994 clsName += ' disabled';
20997 fillMonths.cn.push({
20999 cls: 'day ' + clsName,
21000 html: prevMonth.getDate()
21003 prevMonth.setDate(prevMonth.getDate()+1);
21006 var currentYear = this.date && this.date.getUTCFullYear();
21007 var currentMonth = this.date && this.date.getUTCMonth();
21009 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21011 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21012 v.removeClass('active');
21014 if(currentYear === year && k === currentMonth){
21015 v.addClass('active');
21018 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21019 v.addClass('disabled');
21025 year = parseInt(year/10, 10) * 10;
21027 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21029 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21032 for (var i = -1; i < 11; i++) {
21033 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21035 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21043 showMode: function(dir)
21046 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21049 Roo.each(this.picker().select('>div',true).elements, function(v){
21050 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21053 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21058 if(this.isInline) {
21062 this.picker().removeClass(['bottom', 'top']);
21064 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21066 * place to the top of element!
21070 this.picker().addClass('top');
21071 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21076 this.picker().addClass('bottom');
21078 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21081 parseDate : function(value)
21083 if(!value || value instanceof Date){
21086 var v = Date.parseDate(value, this.format);
21087 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21088 v = Date.parseDate(value, 'Y-m-d');
21090 if(!v && this.altFormats){
21091 if(!this.altFormatsArray){
21092 this.altFormatsArray = this.altFormats.split("|");
21094 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21095 v = Date.parseDate(value, this.altFormatsArray[i]);
21101 formatDate : function(date, fmt)
21103 return (!date || !(date instanceof Date)) ?
21104 date : date.dateFormat(fmt || this.format);
21107 onFocus : function()
21109 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21113 onBlur : function()
21115 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21117 var d = this.inputEl().getValue();
21124 showPopup : function()
21126 this.picker().show();
21130 this.fireEvent('showpopup', this, this.date);
21133 hidePopup : function()
21135 if(this.isInline) {
21138 this.picker().hide();
21139 this.viewMode = this.startViewMode;
21142 this.fireEvent('hidepopup', this, this.date);
21146 onMousedown: function(e)
21148 e.stopPropagation();
21149 e.preventDefault();
21154 Roo.bootstrap.DateField.superclass.keyup.call(this);
21158 setValue: function(v)
21160 if(this.fireEvent('beforeselect', this, v) !== false){
21161 var d = new Date(this.parseDate(v) ).clearTime();
21163 if(isNaN(d.getTime())){
21164 this.date = this.viewDate = '';
21165 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21169 v = this.formatDate(d);
21171 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21173 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21177 this.fireEvent('select', this, this.date);
21181 getValue: function()
21183 return this.formatDate(this.date);
21186 fireKey: function(e)
21188 if (!this.picker().isVisible()){
21189 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21195 var dateChanged = false,
21197 newDate, newViewDate;
21202 e.preventDefault();
21206 if (!this.keyboardNavigation) {
21209 dir = e.keyCode == 37 ? -1 : 1;
21212 newDate = this.moveYear(this.date, dir);
21213 newViewDate = this.moveYear(this.viewDate, dir);
21214 } else if (e.shiftKey){
21215 newDate = this.moveMonth(this.date, dir);
21216 newViewDate = this.moveMonth(this.viewDate, dir);
21218 newDate = new Date(this.date);
21219 newDate.setUTCDate(this.date.getUTCDate() + dir);
21220 newViewDate = new Date(this.viewDate);
21221 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21223 if (this.dateWithinRange(newDate)){
21224 this.date = newDate;
21225 this.viewDate = newViewDate;
21226 this.setValue(this.formatDate(this.date));
21228 e.preventDefault();
21229 dateChanged = true;
21234 if (!this.keyboardNavigation) {
21237 dir = e.keyCode == 38 ? -1 : 1;
21239 newDate = this.moveYear(this.date, dir);
21240 newViewDate = this.moveYear(this.viewDate, dir);
21241 } else if (e.shiftKey){
21242 newDate = this.moveMonth(this.date, dir);
21243 newViewDate = this.moveMonth(this.viewDate, dir);
21245 newDate = new Date(this.date);
21246 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21247 newViewDate = new Date(this.viewDate);
21248 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21250 if (this.dateWithinRange(newDate)){
21251 this.date = newDate;
21252 this.viewDate = newViewDate;
21253 this.setValue(this.formatDate(this.date));
21255 e.preventDefault();
21256 dateChanged = true;
21260 this.setValue(this.formatDate(this.date));
21262 e.preventDefault();
21265 this.setValue(this.formatDate(this.date));
21279 onClick: function(e)
21281 e.stopPropagation();
21282 e.preventDefault();
21284 var target = e.getTarget();
21286 if(target.nodeName.toLowerCase() === 'i'){
21287 target = Roo.get(target).dom.parentNode;
21290 var nodeName = target.nodeName;
21291 var className = target.className;
21292 var html = target.innerHTML;
21293 //Roo.log(nodeName);
21295 switch(nodeName.toLowerCase()) {
21297 switch(className) {
21303 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21304 switch(this.viewMode){
21306 this.viewDate = this.moveMonth(this.viewDate, dir);
21310 this.viewDate = this.moveYear(this.viewDate, dir);
21316 var date = new Date();
21317 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21319 this.setValue(this.formatDate(this.date));
21326 if (className.indexOf('disabled') < 0) {
21327 this.viewDate.setUTCDate(1);
21328 if (className.indexOf('month') > -1) {
21329 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21331 var year = parseInt(html, 10) || 0;
21332 this.viewDate.setUTCFullYear(year);
21336 if(this.singleMode){
21337 this.setValue(this.formatDate(this.viewDate));
21348 //Roo.log(className);
21349 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21350 var day = parseInt(html, 10) || 1;
21351 var year = this.viewDate.getUTCFullYear(),
21352 month = this.viewDate.getUTCMonth();
21354 if (className.indexOf('old') > -1) {
21361 } else if (className.indexOf('new') > -1) {
21369 //Roo.log([year,month,day]);
21370 this.date = this.UTCDate(year, month, day,0,0,0,0);
21371 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21373 //Roo.log(this.formatDate(this.date));
21374 this.setValue(this.formatDate(this.date));
21381 setStartDate: function(startDate)
21383 this.startDate = startDate || -Infinity;
21384 if (this.startDate !== -Infinity) {
21385 this.startDate = this.parseDate(this.startDate);
21388 this.updateNavArrows();
21391 setEndDate: function(endDate)
21393 this.endDate = endDate || Infinity;
21394 if (this.endDate !== Infinity) {
21395 this.endDate = this.parseDate(this.endDate);
21398 this.updateNavArrows();
21401 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21403 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21404 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21405 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21407 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21408 return parseInt(d, 10);
21411 this.updateNavArrows();
21414 updateNavArrows: function()
21416 if(this.singleMode){
21420 var d = new Date(this.viewDate),
21421 year = d.getUTCFullYear(),
21422 month = d.getUTCMonth();
21424 Roo.each(this.picker().select('.prev', true).elements, function(v){
21426 switch (this.viewMode) {
21429 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21435 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21442 Roo.each(this.picker().select('.next', true).elements, function(v){
21444 switch (this.viewMode) {
21447 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21453 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21461 moveMonth: function(date, dir)
21466 var new_date = new Date(date.valueOf()),
21467 day = new_date.getUTCDate(),
21468 month = new_date.getUTCMonth(),
21469 mag = Math.abs(dir),
21471 dir = dir > 0 ? 1 : -1;
21474 // If going back one month, make sure month is not current month
21475 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21477 return new_date.getUTCMonth() == month;
21479 // If going forward one month, make sure month is as expected
21480 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21482 return new_date.getUTCMonth() != new_month;
21484 new_month = month + dir;
21485 new_date.setUTCMonth(new_month);
21486 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21487 if (new_month < 0 || new_month > 11) {
21488 new_month = (new_month + 12) % 12;
21491 // For magnitudes >1, move one month at a time...
21492 for (var i=0; i<mag; i++) {
21493 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21494 new_date = this.moveMonth(new_date, dir);
21496 // ...then reset the day, keeping it in the new month
21497 new_month = new_date.getUTCMonth();
21498 new_date.setUTCDate(day);
21500 return new_month != new_date.getUTCMonth();
21503 // Common date-resetting loop -- if date is beyond end of month, make it
21506 new_date.setUTCDate(--day);
21507 new_date.setUTCMonth(new_month);
21512 moveYear: function(date, dir)
21514 return this.moveMonth(date, dir*12);
21517 dateWithinRange: function(date)
21519 return date >= this.startDate && date <= this.endDate;
21525 this.picker().remove();
21528 validateValue : function(value)
21530 if(this.getVisibilityEl().hasClass('hidden')){
21534 if(value.length < 1) {
21535 if(this.allowBlank){
21541 if(value.length < this.minLength){
21544 if(value.length > this.maxLength){
21548 var vt = Roo.form.VTypes;
21549 if(!vt[this.vtype](value, this)){
21553 if(typeof this.validator == "function"){
21554 var msg = this.validator(value);
21560 if(this.regex && !this.regex.test(value)){
21564 if(typeof(this.parseDate(value)) == 'undefined'){
21568 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21572 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21582 this.date = this.viewDate = '';
21584 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21589 Roo.apply(Roo.bootstrap.DateField, {
21600 html: '<i class="fa fa-arrow-left"/>'
21610 html: '<i class="fa fa-arrow-right"/>'
21652 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21653 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21654 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21655 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21656 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21669 navFnc: 'FullYear',
21674 navFnc: 'FullYear',
21679 Roo.apply(Roo.bootstrap.DateField, {
21683 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21687 cls: 'datepicker-days',
21691 cls: 'table-condensed',
21693 Roo.bootstrap.DateField.head,
21697 Roo.bootstrap.DateField.footer
21704 cls: 'datepicker-months',
21708 cls: 'table-condensed',
21710 Roo.bootstrap.DateField.head,
21711 Roo.bootstrap.DateField.content,
21712 Roo.bootstrap.DateField.footer
21719 cls: 'datepicker-years',
21723 cls: 'table-condensed',
21725 Roo.bootstrap.DateField.head,
21726 Roo.bootstrap.DateField.content,
21727 Roo.bootstrap.DateField.footer
21746 * @class Roo.bootstrap.TimeField
21747 * @extends Roo.bootstrap.Input
21748 * Bootstrap DateField class
21752 * Create a new TimeField
21753 * @param {Object} config The config object
21756 Roo.bootstrap.TimeField = function(config){
21757 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21761 * Fires when this field show.
21762 * @param {Roo.bootstrap.DateField} thisthis
21763 * @param {Mixed} date The date value
21768 * Fires when this field hide.
21769 * @param {Roo.bootstrap.DateField} this
21770 * @param {Mixed} date The date value
21775 * Fires when select a date.
21776 * @param {Roo.bootstrap.DateField} this
21777 * @param {Mixed} date The date value
21783 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21786 * @cfg {String} format
21787 * The default time format string which can be overriden for localization support. The format must be
21788 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21792 onRender: function(ct, position)
21795 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21797 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21799 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21801 this.pop = this.picker().select('>.datepicker-time',true).first();
21802 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21804 this.picker().on('mousedown', this.onMousedown, this);
21805 this.picker().on('click', this.onClick, this);
21807 this.picker().addClass('datepicker-dropdown');
21812 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21813 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21814 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21815 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21816 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21817 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21821 fireKey: function(e){
21822 if (!this.picker().isVisible()){
21823 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21829 e.preventDefault();
21837 this.onTogglePeriod();
21840 this.onIncrementMinutes();
21843 this.onDecrementMinutes();
21852 onClick: function(e) {
21853 e.stopPropagation();
21854 e.preventDefault();
21857 picker : function()
21859 return this.el.select('.datepicker', true).first();
21862 fillTime: function()
21864 var time = this.pop.select('tbody', true).first();
21866 time.dom.innerHTML = '';
21881 cls: 'hours-up glyphicon glyphicon-chevron-up'
21901 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21922 cls: 'timepicker-hour',
21937 cls: 'timepicker-minute',
21952 cls: 'btn btn-primary period',
21974 cls: 'hours-down glyphicon glyphicon-chevron-down'
21994 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22012 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22019 var hours = this.time.getHours();
22020 var minutes = this.time.getMinutes();
22033 hours = hours - 12;
22037 hours = '0' + hours;
22041 minutes = '0' + minutes;
22044 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22045 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22046 this.pop.select('button', true).first().dom.innerHTML = period;
22052 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22054 var cls = ['bottom'];
22056 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22063 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22068 this.picker().addClass(cls.join('-'));
22072 Roo.each(cls, function(c){
22074 _this.picker().setTop(_this.inputEl().getHeight());
22078 _this.picker().setTop(0 - _this.picker().getHeight());
22083 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22087 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22094 onFocus : function()
22096 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22100 onBlur : function()
22102 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22108 this.picker().show();
22113 this.fireEvent('show', this, this.date);
22118 this.picker().hide();
22121 this.fireEvent('hide', this, this.date);
22124 setTime : function()
22127 this.setValue(this.time.format(this.format));
22129 this.fireEvent('select', this, this.date);
22134 onMousedown: function(e){
22135 e.stopPropagation();
22136 e.preventDefault();
22139 onIncrementHours: function()
22141 Roo.log('onIncrementHours');
22142 this.time = this.time.add(Date.HOUR, 1);
22147 onDecrementHours: function()
22149 Roo.log('onDecrementHours');
22150 this.time = this.time.add(Date.HOUR, -1);
22154 onIncrementMinutes: function()
22156 Roo.log('onIncrementMinutes');
22157 this.time = this.time.add(Date.MINUTE, 1);
22161 onDecrementMinutes: function()
22163 Roo.log('onDecrementMinutes');
22164 this.time = this.time.add(Date.MINUTE, -1);
22168 onTogglePeriod: function()
22170 Roo.log('onTogglePeriod');
22171 this.time = this.time.add(Date.HOUR, 12);
22178 Roo.apply(Roo.bootstrap.TimeField, {
22208 cls: 'btn btn-info ok',
22220 Roo.apply(Roo.bootstrap.TimeField, {
22224 cls: 'datepicker dropdown-menu',
22228 cls: 'datepicker-time',
22232 cls: 'table-condensed',
22234 Roo.bootstrap.TimeField.content,
22235 Roo.bootstrap.TimeField.footer
22254 * @class Roo.bootstrap.MonthField
22255 * @extends Roo.bootstrap.Input
22256 * Bootstrap MonthField class
22258 * @cfg {String} language default en
22261 * Create a new MonthField
22262 * @param {Object} config The config object
22265 Roo.bootstrap.MonthField = function(config){
22266 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22271 * Fires when this field show.
22272 * @param {Roo.bootstrap.MonthField} this
22273 * @param {Mixed} date The date value
22278 * Fires when this field hide.
22279 * @param {Roo.bootstrap.MonthField} this
22280 * @param {Mixed} date The date value
22285 * Fires when select a date.
22286 * @param {Roo.bootstrap.MonthField} this
22287 * @param {String} oldvalue The old value
22288 * @param {String} newvalue The new value
22294 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22296 onRender: function(ct, position)
22299 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22301 this.language = this.language || 'en';
22302 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22303 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22305 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22306 this.isInline = false;
22307 this.isInput = true;
22308 this.component = this.el.select('.add-on', true).first() || false;
22309 this.component = (this.component && this.component.length === 0) ? false : this.component;
22310 this.hasInput = this.component && this.inputEL().length;
22312 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22314 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22316 this.picker().on('mousedown', this.onMousedown, this);
22317 this.picker().on('click', this.onClick, this);
22319 this.picker().addClass('datepicker-dropdown');
22321 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22322 v.setStyle('width', '189px');
22329 if(this.isInline) {
22335 setValue: function(v, suppressEvent)
22337 var o = this.getValue();
22339 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22343 if(suppressEvent !== true){
22344 this.fireEvent('select', this, o, v);
22349 getValue: function()
22354 onClick: function(e)
22356 e.stopPropagation();
22357 e.preventDefault();
22359 var target = e.getTarget();
22361 if(target.nodeName.toLowerCase() === 'i'){
22362 target = Roo.get(target).dom.parentNode;
22365 var nodeName = target.nodeName;
22366 var className = target.className;
22367 var html = target.innerHTML;
22369 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22373 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22375 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22381 picker : function()
22383 return this.pickerEl;
22386 fillMonths: function()
22389 var months = this.picker().select('>.datepicker-months td', true).first();
22391 months.dom.innerHTML = '';
22397 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22400 months.createChild(month);
22409 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22410 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22413 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22414 e.removeClass('active');
22416 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22417 e.addClass('active');
22424 if(this.isInline) {
22428 this.picker().removeClass(['bottom', 'top']);
22430 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22432 * place to the top of element!
22436 this.picker().addClass('top');
22437 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22442 this.picker().addClass('bottom');
22444 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22447 onFocus : function()
22449 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22453 onBlur : function()
22455 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22457 var d = this.inputEl().getValue();
22466 this.picker().show();
22467 this.picker().select('>.datepicker-months', true).first().show();
22471 this.fireEvent('show', this, this.date);
22476 if(this.isInline) {
22479 this.picker().hide();
22480 this.fireEvent('hide', this, this.date);
22484 onMousedown: function(e)
22486 e.stopPropagation();
22487 e.preventDefault();
22492 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22496 fireKey: function(e)
22498 if (!this.picker().isVisible()){
22499 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22510 e.preventDefault();
22514 dir = e.keyCode == 37 ? -1 : 1;
22516 this.vIndex = this.vIndex + dir;
22518 if(this.vIndex < 0){
22522 if(this.vIndex > 11){
22526 if(isNaN(this.vIndex)){
22530 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22536 dir = e.keyCode == 38 ? -1 : 1;
22538 this.vIndex = this.vIndex + dir * 4;
22540 if(this.vIndex < 0){
22544 if(this.vIndex > 11){
22548 if(isNaN(this.vIndex)){
22552 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22557 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22558 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22562 e.preventDefault();
22565 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22566 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22582 this.picker().remove();
22587 Roo.apply(Roo.bootstrap.MonthField, {
22606 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22607 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22612 Roo.apply(Roo.bootstrap.MonthField, {
22616 cls: 'datepicker dropdown-menu roo-dynamic',
22620 cls: 'datepicker-months',
22624 cls: 'table-condensed',
22626 Roo.bootstrap.DateField.content
22646 * @class Roo.bootstrap.CheckBox
22647 * @extends Roo.bootstrap.Input
22648 * Bootstrap CheckBox class
22650 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22651 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22652 * @cfg {String} boxLabel The text that appears beside the checkbox
22653 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22654 * @cfg {Boolean} checked initnal the element
22655 * @cfg {Boolean} inline inline the element (default false)
22656 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22657 * @cfg {String} tooltip label tooltip
22660 * Create a new CheckBox
22661 * @param {Object} config The config object
22664 Roo.bootstrap.CheckBox = function(config){
22665 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22670 * Fires when the element is checked or unchecked.
22671 * @param {Roo.bootstrap.CheckBox} this This input
22672 * @param {Boolean} checked The new checked value
22677 * Fires when the element is click.
22678 * @param {Roo.bootstrap.CheckBox} this This input
22685 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22687 inputType: 'checkbox',
22696 // checkbox success does not make any sense really..
22701 getAutoCreate : function()
22703 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22709 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22712 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22718 type : this.inputType,
22719 value : this.inputValue,
22720 cls : 'roo-' + this.inputType, //'form-box',
22721 placeholder : this.placeholder || ''
22725 if(this.inputType != 'radio'){
22729 cls : 'roo-hidden-value',
22730 value : this.checked ? this.inputValue : this.valueOff
22735 if (this.weight) { // Validity check?
22736 cfg.cls += " " + this.inputType + "-" + this.weight;
22739 if (this.disabled) {
22740 input.disabled=true;
22744 input.checked = this.checked;
22749 input.name = this.name;
22751 if(this.inputType != 'radio'){
22752 hidden.name = this.name;
22753 input.name = '_hidden_' + this.name;
22758 input.cls += ' input-' + this.size;
22763 ['xs','sm','md','lg'].map(function(size){
22764 if (settings[size]) {
22765 cfg.cls += ' col-' + size + '-' + settings[size];
22769 var inputblock = input;
22771 if (this.before || this.after) {
22774 cls : 'input-group',
22779 inputblock.cn.push({
22781 cls : 'input-group-addon',
22786 inputblock.cn.push(input);
22788 if(this.inputType != 'radio'){
22789 inputblock.cn.push(hidden);
22793 inputblock.cn.push({
22795 cls : 'input-group-addon',
22801 var boxLabelCfg = false;
22807 //'for': id, // box label is handled by onclick - so no for...
22809 html: this.boxLabel
22812 boxLabelCfg.tooltip = this.tooltip;
22818 if (align ==='left' && this.fieldLabel.length) {
22819 // Roo.log("left and has label");
22824 cls : 'control-label',
22825 html : this.fieldLabel
22836 cfg.cn[1].cn.push(boxLabelCfg);
22839 if(this.labelWidth > 12){
22840 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22843 if(this.labelWidth < 13 && this.labelmd == 0){
22844 this.labelmd = this.labelWidth;
22847 if(this.labellg > 0){
22848 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22849 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22852 if(this.labelmd > 0){
22853 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22854 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22857 if(this.labelsm > 0){
22858 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22859 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22862 if(this.labelxs > 0){
22863 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22864 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22867 } else if ( this.fieldLabel.length) {
22868 // Roo.log(" label");
22872 tag: this.boxLabel ? 'span' : 'label',
22874 cls: 'control-label box-input-label',
22875 //cls : 'input-group-addon',
22876 html : this.fieldLabel
22883 cfg.cn.push(boxLabelCfg);
22888 // Roo.log(" no label && no align");
22889 cfg.cn = [ inputblock ] ;
22891 cfg.cn.push(boxLabelCfg);
22899 if(this.inputType != 'radio'){
22900 cfg.cn.push(hidden);
22908 * return the real input element.
22910 inputEl: function ()
22912 return this.el.select('input.roo-' + this.inputType,true).first();
22914 hiddenEl: function ()
22916 return this.el.select('input.roo-hidden-value',true).first();
22919 labelEl: function()
22921 return this.el.select('label.control-label',true).first();
22923 /* depricated... */
22927 return this.labelEl();
22930 boxLabelEl: function()
22932 return this.el.select('label.box-label',true).first();
22935 initEvents : function()
22937 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22939 this.inputEl().on('click', this.onClick, this);
22941 if (this.boxLabel) {
22942 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22945 this.startValue = this.getValue();
22948 Roo.bootstrap.CheckBox.register(this);
22952 onClick : function(e)
22954 if(this.fireEvent('click', this, e) !== false){
22955 this.setChecked(!this.checked);
22960 setChecked : function(state,suppressEvent)
22962 this.startValue = this.getValue();
22964 if(this.inputType == 'radio'){
22966 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22967 e.dom.checked = false;
22970 this.inputEl().dom.checked = true;
22972 this.inputEl().dom.value = this.inputValue;
22974 if(suppressEvent !== true){
22975 this.fireEvent('check', this, true);
22983 this.checked = state;
22985 this.inputEl().dom.checked = state;
22988 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22990 if(suppressEvent !== true){
22991 this.fireEvent('check', this, state);
22997 getValue : function()
22999 if(this.inputType == 'radio'){
23000 return this.getGroupValue();
23003 return this.hiddenEl().dom.value;
23007 getGroupValue : function()
23009 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23013 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23016 setValue : function(v,suppressEvent)
23018 if(this.inputType == 'radio'){
23019 this.setGroupValue(v, suppressEvent);
23023 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23028 setGroupValue : function(v, suppressEvent)
23030 this.startValue = this.getValue();
23032 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23033 e.dom.checked = false;
23035 if(e.dom.value == v){
23036 e.dom.checked = true;
23040 if(suppressEvent !== true){
23041 this.fireEvent('check', this, true);
23049 validate : function()
23051 if(this.getVisibilityEl().hasClass('hidden')){
23057 (this.inputType == 'radio' && this.validateRadio()) ||
23058 (this.inputType == 'checkbox' && this.validateCheckbox())
23064 this.markInvalid();
23068 validateRadio : function()
23070 if(this.getVisibilityEl().hasClass('hidden')){
23074 if(this.allowBlank){
23080 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23081 if(!e.dom.checked){
23093 validateCheckbox : function()
23096 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23097 //return (this.getValue() == this.inputValue) ? true : false;
23100 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23108 for(var i in group){
23109 if(group[i].el.isVisible(true)){
23117 for(var i in group){
23122 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23129 * Mark this field as valid
23131 markValid : function()
23135 this.fireEvent('valid', this);
23137 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23140 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23147 if(this.inputType == 'radio'){
23148 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23149 var fg = e.findParent('.form-group', false, true);
23150 if (Roo.bootstrap.version == 3) {
23151 fg.removeClass([_this.invalidClass, _this.validClass]);
23152 fg.addClass(_this.validClass);
23154 fg.removeClass(['is-valid', 'is-invalid']);
23155 fg.addClass('is-valid');
23163 var fg = this.el.findParent('.form-group', false, true);
23164 if (Roo.bootstrap.version == 3) {
23165 fg.removeClass([this.invalidClass, this.validClass]);
23166 fg.addClass(this.validClass);
23168 fg.removeClass(['is-valid', 'is-invalid']);
23169 fg.addClass('is-valid');
23174 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23180 for(var i in group){
23181 var fg = group[i].el.findParent('.form-group', false, true);
23182 if (Roo.bootstrap.version == 3) {
23183 fg.removeClass([this.invalidClass, this.validClass]);
23184 fg.addClass(this.validClass);
23186 fg.removeClass(['is-valid', 'is-invalid']);
23187 fg.addClass('is-valid');
23193 * Mark this field as invalid
23194 * @param {String} msg The validation message
23196 markInvalid : function(msg)
23198 if(this.allowBlank){
23204 this.fireEvent('invalid', this, msg);
23206 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23209 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23213 label.markInvalid();
23216 if(this.inputType == 'radio'){
23218 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23219 var fg = e.findParent('.form-group', false, true);
23220 if (Roo.bootstrap.version == 3) {
23221 fg.removeClass([_this.invalidClass, _this.validClass]);
23222 fg.addClass(_this.invalidClass);
23224 fg.removeClass(['is-invalid', 'is-valid']);
23225 fg.addClass('is-invalid');
23233 var fg = this.el.findParent('.form-group', false, true);
23234 if (Roo.bootstrap.version == 3) {
23235 fg.removeClass([_this.invalidClass, _this.validClass]);
23236 fg.addClass(_this.invalidClass);
23238 fg.removeClass(['is-invalid', 'is-valid']);
23239 fg.addClass('is-invalid');
23244 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23250 for(var i in group){
23251 var fg = group[i].el.findParent('.form-group', false, true);
23252 if (Roo.bootstrap.version == 3) {
23253 fg.removeClass([_this.invalidClass, _this.validClass]);
23254 fg.addClass(_this.invalidClass);
23256 fg.removeClass(['is-invalid', 'is-valid']);
23257 fg.addClass('is-invalid');
23263 clearInvalid : function()
23265 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23267 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23269 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23271 if (label && label.iconEl) {
23272 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23273 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23277 disable : function()
23279 if(this.inputType != 'radio'){
23280 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23287 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23288 _this.getActionEl().addClass(this.disabledClass);
23289 e.dom.disabled = true;
23293 this.disabled = true;
23294 this.fireEvent("disable", this);
23298 enable : function()
23300 if(this.inputType != 'radio'){
23301 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23308 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23309 _this.getActionEl().removeClass(this.disabledClass);
23310 e.dom.disabled = false;
23314 this.disabled = false;
23315 this.fireEvent("enable", this);
23319 setBoxLabel : function(v)
23324 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23330 Roo.apply(Roo.bootstrap.CheckBox, {
23335 * register a CheckBox Group
23336 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23338 register : function(checkbox)
23340 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23341 this.groups[checkbox.groupId] = {};
23344 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23348 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23352 * fetch a CheckBox Group based on the group ID
23353 * @param {string} the group ID
23354 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23356 get: function(groupId) {
23357 if (typeof(this.groups[groupId]) == 'undefined') {
23361 return this.groups[groupId] ;
23374 * @class Roo.bootstrap.Radio
23375 * @extends Roo.bootstrap.Component
23376 * Bootstrap Radio class
23377 * @cfg {String} boxLabel - the label associated
23378 * @cfg {String} value - the value of radio
23381 * Create a new Radio
23382 * @param {Object} config The config object
23384 Roo.bootstrap.Radio = function(config){
23385 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23389 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23395 getAutoCreate : function()
23399 cls : 'form-group radio',
23404 html : this.boxLabel
23412 initEvents : function()
23414 this.parent().register(this);
23416 this.el.on('click', this.onClick, this);
23420 onClick : function(e)
23422 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23423 this.setChecked(true);
23427 setChecked : function(state, suppressEvent)
23429 this.parent().setValue(this.value, suppressEvent);
23433 setBoxLabel : function(v)
23438 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23453 * @class Roo.bootstrap.SecurePass
23454 * @extends Roo.bootstrap.Input
23455 * Bootstrap SecurePass class
23459 * Create a new SecurePass
23460 * @param {Object} config The config object
23463 Roo.bootstrap.SecurePass = function (config) {
23464 // these go here, so the translation tool can replace them..
23466 PwdEmpty: "Please type a password, and then retype it to confirm.",
23467 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23468 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23469 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23470 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23471 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23472 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23473 TooWeak: "Your password is Too Weak."
23475 this.meterLabel = "Password strength:";
23476 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23477 this.meterClass = [
23478 "roo-password-meter-tooweak",
23479 "roo-password-meter-weak",
23480 "roo-password-meter-medium",
23481 "roo-password-meter-strong",
23482 "roo-password-meter-grey"
23487 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23490 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23492 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23494 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23495 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23496 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23497 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23498 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23499 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23500 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23510 * @cfg {String/Object} Label for the strength meter (defaults to
23511 * 'Password strength:')
23516 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23517 * ['Weak', 'Medium', 'Strong'])
23520 pwdStrengths: false,
23533 initEvents: function ()
23535 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23537 if (this.el.is('input[type=password]') && Roo.isSafari) {
23538 this.el.on('keydown', this.SafariOnKeyDown, this);
23541 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23544 onRender: function (ct, position)
23546 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23547 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23548 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23550 this.trigger.createChild({
23555 cls: 'roo-password-meter-grey col-xs-12',
23558 //width: this.meterWidth + 'px'
23562 cls: 'roo-password-meter-text'
23568 if (this.hideTrigger) {
23569 this.trigger.setDisplayed(false);
23571 this.setSize(this.width || '', this.height || '');
23574 onDestroy: function ()
23576 if (this.trigger) {
23577 this.trigger.removeAllListeners();
23578 this.trigger.remove();
23581 this.wrap.remove();
23583 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23586 checkStrength: function ()
23588 var pwd = this.inputEl().getValue();
23589 if (pwd == this._lastPwd) {
23594 if (this.ClientSideStrongPassword(pwd)) {
23596 } else if (this.ClientSideMediumPassword(pwd)) {
23598 } else if (this.ClientSideWeakPassword(pwd)) {
23604 Roo.log('strength1: ' + strength);
23606 //var pm = this.trigger.child('div/div/div').dom;
23607 var pm = this.trigger.child('div/div');
23608 pm.removeClass(this.meterClass);
23609 pm.addClass(this.meterClass[strength]);
23612 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23614 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23616 this._lastPwd = pwd;
23620 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23622 this._lastPwd = '';
23624 var pm = this.trigger.child('div/div');
23625 pm.removeClass(this.meterClass);
23626 pm.addClass('roo-password-meter-grey');
23629 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23632 this.inputEl().dom.type='password';
23635 validateValue: function (value)
23637 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23640 if (value.length == 0) {
23641 if (this.allowBlank) {
23642 this.clearInvalid();
23646 this.markInvalid(this.errors.PwdEmpty);
23647 this.errorMsg = this.errors.PwdEmpty;
23655 if (!value.match(/[\x21-\x7e]+/)) {
23656 this.markInvalid(this.errors.PwdBadChar);
23657 this.errorMsg = this.errors.PwdBadChar;
23660 if (value.length < 6) {
23661 this.markInvalid(this.errors.PwdShort);
23662 this.errorMsg = this.errors.PwdShort;
23665 if (value.length > 16) {
23666 this.markInvalid(this.errors.PwdLong);
23667 this.errorMsg = this.errors.PwdLong;
23671 if (this.ClientSideStrongPassword(value)) {
23673 } else if (this.ClientSideMediumPassword(value)) {
23675 } else if (this.ClientSideWeakPassword(value)) {
23682 if (strength < 2) {
23683 //this.markInvalid(this.errors.TooWeak);
23684 this.errorMsg = this.errors.TooWeak;
23689 console.log('strength2: ' + strength);
23691 //var pm = this.trigger.child('div/div/div').dom;
23693 var pm = this.trigger.child('div/div');
23694 pm.removeClass(this.meterClass);
23695 pm.addClass(this.meterClass[strength]);
23697 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23699 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23701 this.errorMsg = '';
23705 CharacterSetChecks: function (type)
23708 this.fResult = false;
23711 isctype: function (character, type)
23714 case this.kCapitalLetter:
23715 if (character >= 'A' && character <= 'Z') {
23720 case this.kSmallLetter:
23721 if (character >= 'a' && character <= 'z') {
23727 if (character >= '0' && character <= '9') {
23732 case this.kPunctuation:
23733 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23744 IsLongEnough: function (pwd, size)
23746 return !(pwd == null || isNaN(size) || pwd.length < size);
23749 SpansEnoughCharacterSets: function (word, nb)
23751 if (!this.IsLongEnough(word, nb))
23756 var characterSetChecks = new Array(
23757 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23758 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23761 for (var index = 0; index < word.length; ++index) {
23762 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23763 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23764 characterSetChecks[nCharSet].fResult = true;
23771 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23772 if (characterSetChecks[nCharSet].fResult) {
23777 if (nCharSets < nb) {
23783 ClientSideStrongPassword: function (pwd)
23785 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23788 ClientSideMediumPassword: function (pwd)
23790 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23793 ClientSideWeakPassword: function (pwd)
23795 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23798 })//<script type="text/javascript">
23801 * Based Ext JS Library 1.1.1
23802 * Copyright(c) 2006-2007, Ext JS, LLC.
23808 * @class Roo.HtmlEditorCore
23809 * @extends Roo.Component
23810 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23812 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23815 Roo.HtmlEditorCore = function(config){
23818 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23823 * @event initialize
23824 * Fires when the editor is fully initialized (including the iframe)
23825 * @param {Roo.HtmlEditorCore} this
23830 * Fires when the editor is first receives the focus. Any insertion must wait
23831 * until after this event.
23832 * @param {Roo.HtmlEditorCore} this
23836 * @event beforesync
23837 * Fires before the textarea is updated with content from the editor iframe. Return false
23838 * to cancel the sync.
23839 * @param {Roo.HtmlEditorCore} this
23840 * @param {String} html
23844 * @event beforepush
23845 * Fires before the iframe editor is updated with content from the textarea. Return false
23846 * to cancel the push.
23847 * @param {Roo.HtmlEditorCore} this
23848 * @param {String} html
23853 * Fires when the textarea is updated with content from the editor iframe.
23854 * @param {Roo.HtmlEditorCore} this
23855 * @param {String} html
23860 * Fires when the iframe editor is updated with content from the textarea.
23861 * @param {Roo.HtmlEditorCore} this
23862 * @param {String} html
23867 * @event editorevent
23868 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23869 * @param {Roo.HtmlEditorCore} this
23875 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23877 // defaults : white / black...
23878 this.applyBlacklists();
23885 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23889 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23895 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23900 * @cfg {Number} height (in pixels)
23904 * @cfg {Number} width (in pixels)
23909 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23912 stylesheets: false,
23917 // private properties
23918 validationEvent : false,
23920 initialized : false,
23922 sourceEditMode : false,
23923 onFocus : Roo.emptyFn,
23925 hideMode:'offsets',
23929 // blacklist + whitelisted elements..
23936 * Protected method that will not generally be called directly. It
23937 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23938 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23940 getDocMarkup : function(){
23944 // inherit styels from page...??
23945 if (this.stylesheets === false) {
23947 Roo.get(document.head).select('style').each(function(node) {
23948 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23951 Roo.get(document.head).select('link').each(function(node) {
23952 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23955 } else if (!this.stylesheets.length) {
23957 st = '<style type="text/css">' +
23958 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23961 for (var i in this.stylesheets) {
23962 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23967 st += '<style type="text/css">' +
23968 'IMG { cursor: pointer } ' +
23971 var cls = 'roo-htmleditor-body';
23973 if(this.bodyCls.length){
23974 cls += ' ' + this.bodyCls;
23977 return '<html><head>' + st +
23978 //<style type="text/css">' +
23979 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23981 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23985 onRender : function(ct, position)
23988 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23989 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23992 this.el.dom.style.border = '0 none';
23993 this.el.dom.setAttribute('tabIndex', -1);
23994 this.el.addClass('x-hidden hide');
23998 if(Roo.isIE){ // fix IE 1px bogus margin
23999 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24003 this.frameId = Roo.id();
24007 var iframe = this.owner.wrap.createChild({
24009 cls: 'form-control', // bootstrap..
24011 name: this.frameId,
24012 frameBorder : 'no',
24013 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24018 this.iframe = iframe.dom;
24020 this.assignDocWin();
24022 this.doc.designMode = 'on';
24025 this.doc.write(this.getDocMarkup());
24029 var task = { // must defer to wait for browser to be ready
24031 //console.log("run task?" + this.doc.readyState);
24032 this.assignDocWin();
24033 if(this.doc.body || this.doc.readyState == 'complete'){
24035 this.doc.designMode="on";
24039 Roo.TaskMgr.stop(task);
24040 this.initEditor.defer(10, this);
24047 Roo.TaskMgr.start(task);
24052 onResize : function(w, h)
24054 Roo.log('resize: ' +w + ',' + h );
24055 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24059 if(typeof w == 'number'){
24061 this.iframe.style.width = w + 'px';
24063 if(typeof h == 'number'){
24065 this.iframe.style.height = h + 'px';
24067 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24074 * Toggles the editor between standard and source edit mode.
24075 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24077 toggleSourceEdit : function(sourceEditMode){
24079 this.sourceEditMode = sourceEditMode === true;
24081 if(this.sourceEditMode){
24083 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24086 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24087 //this.iframe.className = '';
24090 //this.setSize(this.owner.wrap.getSize());
24091 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24098 * Protected method that will not generally be called directly. If you need/want
24099 * custom HTML cleanup, this is the method you should override.
24100 * @param {String} html The HTML to be cleaned
24101 * return {String} The cleaned HTML
24103 cleanHtml : function(html){
24104 html = String(html);
24105 if(html.length > 5){
24106 if(Roo.isSafari){ // strip safari nonsense
24107 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24110 if(html == ' '){
24117 * HTML Editor -> Textarea
24118 * Protected method that will not generally be called directly. Syncs the contents
24119 * of the editor iframe with the textarea.
24121 syncValue : function(){
24122 if(this.initialized){
24123 var bd = (this.doc.body || this.doc.documentElement);
24124 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24125 var html = bd.innerHTML;
24127 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24128 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24130 html = '<div style="'+m[0]+'">' + html + '</div>';
24133 html = this.cleanHtml(html);
24134 // fix up the special chars.. normaly like back quotes in word...
24135 // however we do not want to do this with chinese..
24136 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24138 var cc = match.charCodeAt();
24140 // Get the character value, handling surrogate pairs
24141 if (match.length == 2) {
24142 // It's a surrogate pair, calculate the Unicode code point
24143 var high = match.charCodeAt(0) - 0xD800;
24144 var low = match.charCodeAt(1) - 0xDC00;
24145 cc = (high * 0x400) + low + 0x10000;
24147 (cc >= 0x4E00 && cc < 0xA000 ) ||
24148 (cc >= 0x3400 && cc < 0x4E00 ) ||
24149 (cc >= 0xf900 && cc < 0xfb00 )
24154 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24155 return "&#" + cc + ";";
24162 if(this.owner.fireEvent('beforesync', this, html) !== false){
24163 this.el.dom.value = html;
24164 this.owner.fireEvent('sync', this, html);
24170 * Protected method that will not generally be called directly. Pushes the value of the textarea
24171 * into the iframe editor.
24173 pushValue : function(){
24174 if(this.initialized){
24175 var v = this.el.dom.value.trim();
24177 // if(v.length < 1){
24181 if(this.owner.fireEvent('beforepush', this, v) !== false){
24182 var d = (this.doc.body || this.doc.documentElement);
24184 this.cleanUpPaste();
24185 this.el.dom.value = d.innerHTML;
24186 this.owner.fireEvent('push', this, v);
24192 deferFocus : function(){
24193 this.focus.defer(10, this);
24197 focus : function(){
24198 if(this.win && !this.sourceEditMode){
24205 assignDocWin: function()
24207 var iframe = this.iframe;
24210 this.doc = iframe.contentWindow.document;
24211 this.win = iframe.contentWindow;
24213 // if (!Roo.get(this.frameId)) {
24216 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24217 // this.win = Roo.get(this.frameId).dom.contentWindow;
24219 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24223 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24224 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24229 initEditor : function(){
24230 //console.log("INIT EDITOR");
24231 this.assignDocWin();
24235 this.doc.designMode="on";
24237 this.doc.write(this.getDocMarkup());
24240 var dbody = (this.doc.body || this.doc.documentElement);
24241 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24242 // this copies styles from the containing element into thsi one..
24243 // not sure why we need all of this..
24244 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24246 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24247 //ss['background-attachment'] = 'fixed'; // w3c
24248 dbody.bgProperties = 'fixed'; // ie
24249 //Roo.DomHelper.applyStyles(dbody, ss);
24250 Roo.EventManager.on(this.doc, {
24251 //'mousedown': this.onEditorEvent,
24252 'mouseup': this.onEditorEvent,
24253 'dblclick': this.onEditorEvent,
24254 'click': this.onEditorEvent,
24255 'keyup': this.onEditorEvent,
24260 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24262 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24263 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24265 this.initialized = true;
24267 this.owner.fireEvent('initialize', this);
24272 onDestroy : function(){
24278 //for (var i =0; i < this.toolbars.length;i++) {
24279 // // fixme - ask toolbars for heights?
24280 // this.toolbars[i].onDestroy();
24283 //this.wrap.dom.innerHTML = '';
24284 //this.wrap.remove();
24289 onFirstFocus : function(){
24291 this.assignDocWin();
24294 this.activated = true;
24297 if(Roo.isGecko){ // prevent silly gecko errors
24299 var s = this.win.getSelection();
24300 if(!s.focusNode || s.focusNode.nodeType != 3){
24301 var r = s.getRangeAt(0);
24302 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24307 this.execCmd('useCSS', true);
24308 this.execCmd('styleWithCSS', false);
24311 this.owner.fireEvent('activate', this);
24315 adjustFont: function(btn){
24316 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24317 //if(Roo.isSafari){ // safari
24320 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24321 if(Roo.isSafari){ // safari
24322 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24323 v = (v < 10) ? 10 : v;
24324 v = (v > 48) ? 48 : v;
24325 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24330 v = Math.max(1, v+adjust);
24332 this.execCmd('FontSize', v );
24335 onEditorEvent : function(e)
24337 this.owner.fireEvent('editorevent', this, e);
24338 // this.updateToolbar();
24339 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24342 insertTag : function(tg)
24344 // could be a bit smarter... -> wrap the current selected tRoo..
24345 if (tg.toLowerCase() == 'span' ||
24346 tg.toLowerCase() == 'code' ||
24347 tg.toLowerCase() == 'sup' ||
24348 tg.toLowerCase() == 'sub'
24351 range = this.createRange(this.getSelection());
24352 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24353 wrappingNode.appendChild(range.extractContents());
24354 range.insertNode(wrappingNode);
24361 this.execCmd("formatblock", tg);
24365 insertText : function(txt)
24369 var range = this.createRange();
24370 range.deleteContents();
24371 //alert(Sender.getAttribute('label'));
24373 range.insertNode(this.doc.createTextNode(txt));
24379 * Executes a Midas editor command on the editor document and performs necessary focus and
24380 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24381 * @param {String} cmd The Midas command
24382 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24384 relayCmd : function(cmd, value){
24386 this.execCmd(cmd, value);
24387 this.owner.fireEvent('editorevent', this);
24388 //this.updateToolbar();
24389 this.owner.deferFocus();
24393 * Executes a Midas editor command directly on the editor document.
24394 * For visual commands, you should use {@link #relayCmd} instead.
24395 * <b>This should only be called after the editor is initialized.</b>
24396 * @param {String} cmd The Midas command
24397 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24399 execCmd : function(cmd, value){
24400 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24407 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24409 * @param {String} text | dom node..
24411 insertAtCursor : function(text)
24414 if(!this.activated){
24420 var r = this.doc.selection.createRange();
24431 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24435 // from jquery ui (MIT licenced)
24437 var win = this.win;
24439 if (win.getSelection && win.getSelection().getRangeAt) {
24440 range = win.getSelection().getRangeAt(0);
24441 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24442 range.insertNode(node);
24443 } else if (win.document.selection && win.document.selection.createRange) {
24444 // no firefox support
24445 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24446 win.document.selection.createRange().pasteHTML(txt);
24448 // no firefox support
24449 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24450 this.execCmd('InsertHTML', txt);
24459 mozKeyPress : function(e){
24461 var c = e.getCharCode(), cmd;
24464 c = String.fromCharCode(c).toLowerCase();
24478 this.cleanUpPaste.defer(100, this);
24486 e.preventDefault();
24494 fixKeys : function(){ // load time branching for fastest keydown performance
24496 return function(e){
24497 var k = e.getKey(), r;
24500 r = this.doc.selection.createRange();
24503 r.pasteHTML('    ');
24510 r = this.doc.selection.createRange();
24512 var target = r.parentElement();
24513 if(!target || target.tagName.toLowerCase() != 'li'){
24515 r.pasteHTML('<br />');
24521 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24522 this.cleanUpPaste.defer(100, this);
24528 }else if(Roo.isOpera){
24529 return function(e){
24530 var k = e.getKey();
24534 this.execCmd('InsertHTML','    ');
24537 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24538 this.cleanUpPaste.defer(100, this);
24543 }else if(Roo.isSafari){
24544 return function(e){
24545 var k = e.getKey();
24549 this.execCmd('InsertText','\t');
24553 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24554 this.cleanUpPaste.defer(100, this);
24562 getAllAncestors: function()
24564 var p = this.getSelectedNode();
24567 a.push(p); // push blank onto stack..
24568 p = this.getParentElement();
24572 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24576 a.push(this.doc.body);
24580 lastSelNode : false,
24583 getSelection : function()
24585 this.assignDocWin();
24586 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24589 getSelectedNode: function()
24591 // this may only work on Gecko!!!
24593 // should we cache this!!!!
24598 var range = this.createRange(this.getSelection()).cloneRange();
24601 var parent = range.parentElement();
24603 var testRange = range.duplicate();
24604 testRange.moveToElementText(parent);
24605 if (testRange.inRange(range)) {
24608 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24611 parent = parent.parentElement;
24616 // is ancestor a text element.
24617 var ac = range.commonAncestorContainer;
24618 if (ac.nodeType == 3) {
24619 ac = ac.parentNode;
24622 var ar = ac.childNodes;
24625 var other_nodes = [];
24626 var has_other_nodes = false;
24627 for (var i=0;i<ar.length;i++) {
24628 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24631 // fullly contained node.
24633 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24638 // probably selected..
24639 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24640 other_nodes.push(ar[i]);
24644 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24649 has_other_nodes = true;
24651 if (!nodes.length && other_nodes.length) {
24652 nodes= other_nodes;
24654 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24660 createRange: function(sel)
24662 // this has strange effects when using with
24663 // top toolbar - not sure if it's a great idea.
24664 //this.editor.contentWindow.focus();
24665 if (typeof sel != "undefined") {
24667 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24669 return this.doc.createRange();
24672 return this.doc.createRange();
24675 getParentElement: function()
24678 this.assignDocWin();
24679 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24681 var range = this.createRange(sel);
24684 var p = range.commonAncestorContainer;
24685 while (p.nodeType == 3) { // text node
24696 * Range intersection.. the hard stuff...
24700 * [ -- selected range --- ]
24704 * if end is before start or hits it. fail.
24705 * if start is after end or hits it fail.
24707 * if either hits (but other is outside. - then it's not
24713 // @see http://www.thismuchiknow.co.uk/?p=64.
24714 rangeIntersectsNode : function(range, node)
24716 var nodeRange = node.ownerDocument.createRange();
24718 nodeRange.selectNode(node);
24720 nodeRange.selectNodeContents(node);
24723 var rangeStartRange = range.cloneRange();
24724 rangeStartRange.collapse(true);
24726 var rangeEndRange = range.cloneRange();
24727 rangeEndRange.collapse(false);
24729 var nodeStartRange = nodeRange.cloneRange();
24730 nodeStartRange.collapse(true);
24732 var nodeEndRange = nodeRange.cloneRange();
24733 nodeEndRange.collapse(false);
24735 return rangeStartRange.compareBoundaryPoints(
24736 Range.START_TO_START, nodeEndRange) == -1 &&
24737 rangeEndRange.compareBoundaryPoints(
24738 Range.START_TO_START, nodeStartRange) == 1;
24742 rangeCompareNode : function(range, node)
24744 var nodeRange = node.ownerDocument.createRange();
24746 nodeRange.selectNode(node);
24748 nodeRange.selectNodeContents(node);
24752 range.collapse(true);
24754 nodeRange.collapse(true);
24756 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24757 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24759 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24761 var nodeIsBefore = ss == 1;
24762 var nodeIsAfter = ee == -1;
24764 if (nodeIsBefore && nodeIsAfter) {
24767 if (!nodeIsBefore && nodeIsAfter) {
24768 return 1; //right trailed.
24771 if (nodeIsBefore && !nodeIsAfter) {
24772 return 2; // left trailed.
24778 // private? - in a new class?
24779 cleanUpPaste : function()
24781 // cleans up the whole document..
24782 Roo.log('cleanuppaste');
24784 this.cleanUpChildren(this.doc.body);
24785 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24786 if (clean != this.doc.body.innerHTML) {
24787 this.doc.body.innerHTML = clean;
24792 cleanWordChars : function(input) {// change the chars to hex code
24793 var he = Roo.HtmlEditorCore;
24795 var output = input;
24796 Roo.each(he.swapCodes, function(sw) {
24797 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24799 output = output.replace(swapper, sw[1]);
24806 cleanUpChildren : function (n)
24808 if (!n.childNodes.length) {
24811 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24812 this.cleanUpChild(n.childNodes[i]);
24819 cleanUpChild : function (node)
24822 //console.log(node);
24823 if (node.nodeName == "#text") {
24824 // clean up silly Windows -- stuff?
24827 if (node.nodeName == "#comment") {
24828 node.parentNode.removeChild(node);
24829 // clean up silly Windows -- stuff?
24832 var lcname = node.tagName.toLowerCase();
24833 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24834 // whitelist of tags..
24836 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24838 node.parentNode.removeChild(node);
24843 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24845 // spans with no attributes - just remove them..
24846 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24847 remove_keep_children = true;
24850 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24851 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24853 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24854 // remove_keep_children = true;
24857 if (remove_keep_children) {
24858 this.cleanUpChildren(node);
24859 // inserts everything just before this node...
24860 while (node.childNodes.length) {
24861 var cn = node.childNodes[0];
24862 node.removeChild(cn);
24863 node.parentNode.insertBefore(cn, node);
24865 node.parentNode.removeChild(node);
24869 if (!node.attributes || !node.attributes.length) {
24874 this.cleanUpChildren(node);
24878 function cleanAttr(n,v)
24881 if (v.match(/^\./) || v.match(/^\//)) {
24884 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24887 if (v.match(/^#/)) {
24890 if (v.match(/^\{/)) { // allow template editing.
24893 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24894 node.removeAttribute(n);
24898 var cwhite = this.cwhite;
24899 var cblack = this.cblack;
24901 function cleanStyle(n,v)
24903 if (v.match(/expression/)) { //XSS?? should we even bother..
24904 node.removeAttribute(n);
24908 var parts = v.split(/;/);
24911 Roo.each(parts, function(p) {
24912 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24916 var l = p.split(':').shift().replace(/\s+/g,'');
24917 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24919 if ( cwhite.length && cblack.indexOf(l) > -1) {
24920 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24921 //node.removeAttribute(n);
24925 // only allow 'c whitelisted system attributes'
24926 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24927 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24928 //node.removeAttribute(n);
24938 if (clean.length) {
24939 node.setAttribute(n, clean.join(';'));
24941 node.removeAttribute(n);
24947 for (var i = node.attributes.length-1; i > -1 ; i--) {
24948 var a = node.attributes[i];
24951 if (a.name.toLowerCase().substr(0,2)=='on') {
24952 node.removeAttribute(a.name);
24955 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24956 node.removeAttribute(a.name);
24959 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24960 cleanAttr(a.name,a.value); // fixme..
24963 if (a.name == 'style') {
24964 cleanStyle(a.name,a.value);
24967 /// clean up MS crap..
24968 // tecnically this should be a list of valid class'es..
24971 if (a.name == 'class') {
24972 if (a.value.match(/^Mso/)) {
24973 node.removeAttribute('class');
24976 if (a.value.match(/^body$/)) {
24977 node.removeAttribute('class');
24988 this.cleanUpChildren(node);
24994 * Clean up MS wordisms...
24996 cleanWord : function(node)
24999 this.cleanWord(this.doc.body);
25004 node.nodeName == 'SPAN' &&
25005 !node.hasAttributes() &&
25006 node.childNodes.length == 1 &&
25007 node.firstChild.nodeName == "#text"
25009 var textNode = node.firstChild;
25010 node.removeChild(textNode);
25011 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25012 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25014 node.parentNode.insertBefore(textNode, node);
25015 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25016 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25018 node.parentNode.removeChild(node);
25021 if (node.nodeName == "#text") {
25022 // clean up silly Windows -- stuff?
25025 if (node.nodeName == "#comment") {
25026 node.parentNode.removeChild(node);
25027 // clean up silly Windows -- stuff?
25031 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25032 node.parentNode.removeChild(node);
25035 //Roo.log(node.tagName);
25036 // remove - but keep children..
25037 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25038 //Roo.log('-- removed');
25039 while (node.childNodes.length) {
25040 var cn = node.childNodes[0];
25041 node.removeChild(cn);
25042 node.parentNode.insertBefore(cn, node);
25043 // move node to parent - and clean it..
25044 this.cleanWord(cn);
25046 node.parentNode.removeChild(node);
25047 /// no need to iterate chidlren = it's got none..
25048 //this.iterateChildren(node, this.cleanWord);
25052 if (node.className.length) {
25054 var cn = node.className.split(/\W+/);
25056 Roo.each(cn, function(cls) {
25057 if (cls.match(/Mso[a-zA-Z]+/)) {
25062 node.className = cna.length ? cna.join(' ') : '';
25064 node.removeAttribute("class");
25068 if (node.hasAttribute("lang")) {
25069 node.removeAttribute("lang");
25072 if (node.hasAttribute("style")) {
25074 var styles = node.getAttribute("style").split(";");
25076 Roo.each(styles, function(s) {
25077 if (!s.match(/:/)) {
25080 var kv = s.split(":");
25081 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25084 // what ever is left... we allow.
25087 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25088 if (!nstyle.length) {
25089 node.removeAttribute('style');
25092 this.iterateChildren(node, this.cleanWord);
25098 * iterateChildren of a Node, calling fn each time, using this as the scole..
25099 * @param {DomNode} node node to iterate children of.
25100 * @param {Function} fn method of this class to call on each item.
25102 iterateChildren : function(node, fn)
25104 if (!node.childNodes.length) {
25107 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25108 fn.call(this, node.childNodes[i])
25114 * cleanTableWidths.
25116 * Quite often pasting from word etc.. results in tables with column and widths.
25117 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25120 cleanTableWidths : function(node)
25125 this.cleanTableWidths(this.doc.body);
25130 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25133 Roo.log(node.tagName);
25134 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25135 this.iterateChildren(node, this.cleanTableWidths);
25138 if (node.hasAttribute('width')) {
25139 node.removeAttribute('width');
25143 if (node.hasAttribute("style")) {
25146 var styles = node.getAttribute("style").split(";");
25148 Roo.each(styles, function(s) {
25149 if (!s.match(/:/)) {
25152 var kv = s.split(":");
25153 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25156 // what ever is left... we allow.
25159 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25160 if (!nstyle.length) {
25161 node.removeAttribute('style');
25165 this.iterateChildren(node, this.cleanTableWidths);
25173 domToHTML : function(currentElement, depth, nopadtext) {
25175 depth = depth || 0;
25176 nopadtext = nopadtext || false;
25178 if (!currentElement) {
25179 return this.domToHTML(this.doc.body);
25182 //Roo.log(currentElement);
25184 var allText = false;
25185 var nodeName = currentElement.nodeName;
25186 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25188 if (nodeName == '#text') {
25190 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25195 if (nodeName != 'BODY') {
25198 // Prints the node tagName, such as <A>, <IMG>, etc
25201 for(i = 0; i < currentElement.attributes.length;i++) {
25203 var aname = currentElement.attributes.item(i).name;
25204 if (!currentElement.attributes.item(i).value.length) {
25207 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25210 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25219 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25222 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25227 // Traverse the tree
25229 var currentElementChild = currentElement.childNodes.item(i);
25230 var allText = true;
25231 var innerHTML = '';
25233 while (currentElementChild) {
25234 // Formatting code (indent the tree so it looks nice on the screen)
25235 var nopad = nopadtext;
25236 if (lastnode == 'SPAN') {
25240 if (currentElementChild.nodeName == '#text') {
25241 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25242 toadd = nopadtext ? toadd : toadd.trim();
25243 if (!nopad && toadd.length > 80) {
25244 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25246 innerHTML += toadd;
25249 currentElementChild = currentElement.childNodes.item(i);
25255 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25257 // Recursively traverse the tree structure of the child node
25258 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25259 lastnode = currentElementChild.nodeName;
25261 currentElementChild=currentElement.childNodes.item(i);
25267 // The remaining code is mostly for formatting the tree
25268 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25273 ret+= "</"+tagName+">";
25279 applyBlacklists : function()
25281 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25282 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25286 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25287 if (b.indexOf(tag) > -1) {
25290 this.white.push(tag);
25294 Roo.each(w, function(tag) {
25295 if (b.indexOf(tag) > -1) {
25298 if (this.white.indexOf(tag) > -1) {
25301 this.white.push(tag);
25306 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25307 if (w.indexOf(tag) > -1) {
25310 this.black.push(tag);
25314 Roo.each(b, function(tag) {
25315 if (w.indexOf(tag) > -1) {
25318 if (this.black.indexOf(tag) > -1) {
25321 this.black.push(tag);
25326 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25327 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25331 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25332 if (b.indexOf(tag) > -1) {
25335 this.cwhite.push(tag);
25339 Roo.each(w, function(tag) {
25340 if (b.indexOf(tag) > -1) {
25343 if (this.cwhite.indexOf(tag) > -1) {
25346 this.cwhite.push(tag);
25351 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25352 if (w.indexOf(tag) > -1) {
25355 this.cblack.push(tag);
25359 Roo.each(b, function(tag) {
25360 if (w.indexOf(tag) > -1) {
25363 if (this.cblack.indexOf(tag) > -1) {
25366 this.cblack.push(tag);
25371 setStylesheets : function(stylesheets)
25373 if(typeof(stylesheets) == 'string'){
25374 Roo.get(this.iframe.contentDocument.head).createChild({
25376 rel : 'stylesheet',
25385 Roo.each(stylesheets, function(s) {
25390 Roo.get(_this.iframe.contentDocument.head).createChild({
25392 rel : 'stylesheet',
25401 removeStylesheets : function()
25405 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25410 setStyle : function(style)
25412 Roo.get(this.iframe.contentDocument.head).createChild({
25421 // hide stuff that is not compatible
25435 * @event specialkey
25439 * @cfg {String} fieldClass @hide
25442 * @cfg {String} focusClass @hide
25445 * @cfg {String} autoCreate @hide
25448 * @cfg {String} inputType @hide
25451 * @cfg {String} invalidClass @hide
25454 * @cfg {String} invalidText @hide
25457 * @cfg {String} msgFx @hide
25460 * @cfg {String} validateOnBlur @hide
25464 Roo.HtmlEditorCore.white = [
25465 'area', 'br', 'img', 'input', 'hr', 'wbr',
25467 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25468 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25469 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25470 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25471 'table', 'ul', 'xmp',
25473 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25476 'dir', 'menu', 'ol', 'ul', 'dl',
25482 Roo.HtmlEditorCore.black = [
25483 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25485 'base', 'basefont', 'bgsound', 'blink', 'body',
25486 'frame', 'frameset', 'head', 'html', 'ilayer',
25487 'iframe', 'layer', 'link', 'meta', 'object',
25488 'script', 'style' ,'title', 'xml' // clean later..
25490 Roo.HtmlEditorCore.clean = [
25491 'script', 'style', 'title', 'xml'
25493 Roo.HtmlEditorCore.remove = [
25498 Roo.HtmlEditorCore.ablack = [
25502 Roo.HtmlEditorCore.aclean = [
25503 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25507 Roo.HtmlEditorCore.pwhite= [
25508 'http', 'https', 'mailto'
25511 // white listed style attributes.
25512 Roo.HtmlEditorCore.cwhite= [
25513 // 'text-align', /// default is to allow most things..
25519 // black listed style attributes.
25520 Roo.HtmlEditorCore.cblack= [
25521 // 'font-size' -- this can be set by the project
25525 Roo.HtmlEditorCore.swapCodes =[
25544 * @class Roo.bootstrap.HtmlEditor
25545 * @extends Roo.bootstrap.TextArea
25546 * Bootstrap HtmlEditor class
25549 * Create a new HtmlEditor
25550 * @param {Object} config The config object
25553 Roo.bootstrap.HtmlEditor = function(config){
25554 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25555 if (!this.toolbars) {
25556 this.toolbars = [];
25559 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25562 * @event initialize
25563 * Fires when the editor is fully initialized (including the iframe)
25564 * @param {HtmlEditor} this
25569 * Fires when the editor is first receives the focus. Any insertion must wait
25570 * until after this event.
25571 * @param {HtmlEditor} this
25575 * @event beforesync
25576 * Fires before the textarea is updated with content from the editor iframe. Return false
25577 * to cancel the sync.
25578 * @param {HtmlEditor} this
25579 * @param {String} html
25583 * @event beforepush
25584 * Fires before the iframe editor is updated with content from the textarea. Return false
25585 * to cancel the push.
25586 * @param {HtmlEditor} this
25587 * @param {String} html
25592 * Fires when the textarea is updated with content from the editor iframe.
25593 * @param {HtmlEditor} this
25594 * @param {String} html
25599 * Fires when the iframe editor is updated with content from the textarea.
25600 * @param {HtmlEditor} this
25601 * @param {String} html
25605 * @event editmodechange
25606 * Fires when the editor switches edit modes
25607 * @param {HtmlEditor} this
25608 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25610 editmodechange: true,
25612 * @event editorevent
25613 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25614 * @param {HtmlEditor} this
25618 * @event firstfocus
25619 * Fires when on first focus - needed by toolbars..
25620 * @param {HtmlEditor} this
25625 * Auto save the htmlEditor value as a file into Events
25626 * @param {HtmlEditor} this
25630 * @event savedpreview
25631 * preview the saved version of htmlEditor
25632 * @param {HtmlEditor} this
25639 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25643 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25648 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25653 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25658 * @cfg {Number} height (in pixels)
25662 * @cfg {Number} width (in pixels)
25667 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25670 stylesheets: false,
25675 // private properties
25676 validationEvent : false,
25678 initialized : false,
25681 onFocus : Roo.emptyFn,
25683 hideMode:'offsets',
25685 tbContainer : false,
25689 toolbarContainer :function() {
25690 return this.wrap.select('.x-html-editor-tb',true).first();
25694 * Protected method that will not generally be called directly. It
25695 * is called when the editor creates its toolbar. Override this method if you need to
25696 * add custom toolbar buttons.
25697 * @param {HtmlEditor} editor
25699 createToolbar : function(){
25700 Roo.log('renewing');
25701 Roo.log("create toolbars");
25703 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25704 this.toolbars[0].render(this.toolbarContainer());
25708 // if (!editor.toolbars || !editor.toolbars.length) {
25709 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25712 // for (var i =0 ; i < editor.toolbars.length;i++) {
25713 // editor.toolbars[i] = Roo.factory(
25714 // typeof(editor.toolbars[i]) == 'string' ?
25715 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25716 // Roo.bootstrap.HtmlEditor);
25717 // editor.toolbars[i].init(editor);
25723 onRender : function(ct, position)
25725 // Roo.log("Call onRender: " + this.xtype);
25727 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25729 this.wrap = this.inputEl().wrap({
25730 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25733 this.editorcore.onRender(ct, position);
25735 if (this.resizable) {
25736 this.resizeEl = new Roo.Resizable(this.wrap, {
25740 minHeight : this.height,
25741 height: this.height,
25742 handles : this.resizable,
25745 resize : function(r, w, h) {
25746 _t.onResize(w,h); // -something
25752 this.createToolbar(this);
25755 if(!this.width && this.resizable){
25756 this.setSize(this.wrap.getSize());
25758 if (this.resizeEl) {
25759 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25760 // should trigger onReize..
25766 onResize : function(w, h)
25768 Roo.log('resize: ' +w + ',' + h );
25769 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25773 if(this.inputEl() ){
25774 if(typeof w == 'number'){
25775 var aw = w - this.wrap.getFrameWidth('lr');
25776 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25779 if(typeof h == 'number'){
25780 var tbh = -11; // fixme it needs to tool bar size!
25781 for (var i =0; i < this.toolbars.length;i++) {
25782 // fixme - ask toolbars for heights?
25783 tbh += this.toolbars[i].el.getHeight();
25784 //if (this.toolbars[i].footer) {
25785 // tbh += this.toolbars[i].footer.el.getHeight();
25793 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25794 ah -= 5; // knock a few pixes off for look..
25795 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25799 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25800 this.editorcore.onResize(ew,eh);
25805 * Toggles the editor between standard and source edit mode.
25806 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25808 toggleSourceEdit : function(sourceEditMode)
25810 this.editorcore.toggleSourceEdit(sourceEditMode);
25812 if(this.editorcore.sourceEditMode){
25813 Roo.log('editor - showing textarea');
25816 // Roo.log(this.syncValue());
25818 this.inputEl().removeClass(['hide', 'x-hidden']);
25819 this.inputEl().dom.removeAttribute('tabIndex');
25820 this.inputEl().focus();
25822 Roo.log('editor - hiding textarea');
25824 // Roo.log(this.pushValue());
25827 this.inputEl().addClass(['hide', 'x-hidden']);
25828 this.inputEl().dom.setAttribute('tabIndex', -1);
25829 //this.deferFocus();
25832 if(this.resizable){
25833 this.setSize(this.wrap.getSize());
25836 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25839 // private (for BoxComponent)
25840 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25842 // private (for BoxComponent)
25843 getResizeEl : function(){
25847 // private (for BoxComponent)
25848 getPositionEl : function(){
25853 initEvents : function(){
25854 this.originalValue = this.getValue();
25858 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25861 // markInvalid : Roo.emptyFn,
25863 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25866 // clearInvalid : Roo.emptyFn,
25868 setValue : function(v){
25869 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25870 this.editorcore.pushValue();
25875 deferFocus : function(){
25876 this.focus.defer(10, this);
25880 focus : function(){
25881 this.editorcore.focus();
25887 onDestroy : function(){
25893 for (var i =0; i < this.toolbars.length;i++) {
25894 // fixme - ask toolbars for heights?
25895 this.toolbars[i].onDestroy();
25898 this.wrap.dom.innerHTML = '';
25899 this.wrap.remove();
25904 onFirstFocus : function(){
25905 //Roo.log("onFirstFocus");
25906 this.editorcore.onFirstFocus();
25907 for (var i =0; i < this.toolbars.length;i++) {
25908 this.toolbars[i].onFirstFocus();
25914 syncValue : function()
25916 this.editorcore.syncValue();
25919 pushValue : function()
25921 this.editorcore.pushValue();
25925 // hide stuff that is not compatible
25939 * @event specialkey
25943 * @cfg {String} fieldClass @hide
25946 * @cfg {String} focusClass @hide
25949 * @cfg {String} autoCreate @hide
25952 * @cfg {String} inputType @hide
25956 * @cfg {String} invalidText @hide
25959 * @cfg {String} msgFx @hide
25962 * @cfg {String} validateOnBlur @hide
25971 Roo.namespace('Roo.bootstrap.htmleditor');
25973 * @class Roo.bootstrap.HtmlEditorToolbar1
25979 new Roo.bootstrap.HtmlEditor({
25982 new Roo.bootstrap.HtmlEditorToolbar1({
25983 disable : { fonts: 1 , format: 1, ..., ... , ...],
25989 * @cfg {Object} disable List of elements to disable..
25990 * @cfg {Array} btns List of additional buttons.
25994 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25997 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26000 Roo.apply(this, config);
26002 // default disabled, based on 'good practice'..
26003 this.disable = this.disable || {};
26004 Roo.applyIf(this.disable, {
26007 specialElements : true
26009 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26011 this.editor = config.editor;
26012 this.editorcore = config.editor.editorcore;
26014 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26016 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26017 // dont call parent... till later.
26019 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26024 editorcore : false,
26029 "h1","h2","h3","h4","h5","h6",
26031 "abbr", "acronym", "address", "cite", "samp", "var",
26035 onRender : function(ct, position)
26037 // Roo.log("Call onRender: " + this.xtype);
26039 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26041 this.el.dom.style.marginBottom = '0';
26043 var editorcore = this.editorcore;
26044 var editor= this.editor;
26047 var btn = function(id,cmd , toggle, handler, html){
26049 var event = toggle ? 'toggle' : 'click';
26054 xns: Roo.bootstrap,
26058 enableToggle:toggle !== false,
26060 pressed : toggle ? false : null,
26063 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26064 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26070 // var cb_box = function...
26075 xns: Roo.bootstrap,
26080 xns: Roo.bootstrap,
26084 Roo.each(this.formats, function(f) {
26085 style.menu.items.push({
26087 xns: Roo.bootstrap,
26088 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26093 editorcore.insertTag(this.tagname);
26100 children.push(style);
26102 btn('bold',false,true);
26103 btn('italic',false,true);
26104 btn('align-left', 'justifyleft',true);
26105 btn('align-center', 'justifycenter',true);
26106 btn('align-right' , 'justifyright',true);
26107 btn('link', false, false, function(btn) {
26108 //Roo.log("create link?");
26109 var url = prompt(this.createLinkText, this.defaultLinkValue);
26110 if(url && url != 'http:/'+'/'){
26111 this.editorcore.relayCmd('createlink', url);
26114 btn('list','insertunorderedlist',true);
26115 btn('pencil', false,true, function(btn){
26117 this.toggleSourceEdit(btn.pressed);
26120 if (this.editor.btns.length > 0) {
26121 for (var i = 0; i<this.editor.btns.length; i++) {
26122 children.push(this.editor.btns[i]);
26130 xns: Roo.bootstrap,
26135 xns: Roo.bootstrap,
26140 cog.menu.items.push({
26142 xns: Roo.bootstrap,
26143 html : Clean styles,
26148 editorcore.insertTag(this.tagname);
26157 this.xtype = 'NavSimplebar';
26159 for(var i=0;i< children.length;i++) {
26161 this.buttons.add(this.addxtypeChild(children[i]));
26165 editor.on('editorevent', this.updateToolbar, this);
26167 onBtnClick : function(id)
26169 this.editorcore.relayCmd(id);
26170 this.editorcore.focus();
26174 * Protected method that will not generally be called directly. It triggers
26175 * a toolbar update by reading the markup state of the current selection in the editor.
26177 updateToolbar: function(){
26179 if(!this.editorcore.activated){
26180 this.editor.onFirstFocus(); // is this neeed?
26184 var btns = this.buttons;
26185 var doc = this.editorcore.doc;
26186 btns.get('bold').setActive(doc.queryCommandState('bold'));
26187 btns.get('italic').setActive(doc.queryCommandState('italic'));
26188 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26190 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26191 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26192 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26194 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26195 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26198 var ans = this.editorcore.getAllAncestors();
26199 if (this.formatCombo) {
26202 var store = this.formatCombo.store;
26203 this.formatCombo.setValue("");
26204 for (var i =0; i < ans.length;i++) {
26205 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26207 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26215 // hides menus... - so this cant be on a menu...
26216 Roo.bootstrap.MenuMgr.hideAll();
26218 Roo.bootstrap.MenuMgr.hideAll();
26219 //this.editorsyncValue();
26221 onFirstFocus: function() {
26222 this.buttons.each(function(item){
26226 toggleSourceEdit : function(sourceEditMode){
26229 if(sourceEditMode){
26230 Roo.log("disabling buttons");
26231 this.buttons.each( function(item){
26232 if(item.cmd != 'pencil'){
26238 Roo.log("enabling buttons");
26239 if(this.editorcore.initialized){
26240 this.buttons.each( function(item){
26246 Roo.log("calling toggole on editor");
26247 // tell the editor that it's been pressed..
26248 this.editor.toggleSourceEdit(sourceEditMode);
26262 * @class Roo.bootstrap.Markdown
26263 * @extends Roo.bootstrap.TextArea
26264 * Bootstrap Showdown editable area
26265 * @cfg {string} content
26268 * Create a new Showdown
26271 Roo.bootstrap.Markdown = function(config){
26272 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26276 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26280 initEvents : function()
26283 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26284 this.markdownEl = this.el.createChild({
26285 cls : 'roo-markdown-area'
26287 this.inputEl().addClass('d-none');
26288 if (this.getValue() == '') {
26289 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26292 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26294 this.markdownEl.on('click', this.toggleTextEdit, this);
26295 this.on('blur', this.toggleTextEdit, this);
26296 this.on('specialkey', this.resizeTextArea, this);
26299 toggleTextEdit : function()
26301 var sh = this.markdownEl.getHeight();
26302 this.inputEl().addClass('d-none');
26303 this.markdownEl.addClass('d-none');
26304 if (!this.editing) {
26306 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26307 this.inputEl().removeClass('d-none');
26308 this.inputEl().focus();
26309 this.editing = true;
26312 // show showdown...
26313 this.updateMarkdown();
26314 this.markdownEl.removeClass('d-none');
26315 this.editing = false;
26318 updateMarkdown : function()
26320 if (this.getValue() == '') {
26321 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26325 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26328 resizeTextArea: function () {
26331 Roo.log([sh, this.getValue().split("\n").length * 30]);
26332 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26334 setValue : function(val)
26336 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26337 if (!this.editing) {
26338 this.updateMarkdown();
26344 if (!this.editing) {
26345 this.toggleTextEdit();
26353 * @class Roo.bootstrap.Table.AbstractSelectionModel
26354 * @extends Roo.util.Observable
26355 * Abstract base class for grid SelectionModels. It provides the interface that should be
26356 * implemented by descendant classes. This class should not be directly instantiated.
26359 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26360 this.locked = false;
26361 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26365 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26366 /** @ignore Called by the grid automatically. Do not call directly. */
26367 init : function(grid){
26373 * Locks the selections.
26376 this.locked = true;
26380 * Unlocks the selections.
26382 unlock : function(){
26383 this.locked = false;
26387 * Returns true if the selections are locked.
26388 * @return {Boolean}
26390 isLocked : function(){
26391 return this.locked;
26395 initEvents : function ()
26401 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26402 * @class Roo.bootstrap.Table.RowSelectionModel
26403 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26404 * It supports multiple selections and keyboard selection/navigation.
26406 * @param {Object} config
26409 Roo.bootstrap.Table.RowSelectionModel = function(config){
26410 Roo.apply(this, config);
26411 this.selections = new Roo.util.MixedCollection(false, function(o){
26416 this.lastActive = false;
26420 * @event selectionchange
26421 * Fires when the selection changes
26422 * @param {SelectionModel} this
26424 "selectionchange" : true,
26426 * @event afterselectionchange
26427 * Fires after the selection changes (eg. by key press or clicking)
26428 * @param {SelectionModel} this
26430 "afterselectionchange" : true,
26432 * @event beforerowselect
26433 * Fires when a row is selected being selected, return false to cancel.
26434 * @param {SelectionModel} this
26435 * @param {Number} rowIndex The selected index
26436 * @param {Boolean} keepExisting False if other selections will be cleared
26438 "beforerowselect" : true,
26441 * Fires when a row is selected.
26442 * @param {SelectionModel} this
26443 * @param {Number} rowIndex The selected index
26444 * @param {Roo.data.Record} r The record
26446 "rowselect" : true,
26448 * @event rowdeselect
26449 * Fires when a row is deselected.
26450 * @param {SelectionModel} this
26451 * @param {Number} rowIndex The selected index
26453 "rowdeselect" : true
26455 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26456 this.locked = false;
26459 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26461 * @cfg {Boolean} singleSelect
26462 * True to allow selection of only one row at a time (defaults to false)
26464 singleSelect : false,
26467 initEvents : function()
26470 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26471 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26472 //}else{ // allow click to work like normal
26473 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26475 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26476 this.grid.on("rowclick", this.handleMouseDown, this);
26478 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26479 "up" : function(e){
26481 this.selectPrevious(e.shiftKey);
26482 }else if(this.last !== false && this.lastActive !== false){
26483 var last = this.last;
26484 this.selectRange(this.last, this.lastActive-1);
26485 this.grid.getView().focusRow(this.lastActive);
26486 if(last !== false){
26490 this.selectFirstRow();
26492 this.fireEvent("afterselectionchange", this);
26494 "down" : function(e){
26496 this.selectNext(e.shiftKey);
26497 }else if(this.last !== false && this.lastActive !== false){
26498 var last = this.last;
26499 this.selectRange(this.last, this.lastActive+1);
26500 this.grid.getView().focusRow(this.lastActive);
26501 if(last !== false){
26505 this.selectFirstRow();
26507 this.fireEvent("afterselectionchange", this);
26511 this.grid.store.on('load', function(){
26512 this.selections.clear();
26515 var view = this.grid.view;
26516 view.on("refresh", this.onRefresh, this);
26517 view.on("rowupdated", this.onRowUpdated, this);
26518 view.on("rowremoved", this.onRemove, this);
26523 onRefresh : function()
26525 var ds = this.grid.store, i, v = this.grid.view;
26526 var s = this.selections;
26527 s.each(function(r){
26528 if((i = ds.indexOfId(r.id)) != -1){
26537 onRemove : function(v, index, r){
26538 this.selections.remove(r);
26542 onRowUpdated : function(v, index, r){
26543 if(this.isSelected(r)){
26544 v.onRowSelect(index);
26550 * @param {Array} records The records to select
26551 * @param {Boolean} keepExisting (optional) True to keep existing selections
26553 selectRecords : function(records, keepExisting)
26556 this.clearSelections();
26558 var ds = this.grid.store;
26559 for(var i = 0, len = records.length; i < len; i++){
26560 this.selectRow(ds.indexOf(records[i]), true);
26565 * Gets the number of selected rows.
26568 getCount : function(){
26569 return this.selections.length;
26573 * Selects the first row in the grid.
26575 selectFirstRow : function(){
26580 * Select the last row.
26581 * @param {Boolean} keepExisting (optional) True to keep existing selections
26583 selectLastRow : function(keepExisting){
26584 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26585 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26589 * Selects the row immediately following the last selected row.
26590 * @param {Boolean} keepExisting (optional) True to keep existing selections
26592 selectNext : function(keepExisting)
26594 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26595 this.selectRow(this.last+1, keepExisting);
26596 this.grid.getView().focusRow(this.last);
26601 * Selects the row that precedes the last selected row.
26602 * @param {Boolean} keepExisting (optional) True to keep existing selections
26604 selectPrevious : function(keepExisting){
26606 this.selectRow(this.last-1, keepExisting);
26607 this.grid.getView().focusRow(this.last);
26612 * Returns the selected records
26613 * @return {Array} Array of selected records
26615 getSelections : function(){
26616 return [].concat(this.selections.items);
26620 * Returns the first selected record.
26623 getSelected : function(){
26624 return this.selections.itemAt(0);
26629 * Clears all selections.
26631 clearSelections : function(fast)
26637 var ds = this.grid.store;
26638 var s = this.selections;
26639 s.each(function(r){
26640 this.deselectRow(ds.indexOfId(r.id));
26644 this.selections.clear();
26651 * Selects all rows.
26653 selectAll : function(){
26657 this.selections.clear();
26658 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26659 this.selectRow(i, true);
26664 * Returns True if there is a selection.
26665 * @return {Boolean}
26667 hasSelection : function(){
26668 return this.selections.length > 0;
26672 * Returns True if the specified row is selected.
26673 * @param {Number/Record} record The record or index of the record to check
26674 * @return {Boolean}
26676 isSelected : function(index){
26677 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26678 return (r && this.selections.key(r.id) ? true : false);
26682 * Returns True if the specified record id is selected.
26683 * @param {String} id The id of record to check
26684 * @return {Boolean}
26686 isIdSelected : function(id){
26687 return (this.selections.key(id) ? true : false);
26692 handleMouseDBClick : function(e, t){
26696 handleMouseDown : function(e, t)
26698 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26699 if(this.isLocked() || rowIndex < 0 ){
26702 if(e.shiftKey && this.last !== false){
26703 var last = this.last;
26704 this.selectRange(last, rowIndex, e.ctrlKey);
26705 this.last = last; // reset the last
26709 var isSelected = this.isSelected(rowIndex);
26710 //Roo.log("select row:" + rowIndex);
26712 this.deselectRow(rowIndex);
26714 this.selectRow(rowIndex, true);
26718 if(e.button !== 0 && isSelected){
26719 alert('rowIndex 2: ' + rowIndex);
26720 view.focusRow(rowIndex);
26721 }else if(e.ctrlKey && isSelected){
26722 this.deselectRow(rowIndex);
26723 }else if(!isSelected){
26724 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26725 view.focusRow(rowIndex);
26729 this.fireEvent("afterselectionchange", this);
26732 handleDragableRowClick : function(grid, rowIndex, e)
26734 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26735 this.selectRow(rowIndex, false);
26736 grid.view.focusRow(rowIndex);
26737 this.fireEvent("afterselectionchange", this);
26742 * Selects multiple rows.
26743 * @param {Array} rows Array of the indexes of the row to select
26744 * @param {Boolean} keepExisting (optional) True to keep existing selections
26746 selectRows : function(rows, keepExisting){
26748 this.clearSelections();
26750 for(var i = 0, len = rows.length; i < len; i++){
26751 this.selectRow(rows[i], true);
26756 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26757 * @param {Number} startRow The index of the first row in the range
26758 * @param {Number} endRow The index of the last row in the range
26759 * @param {Boolean} keepExisting (optional) True to retain existing selections
26761 selectRange : function(startRow, endRow, keepExisting){
26766 this.clearSelections();
26768 if(startRow <= endRow){
26769 for(var i = startRow; i <= endRow; i++){
26770 this.selectRow(i, true);
26773 for(var i = startRow; i >= endRow; i--){
26774 this.selectRow(i, true);
26780 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26781 * @param {Number} startRow The index of the first row in the range
26782 * @param {Number} endRow The index of the last row in the range
26784 deselectRange : function(startRow, endRow, preventViewNotify){
26788 for(var i = startRow; i <= endRow; i++){
26789 this.deselectRow(i, preventViewNotify);
26795 * @param {Number} row The index of the row to select
26796 * @param {Boolean} keepExisting (optional) True to keep existing selections
26798 selectRow : function(index, keepExisting, preventViewNotify)
26800 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26803 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26804 if(!keepExisting || this.singleSelect){
26805 this.clearSelections();
26808 var r = this.grid.store.getAt(index);
26809 //console.log('selectRow - record id :' + r.id);
26811 this.selections.add(r);
26812 this.last = this.lastActive = index;
26813 if(!preventViewNotify){
26814 var proxy = new Roo.Element(
26815 this.grid.getRowDom(index)
26817 proxy.addClass('bg-info info');
26819 this.fireEvent("rowselect", this, index, r);
26820 this.fireEvent("selectionchange", this);
26826 * @param {Number} row The index of the row to deselect
26828 deselectRow : function(index, preventViewNotify)
26833 if(this.last == index){
26836 if(this.lastActive == index){
26837 this.lastActive = false;
26840 var r = this.grid.store.getAt(index);
26845 this.selections.remove(r);
26846 //.console.log('deselectRow - record id :' + r.id);
26847 if(!preventViewNotify){
26849 var proxy = new Roo.Element(
26850 this.grid.getRowDom(index)
26852 proxy.removeClass('bg-info info');
26854 this.fireEvent("rowdeselect", this, index);
26855 this.fireEvent("selectionchange", this);
26859 restoreLast : function(){
26861 this.last = this._last;
26866 acceptsNav : function(row, col, cm){
26867 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26871 onEditorKey : function(field, e){
26872 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26877 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26879 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26881 }else if(k == e.ENTER && !e.ctrlKey){
26885 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26887 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26889 }else if(k == e.ESC){
26893 g.startEditing(newCell[0], newCell[1]);
26899 * Ext JS Library 1.1.1
26900 * Copyright(c) 2006-2007, Ext JS, LLC.
26902 * Originally Released Under LGPL - original licence link has changed is not relivant.
26905 * <script type="text/javascript">
26909 * @class Roo.bootstrap.PagingToolbar
26910 * @extends Roo.bootstrap.NavSimplebar
26911 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26913 * Create a new PagingToolbar
26914 * @param {Object} config The config object
26915 * @param {Roo.data.Store} store
26917 Roo.bootstrap.PagingToolbar = function(config)
26919 // old args format still supported... - xtype is prefered..
26920 // created from xtype...
26922 this.ds = config.dataSource;
26924 if (config.store && !this.ds) {
26925 this.store= Roo.factory(config.store, Roo.data);
26926 this.ds = this.store;
26927 this.ds.xmodule = this.xmodule || false;
26930 this.toolbarItems = [];
26931 if (config.items) {
26932 this.toolbarItems = config.items;
26935 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26940 this.bind(this.ds);
26943 if (Roo.bootstrap.version == 4) {
26944 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26946 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26951 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26953 * @cfg {Roo.data.Store} dataSource
26954 * The underlying data store providing the paged data
26957 * @cfg {String/HTMLElement/Element} container
26958 * container The id or element that will contain the toolbar
26961 * @cfg {Boolean} displayInfo
26962 * True to display the displayMsg (defaults to false)
26965 * @cfg {Number} pageSize
26966 * The number of records to display per page (defaults to 20)
26970 * @cfg {String} displayMsg
26971 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26973 displayMsg : 'Displaying {0} - {1} of {2}',
26975 * @cfg {String} emptyMsg
26976 * The message to display when no records are found (defaults to "No data to display")
26978 emptyMsg : 'No data to display',
26980 * Customizable piece of the default paging text (defaults to "Page")
26983 beforePageText : "Page",
26985 * Customizable piece of the default paging text (defaults to "of %0")
26988 afterPageText : "of {0}",
26990 * Customizable piece of the default paging text (defaults to "First Page")
26993 firstText : "First Page",
26995 * Customizable piece of the default paging text (defaults to "Previous Page")
26998 prevText : "Previous Page",
27000 * Customizable piece of the default paging text (defaults to "Next Page")
27003 nextText : "Next Page",
27005 * Customizable piece of the default paging text (defaults to "Last Page")
27008 lastText : "Last Page",
27010 * Customizable piece of the default paging text (defaults to "Refresh")
27013 refreshText : "Refresh",
27017 onRender : function(ct, position)
27019 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27020 this.navgroup.parentId = this.id;
27021 this.navgroup.onRender(this.el, null);
27022 // add the buttons to the navgroup
27024 if(this.displayInfo){
27025 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27026 this.displayEl = this.el.select('.x-paging-info', true).first();
27027 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27028 // this.displayEl = navel.el.select('span',true).first();
27034 Roo.each(_this.buttons, function(e){ // this might need to use render????
27035 Roo.factory(e).render(_this.el);
27039 Roo.each(_this.toolbarItems, function(e) {
27040 _this.navgroup.addItem(e);
27044 this.first = this.navgroup.addItem({
27045 tooltip: this.firstText,
27046 cls: "prev btn-outline-secondary",
27047 html : ' <i class="fa fa-step-backward"></i>',
27049 preventDefault: true,
27050 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27053 this.prev = this.navgroup.addItem({
27054 tooltip: this.prevText,
27055 cls: "prev btn-outline-secondary",
27056 html : ' <i class="fa fa-backward"></i>',
27058 preventDefault: true,
27059 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27061 //this.addSeparator();
27064 var field = this.navgroup.addItem( {
27066 cls : 'x-paging-position btn-outline-secondary',
27068 html : this.beforePageText +
27069 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27070 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27073 this.field = field.el.select('input', true).first();
27074 this.field.on("keydown", this.onPagingKeydown, this);
27075 this.field.on("focus", function(){this.dom.select();});
27078 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27079 //this.field.setHeight(18);
27080 //this.addSeparator();
27081 this.next = this.navgroup.addItem({
27082 tooltip: this.nextText,
27083 cls: "next btn-outline-secondary",
27084 html : ' <i class="fa fa-forward"></i>',
27086 preventDefault: true,
27087 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27089 this.last = this.navgroup.addItem({
27090 tooltip: this.lastText,
27091 html : ' <i class="fa fa-step-forward"></i>',
27092 cls: "next btn-outline-secondary",
27094 preventDefault: true,
27095 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27097 //this.addSeparator();
27098 this.loading = this.navgroup.addItem({
27099 tooltip: this.refreshText,
27100 cls: "btn-outline-secondary",
27101 html : ' <i class="fa fa-refresh"></i>',
27102 preventDefault: true,
27103 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27109 updateInfo : function(){
27110 if(this.displayEl){
27111 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27112 var msg = count == 0 ?
27116 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27118 this.displayEl.update(msg);
27123 onLoad : function(ds, r, o)
27125 this.cursor = o.params.start ? o.params.start : 0;
27127 var d = this.getPageData(),
27132 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27133 this.field.dom.value = ap;
27134 this.first.setDisabled(ap == 1);
27135 this.prev.setDisabled(ap == 1);
27136 this.next.setDisabled(ap == ps);
27137 this.last.setDisabled(ap == ps);
27138 this.loading.enable();
27143 getPageData : function(){
27144 var total = this.ds.getTotalCount();
27147 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27148 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27153 onLoadError : function(){
27154 this.loading.enable();
27158 onPagingKeydown : function(e){
27159 var k = e.getKey();
27160 var d = this.getPageData();
27162 var v = this.field.dom.value, pageNum;
27163 if(!v || isNaN(pageNum = parseInt(v, 10))){
27164 this.field.dom.value = d.activePage;
27167 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27168 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27171 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))
27173 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27174 this.field.dom.value = pageNum;
27175 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27178 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27180 var v = this.field.dom.value, pageNum;
27181 var increment = (e.shiftKey) ? 10 : 1;
27182 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27185 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27186 this.field.dom.value = d.activePage;
27189 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27191 this.field.dom.value = parseInt(v, 10) + increment;
27192 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27193 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27200 beforeLoad : function(){
27202 this.loading.disable();
27207 onClick : function(which){
27216 ds.load({params:{start: 0, limit: this.pageSize}});
27219 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27222 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27225 var total = ds.getTotalCount();
27226 var extra = total % this.pageSize;
27227 var lastStart = extra ? (total - extra) : total-this.pageSize;
27228 ds.load({params:{start: lastStart, limit: this.pageSize}});
27231 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27237 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27238 * @param {Roo.data.Store} store The data store to unbind
27240 unbind : function(ds){
27241 ds.un("beforeload", this.beforeLoad, this);
27242 ds.un("load", this.onLoad, this);
27243 ds.un("loadexception", this.onLoadError, this);
27244 ds.un("remove", this.updateInfo, this);
27245 ds.un("add", this.updateInfo, this);
27246 this.ds = undefined;
27250 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27251 * @param {Roo.data.Store} store The data store to bind
27253 bind : function(ds){
27254 ds.on("beforeload", this.beforeLoad, this);
27255 ds.on("load", this.onLoad, this);
27256 ds.on("loadexception", this.onLoadError, this);
27257 ds.on("remove", this.updateInfo, this);
27258 ds.on("add", this.updateInfo, this);
27269 * @class Roo.bootstrap.MessageBar
27270 * @extends Roo.bootstrap.Component
27271 * Bootstrap MessageBar class
27272 * @cfg {String} html contents of the MessageBar
27273 * @cfg {String} weight (info | success | warning | danger) default info
27274 * @cfg {String} beforeClass insert the bar before the given class
27275 * @cfg {Boolean} closable (true | false) default false
27276 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27279 * Create a new Element
27280 * @param {Object} config The config object
27283 Roo.bootstrap.MessageBar = function(config){
27284 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27287 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27293 beforeClass: 'bootstrap-sticky-wrap',
27295 getAutoCreate : function(){
27299 cls: 'alert alert-dismissable alert-' + this.weight,
27304 html: this.html || ''
27310 cfg.cls += ' alert-messages-fixed';
27324 onRender : function(ct, position)
27326 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27329 var cfg = Roo.apply({}, this.getAutoCreate());
27333 cfg.cls += ' ' + this.cls;
27336 cfg.style = this.style;
27338 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27340 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27343 this.el.select('>button.close').on('click', this.hide, this);
27349 if (!this.rendered) {
27355 this.fireEvent('show', this);
27361 if (!this.rendered) {
27367 this.fireEvent('hide', this);
27370 update : function()
27372 // var e = this.el.dom.firstChild;
27374 // if(this.closable){
27375 // e = e.nextSibling;
27378 // e.data = this.html || '';
27380 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27396 * @class Roo.bootstrap.Graph
27397 * @extends Roo.bootstrap.Component
27398 * Bootstrap Graph class
27402 @cfg {String} graphtype bar | vbar | pie
27403 @cfg {number} g_x coodinator | centre x (pie)
27404 @cfg {number} g_y coodinator | centre y (pie)
27405 @cfg {number} g_r radius (pie)
27406 @cfg {number} g_height height of the chart (respected by all elements in the set)
27407 @cfg {number} g_width width of the chart (respected by all elements in the set)
27408 @cfg {Object} title The title of the chart
27411 -opts (object) options for the chart
27413 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27414 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27416 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.
27417 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27419 o stretch (boolean)
27421 -opts (object) options for the pie
27424 o startAngle (number)
27425 o endAngle (number)
27429 * Create a new Input
27430 * @param {Object} config The config object
27433 Roo.bootstrap.Graph = function(config){
27434 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27440 * The img click event for the img.
27441 * @param {Roo.EventObject} e
27447 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27458 //g_colors: this.colors,
27465 getAutoCreate : function(){
27476 onRender : function(ct,position){
27479 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27481 if (typeof(Raphael) == 'undefined') {
27482 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27486 this.raphael = Raphael(this.el.dom);
27488 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27489 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27490 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27491 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27493 r.text(160, 10, "Single Series Chart").attr(txtattr);
27494 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27495 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27496 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27498 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27499 r.barchart(330, 10, 300, 220, data1);
27500 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27501 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27504 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27505 // r.barchart(30, 30, 560, 250, xdata, {
27506 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27507 // axis : "0 0 1 1",
27508 // axisxlabels : xdata
27509 // //yvalues : cols,
27512 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27514 // this.load(null,xdata,{
27515 // axis : "0 0 1 1",
27516 // axisxlabels : xdata
27521 load : function(graphtype,xdata,opts)
27523 this.raphael.clear();
27525 graphtype = this.graphtype;
27530 var r = this.raphael,
27531 fin = function () {
27532 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27534 fout = function () {
27535 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27537 pfin = function() {
27538 this.sector.stop();
27539 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27542 this.label[0].stop();
27543 this.label[0].attr({ r: 7.5 });
27544 this.label[1].attr({ "font-weight": 800 });
27547 pfout = function() {
27548 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27551 this.label[0].animate({ r: 5 }, 500, "bounce");
27552 this.label[1].attr({ "font-weight": 400 });
27558 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27561 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27564 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27565 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27567 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27574 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27579 setTitle: function(o)
27584 initEvents: function() {
27587 this.el.on('click', this.onClick, this);
27591 onClick : function(e)
27593 Roo.log('img onclick');
27594 this.fireEvent('click', this, e);
27606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27609 * @class Roo.bootstrap.dash.NumberBox
27610 * @extends Roo.bootstrap.Component
27611 * Bootstrap NumberBox class
27612 * @cfg {String} headline Box headline
27613 * @cfg {String} content Box content
27614 * @cfg {String} icon Box icon
27615 * @cfg {String} footer Footer text
27616 * @cfg {String} fhref Footer href
27619 * Create a new NumberBox
27620 * @param {Object} config The config object
27624 Roo.bootstrap.dash.NumberBox = function(config){
27625 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27629 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27638 getAutoCreate : function(){
27642 cls : 'small-box ',
27650 cls : 'roo-headline',
27651 html : this.headline
27655 cls : 'roo-content',
27656 html : this.content
27670 cls : 'ion ' + this.icon
27679 cls : 'small-box-footer',
27680 href : this.fhref || '#',
27684 cfg.cn.push(footer);
27691 onRender : function(ct,position){
27692 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27699 setHeadline: function (value)
27701 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27704 setFooter: function (value, href)
27706 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27709 this.el.select('a.small-box-footer',true).first().attr('href', href);
27714 setContent: function (value)
27716 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27719 initEvents: function()
27733 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27736 * @class Roo.bootstrap.dash.TabBox
27737 * @extends Roo.bootstrap.Component
27738 * Bootstrap TabBox class
27739 * @cfg {String} title Title of the TabBox
27740 * @cfg {String} icon Icon of the TabBox
27741 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27742 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27745 * Create a new TabBox
27746 * @param {Object} config The config object
27750 Roo.bootstrap.dash.TabBox = function(config){
27751 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27756 * When a pane is added
27757 * @param {Roo.bootstrap.dash.TabPane} pane
27761 * @event activatepane
27762 * When a pane is activated
27763 * @param {Roo.bootstrap.dash.TabPane} pane
27765 "activatepane" : true
27773 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27778 tabScrollable : false,
27780 getChildContainer : function()
27782 return this.el.select('.tab-content', true).first();
27785 getAutoCreate : function(){
27789 cls: 'pull-left header',
27797 cls: 'fa ' + this.icon
27803 cls: 'nav nav-tabs pull-right',
27809 if(this.tabScrollable){
27816 cls: 'nav nav-tabs pull-right',
27827 cls: 'nav-tabs-custom',
27832 cls: 'tab-content no-padding',
27840 initEvents : function()
27842 //Roo.log('add add pane handler');
27843 this.on('addpane', this.onAddPane, this);
27846 * Updates the box title
27847 * @param {String} html to set the title to.
27849 setTitle : function(value)
27851 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27853 onAddPane : function(pane)
27855 this.panes.push(pane);
27856 //Roo.log('addpane');
27858 // tabs are rendere left to right..
27859 if(!this.showtabs){
27863 var ctr = this.el.select('.nav-tabs', true).first();
27866 var existing = ctr.select('.nav-tab',true);
27867 var qty = existing.getCount();;
27870 var tab = ctr.createChild({
27872 cls : 'nav-tab' + (qty ? '' : ' active'),
27880 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27883 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27885 pane.el.addClass('active');
27890 onTabClick : function(ev,un,ob,pane)
27892 //Roo.log('tab - prev default');
27893 ev.preventDefault();
27896 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27897 pane.tab.addClass('active');
27898 //Roo.log(pane.title);
27899 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27900 // technically we should have a deactivate event.. but maybe add later.
27901 // and it should not de-activate the selected tab...
27902 this.fireEvent('activatepane', pane);
27903 pane.el.addClass('active');
27904 pane.fireEvent('activate');
27909 getActivePane : function()
27912 Roo.each(this.panes, function(p) {
27913 if(p.el.hasClass('active')){
27934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27936 * @class Roo.bootstrap.TabPane
27937 * @extends Roo.bootstrap.Component
27938 * Bootstrap TabPane class
27939 * @cfg {Boolean} active (false | true) Default false
27940 * @cfg {String} title title of panel
27944 * Create a new TabPane
27945 * @param {Object} config The config object
27948 Roo.bootstrap.dash.TabPane = function(config){
27949 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27955 * When a pane is activated
27956 * @param {Roo.bootstrap.dash.TabPane} pane
27963 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27968 // the tabBox that this is attached to.
27971 getAutoCreate : function()
27979 cfg.cls += ' active';
27984 initEvents : function()
27986 //Roo.log('trigger add pane handler');
27987 this.parent().fireEvent('addpane', this)
27991 * Updates the tab title
27992 * @param {String} html to set the title to.
27994 setTitle: function(str)
28000 this.tab.select('a', true).first().dom.innerHTML = str;
28017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28020 * @class Roo.bootstrap.menu.Menu
28021 * @extends Roo.bootstrap.Component
28022 * Bootstrap Menu class - container for Menu
28023 * @cfg {String} html Text of the menu
28024 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28025 * @cfg {String} icon Font awesome icon
28026 * @cfg {String} pos Menu align to (top | bottom) default bottom
28030 * Create a new Menu
28031 * @param {Object} config The config object
28035 Roo.bootstrap.menu.Menu = function(config){
28036 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28040 * @event beforeshow
28041 * Fires before this menu is displayed
28042 * @param {Roo.bootstrap.menu.Menu} this
28046 * @event beforehide
28047 * Fires before this menu is hidden
28048 * @param {Roo.bootstrap.menu.Menu} this
28053 * Fires after this menu is displayed
28054 * @param {Roo.bootstrap.menu.Menu} this
28059 * Fires after this menu is hidden
28060 * @param {Roo.bootstrap.menu.Menu} this
28065 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28066 * @param {Roo.bootstrap.menu.Menu} this
28067 * @param {Roo.EventObject} e
28074 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28078 weight : 'default',
28083 getChildContainer : function() {
28084 if(this.isSubMenu){
28088 return this.el.select('ul.dropdown-menu', true).first();
28091 getAutoCreate : function()
28096 cls : 'roo-menu-text',
28104 cls : 'fa ' + this.icon
28115 cls : 'dropdown-button btn btn-' + this.weight,
28120 cls : 'dropdown-toggle btn btn-' + this.weight,
28130 cls : 'dropdown-menu'
28136 if(this.pos == 'top'){
28137 cfg.cls += ' dropup';
28140 if(this.isSubMenu){
28143 cls : 'dropdown-menu'
28150 onRender : function(ct, position)
28152 this.isSubMenu = ct.hasClass('dropdown-submenu');
28154 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28157 initEvents : function()
28159 if(this.isSubMenu){
28163 this.hidden = true;
28165 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28166 this.triggerEl.on('click', this.onTriggerPress, this);
28168 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28169 this.buttonEl.on('click', this.onClick, this);
28175 if(this.isSubMenu){
28179 return this.el.select('ul.dropdown-menu', true).first();
28182 onClick : function(e)
28184 this.fireEvent("click", this, e);
28187 onTriggerPress : function(e)
28189 if (this.isVisible()) {
28196 isVisible : function(){
28197 return !this.hidden;
28202 this.fireEvent("beforeshow", this);
28204 this.hidden = false;
28205 this.el.addClass('open');
28207 Roo.get(document).on("mouseup", this.onMouseUp, this);
28209 this.fireEvent("show", this);
28216 this.fireEvent("beforehide", this);
28218 this.hidden = true;
28219 this.el.removeClass('open');
28221 Roo.get(document).un("mouseup", this.onMouseUp);
28223 this.fireEvent("hide", this);
28226 onMouseUp : function()
28240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28243 * @class Roo.bootstrap.menu.Item
28244 * @extends Roo.bootstrap.Component
28245 * Bootstrap MenuItem class
28246 * @cfg {Boolean} submenu (true | false) default false
28247 * @cfg {String} html text of the item
28248 * @cfg {String} href the link
28249 * @cfg {Boolean} disable (true | false) default false
28250 * @cfg {Boolean} preventDefault (true | false) default true
28251 * @cfg {String} icon Font awesome icon
28252 * @cfg {String} pos Submenu align to (left | right) default right
28256 * Create a new Item
28257 * @param {Object} config The config object
28261 Roo.bootstrap.menu.Item = function(config){
28262 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28266 * Fires when the mouse is hovering over this menu
28267 * @param {Roo.bootstrap.menu.Item} this
28268 * @param {Roo.EventObject} e
28273 * Fires when the mouse exits this menu
28274 * @param {Roo.bootstrap.menu.Item} this
28275 * @param {Roo.EventObject} e
28281 * The raw click event for the entire grid.
28282 * @param {Roo.EventObject} e
28288 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28293 preventDefault: true,
28298 getAutoCreate : function()
28303 cls : 'roo-menu-item-text',
28311 cls : 'fa ' + this.icon
28320 href : this.href || '#',
28327 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28331 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28333 if(this.pos == 'left'){
28334 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28341 initEvents : function()
28343 this.el.on('mouseover', this.onMouseOver, this);
28344 this.el.on('mouseout', this.onMouseOut, this);
28346 this.el.select('a', true).first().on('click', this.onClick, this);
28350 onClick : function(e)
28352 if(this.preventDefault){
28353 e.preventDefault();
28356 this.fireEvent("click", this, e);
28359 onMouseOver : function(e)
28361 if(this.submenu && this.pos == 'left'){
28362 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28365 this.fireEvent("mouseover", this, e);
28368 onMouseOut : function(e)
28370 this.fireEvent("mouseout", this, e);
28382 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28385 * @class Roo.bootstrap.menu.Separator
28386 * @extends Roo.bootstrap.Component
28387 * Bootstrap Separator class
28390 * Create a new Separator
28391 * @param {Object} config The config object
28395 Roo.bootstrap.menu.Separator = function(config){
28396 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28399 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28401 getAutoCreate : function(){
28422 * @class Roo.bootstrap.Tooltip
28423 * Bootstrap Tooltip class
28424 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28425 * to determine which dom element triggers the tooltip.
28427 * It needs to add support for additional attributes like tooltip-position
28430 * Create a new Toolti
28431 * @param {Object} config The config object
28434 Roo.bootstrap.Tooltip = function(config){
28435 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28437 this.alignment = Roo.bootstrap.Tooltip.alignment;
28439 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28440 this.alignment = config.alignment;
28445 Roo.apply(Roo.bootstrap.Tooltip, {
28447 * @function init initialize tooltip monitoring.
28451 currentTip : false,
28452 currentRegion : false,
28458 Roo.get(document).on('mouseover', this.enter ,this);
28459 Roo.get(document).on('mouseout', this.leave, this);
28462 this.currentTip = new Roo.bootstrap.Tooltip();
28465 enter : function(ev)
28467 var dom = ev.getTarget();
28469 //Roo.log(['enter',dom]);
28470 var el = Roo.fly(dom);
28471 if (this.currentEl) {
28473 //Roo.log(this.currentEl);
28474 //Roo.log(this.currentEl.contains(dom));
28475 if (this.currentEl == el) {
28478 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28484 if (this.currentTip.el) {
28485 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28489 if(!el || el.dom == document){
28495 // you can not look for children, as if el is the body.. then everythign is the child..
28496 if (!el.attr('tooltip')) { //
28497 if (!el.select("[tooltip]").elements.length) {
28500 // is the mouse over this child...?
28501 bindEl = el.select("[tooltip]").first();
28502 var xy = ev.getXY();
28503 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28504 //Roo.log("not in region.");
28507 //Roo.log("child element over..");
28510 this.currentEl = bindEl;
28511 this.currentTip.bind(bindEl);
28512 this.currentRegion = Roo.lib.Region.getRegion(dom);
28513 this.currentTip.enter();
28516 leave : function(ev)
28518 var dom = ev.getTarget();
28519 //Roo.log(['leave',dom]);
28520 if (!this.currentEl) {
28525 if (dom != this.currentEl.dom) {
28528 var xy = ev.getXY();
28529 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28532 // only activate leave if mouse cursor is outside... bounding box..
28537 if (this.currentTip) {
28538 this.currentTip.leave();
28540 //Roo.log('clear currentEl');
28541 this.currentEl = false;
28546 'left' : ['r-l', [-2,0], 'right'],
28547 'right' : ['l-r', [2,0], 'left'],
28548 'bottom' : ['t-b', [0,2], 'top'],
28549 'top' : [ 'b-t', [0,-2], 'bottom']
28555 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28560 delay : null, // can be { show : 300 , hide: 500}
28564 hoverState : null, //???
28566 placement : 'bottom',
28570 getAutoCreate : function(){
28577 cls : 'tooltip-arrow arrow'
28580 cls : 'tooltip-inner'
28587 bind : function(el)
28592 initEvents : function()
28594 this.arrowEl = this.el.select('.arrow', true).first();
28595 this.innerEl = this.el.select('.tooltip-inner', true).first();
28598 enter : function () {
28600 if (this.timeout != null) {
28601 clearTimeout(this.timeout);
28604 this.hoverState = 'in';
28605 //Roo.log("enter - show");
28606 if (!this.delay || !this.delay.show) {
28611 this.timeout = setTimeout(function () {
28612 if (_t.hoverState == 'in') {
28615 }, this.delay.show);
28619 clearTimeout(this.timeout);
28621 this.hoverState = 'out';
28622 if (!this.delay || !this.delay.hide) {
28628 this.timeout = setTimeout(function () {
28629 //Roo.log("leave - timeout");
28631 if (_t.hoverState == 'out') {
28633 Roo.bootstrap.Tooltip.currentEl = false;
28638 show : function (msg)
28641 this.render(document.body);
28644 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28646 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28648 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28650 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28651 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28653 var placement = typeof this.placement == 'function' ?
28654 this.placement.call(this, this.el, on_el) :
28657 var autoToken = /\s?auto?\s?/i;
28658 var autoPlace = autoToken.test(placement);
28660 placement = placement.replace(autoToken, '') || 'top';
28664 //this.el.setXY([0,0]);
28666 //this.el.dom.style.display='block';
28668 //this.el.appendTo(on_el);
28670 var p = this.getPosition();
28671 var box = this.el.getBox();
28677 var align = this.alignment[placement];
28679 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28681 if(placement == 'top' || placement == 'bottom'){
28683 placement = 'right';
28686 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28687 placement = 'left';
28690 var scroll = Roo.select('body', true).first().getScroll();
28692 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28696 align = this.alignment[placement];
28698 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28702 this.el.alignTo(this.bindEl, align[0],align[1]);
28703 //var arrow = this.el.select('.arrow',true).first();
28704 //arrow.set(align[2],
28706 this.el.addClass(placement);
28707 this.el.addClass("bs-tooltip-"+ placement);
28709 this.el.addClass('in fade show');
28711 this.hoverState = null;
28713 if (this.el.hasClass('fade')) {
28728 //this.el.setXY([0,0]);
28729 this.el.removeClass(['show', 'in']);
28745 * @class Roo.bootstrap.LocationPicker
28746 * @extends Roo.bootstrap.Component
28747 * Bootstrap LocationPicker class
28748 * @cfg {Number} latitude Position when init default 0
28749 * @cfg {Number} longitude Position when init default 0
28750 * @cfg {Number} zoom default 15
28751 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28752 * @cfg {Boolean} mapTypeControl default false
28753 * @cfg {Boolean} disableDoubleClickZoom default false
28754 * @cfg {Boolean} scrollwheel default true
28755 * @cfg {Boolean} streetViewControl default false
28756 * @cfg {Number} radius default 0
28757 * @cfg {String} locationName
28758 * @cfg {Boolean} draggable default true
28759 * @cfg {Boolean} enableAutocomplete default false
28760 * @cfg {Boolean} enableReverseGeocode default true
28761 * @cfg {String} markerTitle
28764 * Create a new LocationPicker
28765 * @param {Object} config The config object
28769 Roo.bootstrap.LocationPicker = function(config){
28771 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28776 * Fires when the picker initialized.
28777 * @param {Roo.bootstrap.LocationPicker} this
28778 * @param {Google Location} location
28782 * @event positionchanged
28783 * Fires when the picker position changed.
28784 * @param {Roo.bootstrap.LocationPicker} this
28785 * @param {Google Location} location
28787 positionchanged : true,
28790 * Fires when the map resize.
28791 * @param {Roo.bootstrap.LocationPicker} this
28796 * Fires when the map show.
28797 * @param {Roo.bootstrap.LocationPicker} this
28802 * Fires when the map hide.
28803 * @param {Roo.bootstrap.LocationPicker} this
28808 * Fires when click the map.
28809 * @param {Roo.bootstrap.LocationPicker} this
28810 * @param {Map event} e
28814 * @event mapRightClick
28815 * Fires when right click the map.
28816 * @param {Roo.bootstrap.LocationPicker} this
28817 * @param {Map event} e
28819 mapRightClick : true,
28821 * @event markerClick
28822 * Fires when click the marker.
28823 * @param {Roo.bootstrap.LocationPicker} this
28824 * @param {Map event} e
28826 markerClick : true,
28828 * @event markerRightClick
28829 * Fires when right click the marker.
28830 * @param {Roo.bootstrap.LocationPicker} this
28831 * @param {Map event} e
28833 markerRightClick : true,
28835 * @event OverlayViewDraw
28836 * Fires when OverlayView Draw
28837 * @param {Roo.bootstrap.LocationPicker} this
28839 OverlayViewDraw : true,
28841 * @event OverlayViewOnAdd
28842 * Fires when OverlayView Draw
28843 * @param {Roo.bootstrap.LocationPicker} this
28845 OverlayViewOnAdd : true,
28847 * @event OverlayViewOnRemove
28848 * Fires when OverlayView Draw
28849 * @param {Roo.bootstrap.LocationPicker} this
28851 OverlayViewOnRemove : true,
28853 * @event OverlayViewShow
28854 * Fires when OverlayView Draw
28855 * @param {Roo.bootstrap.LocationPicker} this
28856 * @param {Pixel} cpx
28858 OverlayViewShow : true,
28860 * @event OverlayViewHide
28861 * Fires when OverlayView Draw
28862 * @param {Roo.bootstrap.LocationPicker} this
28864 OverlayViewHide : true,
28866 * @event loadexception
28867 * Fires when load google lib failed.
28868 * @param {Roo.bootstrap.LocationPicker} this
28870 loadexception : true
28875 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28877 gMapContext: false,
28883 mapTypeControl: false,
28884 disableDoubleClickZoom: false,
28886 streetViewControl: false,
28890 enableAutocomplete: false,
28891 enableReverseGeocode: true,
28894 getAutoCreate: function()
28899 cls: 'roo-location-picker'
28905 initEvents: function(ct, position)
28907 if(!this.el.getWidth() || this.isApplied()){
28911 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28916 initial: function()
28918 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28919 this.fireEvent('loadexception', this);
28923 if(!this.mapTypeId){
28924 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28927 this.gMapContext = this.GMapContext();
28929 this.initOverlayView();
28931 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28935 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28936 _this.setPosition(_this.gMapContext.marker.position);
28939 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28940 _this.fireEvent('mapClick', this, event);
28944 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28945 _this.fireEvent('mapRightClick', this, event);
28949 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28950 _this.fireEvent('markerClick', this, event);
28954 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28955 _this.fireEvent('markerRightClick', this, event);
28959 this.setPosition(this.gMapContext.location);
28961 this.fireEvent('initial', this, this.gMapContext.location);
28964 initOverlayView: function()
28968 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28972 _this.fireEvent('OverlayViewDraw', _this);
28977 _this.fireEvent('OverlayViewOnAdd', _this);
28980 onRemove: function()
28982 _this.fireEvent('OverlayViewOnRemove', _this);
28985 show: function(cpx)
28987 _this.fireEvent('OverlayViewShow', _this, cpx);
28992 _this.fireEvent('OverlayViewHide', _this);
28998 fromLatLngToContainerPixel: function(event)
29000 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29003 isApplied: function()
29005 return this.getGmapContext() == false ? false : true;
29008 getGmapContext: function()
29010 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29013 GMapContext: function()
29015 var position = new google.maps.LatLng(this.latitude, this.longitude);
29017 var _map = new google.maps.Map(this.el.dom, {
29020 mapTypeId: this.mapTypeId,
29021 mapTypeControl: this.mapTypeControl,
29022 disableDoubleClickZoom: this.disableDoubleClickZoom,
29023 scrollwheel: this.scrollwheel,
29024 streetViewControl: this.streetViewControl,
29025 locationName: this.locationName,
29026 draggable: this.draggable,
29027 enableAutocomplete: this.enableAutocomplete,
29028 enableReverseGeocode: this.enableReverseGeocode
29031 var _marker = new google.maps.Marker({
29032 position: position,
29034 title: this.markerTitle,
29035 draggable: this.draggable
29042 location: position,
29043 radius: this.radius,
29044 locationName: this.locationName,
29045 addressComponents: {
29046 formatted_address: null,
29047 addressLine1: null,
29048 addressLine2: null,
29050 streetNumber: null,
29054 stateOrProvince: null
29057 domContainer: this.el.dom,
29058 geodecoder: new google.maps.Geocoder()
29062 drawCircle: function(center, radius, options)
29064 if (this.gMapContext.circle != null) {
29065 this.gMapContext.circle.setMap(null);
29069 options = Roo.apply({}, options, {
29070 strokeColor: "#0000FF",
29071 strokeOpacity: .35,
29073 fillColor: "#0000FF",
29077 options.map = this.gMapContext.map;
29078 options.radius = radius;
29079 options.center = center;
29080 this.gMapContext.circle = new google.maps.Circle(options);
29081 return this.gMapContext.circle;
29087 setPosition: function(location)
29089 this.gMapContext.location = location;
29090 this.gMapContext.marker.setPosition(location);
29091 this.gMapContext.map.panTo(location);
29092 this.drawCircle(location, this.gMapContext.radius, {});
29096 if (this.gMapContext.settings.enableReverseGeocode) {
29097 this.gMapContext.geodecoder.geocode({
29098 latLng: this.gMapContext.location
29099 }, function(results, status) {
29101 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29102 _this.gMapContext.locationName = results[0].formatted_address;
29103 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29105 _this.fireEvent('positionchanged', this, location);
29112 this.fireEvent('positionchanged', this, location);
29117 google.maps.event.trigger(this.gMapContext.map, "resize");
29119 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29121 this.fireEvent('resize', this);
29124 setPositionByLatLng: function(latitude, longitude)
29126 this.setPosition(new google.maps.LatLng(latitude, longitude));
29129 getCurrentPosition: function()
29132 latitude: this.gMapContext.location.lat(),
29133 longitude: this.gMapContext.location.lng()
29137 getAddressName: function()
29139 return this.gMapContext.locationName;
29142 getAddressComponents: function()
29144 return this.gMapContext.addressComponents;
29147 address_component_from_google_geocode: function(address_components)
29151 for (var i = 0; i < address_components.length; i++) {
29152 var component = address_components[i];
29153 if (component.types.indexOf("postal_code") >= 0) {
29154 result.postalCode = component.short_name;
29155 } else if (component.types.indexOf("street_number") >= 0) {
29156 result.streetNumber = component.short_name;
29157 } else if (component.types.indexOf("route") >= 0) {
29158 result.streetName = component.short_name;
29159 } else if (component.types.indexOf("neighborhood") >= 0) {
29160 result.city = component.short_name;
29161 } else if (component.types.indexOf("locality") >= 0) {
29162 result.city = component.short_name;
29163 } else if (component.types.indexOf("sublocality") >= 0) {
29164 result.district = component.short_name;
29165 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29166 result.stateOrProvince = component.short_name;
29167 } else if (component.types.indexOf("country") >= 0) {
29168 result.country = component.short_name;
29172 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29173 result.addressLine2 = "";
29177 setZoomLevel: function(zoom)
29179 this.gMapContext.map.setZoom(zoom);
29192 this.fireEvent('show', this);
29203 this.fireEvent('hide', this);
29208 Roo.apply(Roo.bootstrap.LocationPicker, {
29210 OverlayView : function(map, options)
29212 options = options || {};
29219 * @class Roo.bootstrap.Alert
29220 * @extends Roo.bootstrap.Component
29221 * Bootstrap Alert class - shows an alert area box
29223 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29224 Enter a valid email address
29227 * @cfg {String} title The title of alert
29228 * @cfg {String} html The content of alert
29229 * @cfg {String} weight ( success | info | warning | danger )
29230 * @cfg {String} faicon font-awesomeicon
29233 * Create a new alert
29234 * @param {Object} config The config object
29238 Roo.bootstrap.Alert = function(config){
29239 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29243 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29250 getAutoCreate : function()
29259 cls : 'roo-alert-icon'
29264 cls : 'roo-alert-title',
29269 cls : 'roo-alert-text',
29276 cfg.cn[0].cls += ' fa ' + this.faicon;
29280 cfg.cls += ' alert-' + this.weight;
29286 initEvents: function()
29288 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29291 setTitle : function(str)
29293 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29296 setText : function(str)
29298 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29301 setWeight : function(weight)
29304 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29307 this.weight = weight;
29309 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29312 setIcon : function(icon)
29315 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29318 this.faicon = icon;
29320 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29341 * @class Roo.bootstrap.UploadCropbox
29342 * @extends Roo.bootstrap.Component
29343 * Bootstrap UploadCropbox class
29344 * @cfg {String} emptyText show when image has been loaded
29345 * @cfg {String} rotateNotify show when image too small to rotate
29346 * @cfg {Number} errorTimeout default 3000
29347 * @cfg {Number} minWidth default 300
29348 * @cfg {Number} minHeight default 300
29349 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29350 * @cfg {Boolean} isDocument (true|false) default false
29351 * @cfg {String} url action url
29352 * @cfg {String} paramName default 'imageUpload'
29353 * @cfg {String} method default POST
29354 * @cfg {Boolean} loadMask (true|false) default true
29355 * @cfg {Boolean} loadingText default 'Loading...'
29358 * Create a new UploadCropbox
29359 * @param {Object} config The config object
29362 Roo.bootstrap.UploadCropbox = function(config){
29363 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29367 * @event beforeselectfile
29368 * Fire before select file
29369 * @param {Roo.bootstrap.UploadCropbox} this
29371 "beforeselectfile" : true,
29374 * Fire after initEvent
29375 * @param {Roo.bootstrap.UploadCropbox} this
29380 * Fire after initEvent
29381 * @param {Roo.bootstrap.UploadCropbox} this
29382 * @param {String} data
29387 * Fire when preparing the file data
29388 * @param {Roo.bootstrap.UploadCropbox} this
29389 * @param {Object} file
29394 * Fire when get exception
29395 * @param {Roo.bootstrap.UploadCropbox} this
29396 * @param {XMLHttpRequest} xhr
29398 "exception" : true,
29400 * @event beforeloadcanvas
29401 * Fire before load the canvas
29402 * @param {Roo.bootstrap.UploadCropbox} this
29403 * @param {String} src
29405 "beforeloadcanvas" : true,
29408 * Fire when trash image
29409 * @param {Roo.bootstrap.UploadCropbox} this
29414 * Fire when download the image
29415 * @param {Roo.bootstrap.UploadCropbox} this
29419 * @event footerbuttonclick
29420 * Fire when footerbuttonclick
29421 * @param {Roo.bootstrap.UploadCropbox} this
29422 * @param {String} type
29424 "footerbuttonclick" : true,
29428 * @param {Roo.bootstrap.UploadCropbox} this
29433 * Fire when rotate the image
29434 * @param {Roo.bootstrap.UploadCropbox} this
29435 * @param {String} pos
29440 * Fire when inspect the file
29441 * @param {Roo.bootstrap.UploadCropbox} this
29442 * @param {Object} file
29447 * Fire when xhr upload the file
29448 * @param {Roo.bootstrap.UploadCropbox} this
29449 * @param {Object} data
29454 * Fire when arrange the file data
29455 * @param {Roo.bootstrap.UploadCropbox} this
29456 * @param {Object} formData
29461 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29464 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29466 emptyText : 'Click to upload image',
29467 rotateNotify : 'Image is too small to rotate',
29468 errorTimeout : 3000,
29482 cropType : 'image/jpeg',
29484 canvasLoaded : false,
29485 isDocument : false,
29487 paramName : 'imageUpload',
29489 loadingText : 'Loading...',
29492 getAutoCreate : function()
29496 cls : 'roo-upload-cropbox',
29500 cls : 'roo-upload-cropbox-selector',
29505 cls : 'roo-upload-cropbox-body',
29506 style : 'cursor:pointer',
29510 cls : 'roo-upload-cropbox-preview'
29514 cls : 'roo-upload-cropbox-thumb'
29518 cls : 'roo-upload-cropbox-empty-notify',
29519 html : this.emptyText
29523 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29524 html : this.rotateNotify
29530 cls : 'roo-upload-cropbox-footer',
29533 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29543 onRender : function(ct, position)
29545 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29547 if (this.buttons.length) {
29549 Roo.each(this.buttons, function(bb) {
29551 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29553 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29559 this.maskEl = this.el;
29563 initEvents : function()
29565 this.urlAPI = (window.createObjectURL && window) ||
29566 (window.URL && URL.revokeObjectURL && URL) ||
29567 (window.webkitURL && webkitURL);
29569 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29570 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29572 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29573 this.selectorEl.hide();
29575 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29576 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29578 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29579 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29580 this.thumbEl.hide();
29582 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29583 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29585 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29586 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29587 this.errorEl.hide();
29589 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29590 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29591 this.footerEl.hide();
29593 this.setThumbBoxSize();
29599 this.fireEvent('initial', this);
29606 window.addEventListener("resize", function() { _this.resize(); } );
29608 this.bodyEl.on('click', this.beforeSelectFile, this);
29611 this.bodyEl.on('touchstart', this.onTouchStart, this);
29612 this.bodyEl.on('touchmove', this.onTouchMove, this);
29613 this.bodyEl.on('touchend', this.onTouchEnd, this);
29617 this.bodyEl.on('mousedown', this.onMouseDown, this);
29618 this.bodyEl.on('mousemove', this.onMouseMove, this);
29619 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29620 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29621 Roo.get(document).on('mouseup', this.onMouseUp, this);
29624 this.selectorEl.on('change', this.onFileSelected, this);
29630 this.baseScale = 1;
29632 this.baseRotate = 1;
29633 this.dragable = false;
29634 this.pinching = false;
29637 this.cropData = false;
29638 this.notifyEl.dom.innerHTML = this.emptyText;
29640 this.selectorEl.dom.value = '';
29644 resize : function()
29646 if(this.fireEvent('resize', this) != false){
29647 this.setThumbBoxPosition();
29648 this.setCanvasPosition();
29652 onFooterButtonClick : function(e, el, o, type)
29655 case 'rotate-left' :
29656 this.onRotateLeft(e);
29658 case 'rotate-right' :
29659 this.onRotateRight(e);
29662 this.beforeSelectFile(e);
29677 this.fireEvent('footerbuttonclick', this, type);
29680 beforeSelectFile : function(e)
29682 e.preventDefault();
29684 if(this.fireEvent('beforeselectfile', this) != false){
29685 this.selectorEl.dom.click();
29689 onFileSelected : function(e)
29691 e.preventDefault();
29693 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29697 var file = this.selectorEl.dom.files[0];
29699 if(this.fireEvent('inspect', this, file) != false){
29700 this.prepare(file);
29705 trash : function(e)
29707 this.fireEvent('trash', this);
29710 download : function(e)
29712 this.fireEvent('download', this);
29715 loadCanvas : function(src)
29717 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29721 this.imageEl = document.createElement('img');
29725 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29727 this.imageEl.src = src;
29731 onLoadCanvas : function()
29733 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29734 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29736 this.bodyEl.un('click', this.beforeSelectFile, this);
29738 this.notifyEl.hide();
29739 this.thumbEl.show();
29740 this.footerEl.show();
29742 this.baseRotateLevel();
29744 if(this.isDocument){
29745 this.setThumbBoxSize();
29748 this.setThumbBoxPosition();
29750 this.baseScaleLevel();
29756 this.canvasLoaded = true;
29759 this.maskEl.unmask();
29764 setCanvasPosition : function()
29766 if(!this.canvasEl){
29770 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29771 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29773 this.previewEl.setLeft(pw);
29774 this.previewEl.setTop(ph);
29778 onMouseDown : function(e)
29782 this.dragable = true;
29783 this.pinching = false;
29785 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29786 this.dragable = false;
29790 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29791 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29795 onMouseMove : function(e)
29799 if(!this.canvasLoaded){
29803 if (!this.dragable){
29807 var minX = Math.ceil(this.thumbEl.getLeft(true));
29808 var minY = Math.ceil(this.thumbEl.getTop(true));
29810 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29811 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29813 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29814 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29816 x = x - this.mouseX;
29817 y = y - this.mouseY;
29819 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29820 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29822 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29823 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29825 this.previewEl.setLeft(bgX);
29826 this.previewEl.setTop(bgY);
29828 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29829 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29832 onMouseUp : function(e)
29836 this.dragable = false;
29839 onMouseWheel : function(e)
29843 this.startScale = this.scale;
29845 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29847 if(!this.zoomable()){
29848 this.scale = this.startScale;
29857 zoomable : function()
29859 var minScale = this.thumbEl.getWidth() / this.minWidth;
29861 if(this.minWidth < this.minHeight){
29862 minScale = this.thumbEl.getHeight() / this.minHeight;
29865 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29866 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29870 (this.rotate == 0 || this.rotate == 180) &&
29872 width > this.imageEl.OriginWidth ||
29873 height > this.imageEl.OriginHeight ||
29874 (width < this.minWidth && height < this.minHeight)
29882 (this.rotate == 90 || this.rotate == 270) &&
29884 width > this.imageEl.OriginWidth ||
29885 height > this.imageEl.OriginHeight ||
29886 (width < this.minHeight && height < this.minWidth)
29893 !this.isDocument &&
29894 (this.rotate == 0 || this.rotate == 180) &&
29896 width < this.minWidth ||
29897 width > this.imageEl.OriginWidth ||
29898 height < this.minHeight ||
29899 height > this.imageEl.OriginHeight
29906 !this.isDocument &&
29907 (this.rotate == 90 || this.rotate == 270) &&
29909 width < this.minHeight ||
29910 width > this.imageEl.OriginWidth ||
29911 height < this.minWidth ||
29912 height > this.imageEl.OriginHeight
29922 onRotateLeft : function(e)
29924 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29926 var minScale = this.thumbEl.getWidth() / this.minWidth;
29928 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29929 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29931 this.startScale = this.scale;
29933 while (this.getScaleLevel() < minScale){
29935 this.scale = this.scale + 1;
29937 if(!this.zoomable()){
29942 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29943 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29948 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29955 this.scale = this.startScale;
29957 this.onRotateFail();
29962 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29964 if(this.isDocument){
29965 this.setThumbBoxSize();
29966 this.setThumbBoxPosition();
29967 this.setCanvasPosition();
29972 this.fireEvent('rotate', this, 'left');
29976 onRotateRight : function(e)
29978 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29980 var minScale = this.thumbEl.getWidth() / this.minWidth;
29982 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29983 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29985 this.startScale = this.scale;
29987 while (this.getScaleLevel() < minScale){
29989 this.scale = this.scale + 1;
29991 if(!this.zoomable()){
29996 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29997 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30002 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30009 this.scale = this.startScale;
30011 this.onRotateFail();
30016 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30018 if(this.isDocument){
30019 this.setThumbBoxSize();
30020 this.setThumbBoxPosition();
30021 this.setCanvasPosition();
30026 this.fireEvent('rotate', this, 'right');
30029 onRotateFail : function()
30031 this.errorEl.show(true);
30035 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30040 this.previewEl.dom.innerHTML = '';
30042 var canvasEl = document.createElement("canvas");
30044 var contextEl = canvasEl.getContext("2d");
30046 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30047 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30048 var center = this.imageEl.OriginWidth / 2;
30050 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30051 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30052 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30053 center = this.imageEl.OriginHeight / 2;
30056 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30058 contextEl.translate(center, center);
30059 contextEl.rotate(this.rotate * Math.PI / 180);
30061 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30063 this.canvasEl = document.createElement("canvas");
30065 this.contextEl = this.canvasEl.getContext("2d");
30067 switch (this.rotate) {
30070 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30071 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30073 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30078 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30079 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30081 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30082 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);
30086 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30091 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30092 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30094 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30095 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);
30099 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);
30104 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30105 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30107 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30108 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30112 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);
30119 this.previewEl.appendChild(this.canvasEl);
30121 this.setCanvasPosition();
30126 if(!this.canvasLoaded){
30130 var imageCanvas = document.createElement("canvas");
30132 var imageContext = imageCanvas.getContext("2d");
30134 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30135 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30137 var center = imageCanvas.width / 2;
30139 imageContext.translate(center, center);
30141 imageContext.rotate(this.rotate * Math.PI / 180);
30143 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30145 var canvas = document.createElement("canvas");
30147 var context = canvas.getContext("2d");
30149 canvas.width = this.minWidth;
30150 canvas.height = this.minHeight;
30152 switch (this.rotate) {
30155 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30156 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30158 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30159 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30161 var targetWidth = this.minWidth - 2 * x;
30162 var targetHeight = this.minHeight - 2 * y;
30166 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30167 scale = targetWidth / width;
30170 if(x > 0 && y == 0){
30171 scale = targetHeight / height;
30174 if(x > 0 && y > 0){
30175 scale = targetWidth / width;
30177 if(width < height){
30178 scale = targetHeight / height;
30182 context.scale(scale, scale);
30184 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30185 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30187 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30188 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30190 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30195 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30196 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30198 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30199 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30201 var targetWidth = this.minWidth - 2 * x;
30202 var targetHeight = this.minHeight - 2 * y;
30206 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30207 scale = targetWidth / width;
30210 if(x > 0 && y == 0){
30211 scale = targetHeight / height;
30214 if(x > 0 && y > 0){
30215 scale = targetWidth / width;
30217 if(width < height){
30218 scale = targetHeight / height;
30222 context.scale(scale, scale);
30224 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30225 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30227 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30228 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30230 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30232 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30237 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30238 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30240 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30241 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30243 var targetWidth = this.minWidth - 2 * x;
30244 var targetHeight = this.minHeight - 2 * y;
30248 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30249 scale = targetWidth / width;
30252 if(x > 0 && y == 0){
30253 scale = targetHeight / height;
30256 if(x > 0 && y > 0){
30257 scale = targetWidth / width;
30259 if(width < height){
30260 scale = targetHeight / height;
30264 context.scale(scale, scale);
30266 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30267 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30269 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30270 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30272 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30273 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30275 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30280 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30281 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30283 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30284 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30286 var targetWidth = this.minWidth - 2 * x;
30287 var targetHeight = this.minHeight - 2 * y;
30291 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30292 scale = targetWidth / width;
30295 if(x > 0 && y == 0){
30296 scale = targetHeight / height;
30299 if(x > 0 && y > 0){
30300 scale = targetWidth / width;
30302 if(width < height){
30303 scale = targetHeight / height;
30307 context.scale(scale, scale);
30309 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30310 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30312 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30313 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30315 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30317 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30324 this.cropData = canvas.toDataURL(this.cropType);
30326 if(this.fireEvent('crop', this, this.cropData) !== false){
30327 this.process(this.file, this.cropData);
30334 setThumbBoxSize : function()
30338 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30339 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30340 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30342 this.minWidth = width;
30343 this.minHeight = height;
30345 if(this.rotate == 90 || this.rotate == 270){
30346 this.minWidth = height;
30347 this.minHeight = width;
30352 width = Math.ceil(this.minWidth * height / this.minHeight);
30354 if(this.minWidth > this.minHeight){
30356 height = Math.ceil(this.minHeight * width / this.minWidth);
30359 this.thumbEl.setStyle({
30360 width : width + 'px',
30361 height : height + 'px'
30368 setThumbBoxPosition : function()
30370 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30371 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30373 this.thumbEl.setLeft(x);
30374 this.thumbEl.setTop(y);
30378 baseRotateLevel : function()
30380 this.baseRotate = 1;
30383 typeof(this.exif) != 'undefined' &&
30384 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30385 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30387 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30390 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30394 baseScaleLevel : function()
30398 if(this.isDocument){
30400 if(this.baseRotate == 6 || this.baseRotate == 8){
30402 height = this.thumbEl.getHeight();
30403 this.baseScale = height / this.imageEl.OriginWidth;
30405 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30406 width = this.thumbEl.getWidth();
30407 this.baseScale = width / this.imageEl.OriginHeight;
30413 height = this.thumbEl.getHeight();
30414 this.baseScale = height / this.imageEl.OriginHeight;
30416 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30417 width = this.thumbEl.getWidth();
30418 this.baseScale = width / this.imageEl.OriginWidth;
30424 if(this.baseRotate == 6 || this.baseRotate == 8){
30426 width = this.thumbEl.getHeight();
30427 this.baseScale = width / this.imageEl.OriginHeight;
30429 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30430 height = this.thumbEl.getWidth();
30431 this.baseScale = height / this.imageEl.OriginHeight;
30434 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30435 height = this.thumbEl.getWidth();
30436 this.baseScale = height / this.imageEl.OriginHeight;
30438 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30439 width = this.thumbEl.getHeight();
30440 this.baseScale = width / this.imageEl.OriginWidth;
30447 width = this.thumbEl.getWidth();
30448 this.baseScale = width / this.imageEl.OriginWidth;
30450 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30451 height = this.thumbEl.getHeight();
30452 this.baseScale = height / this.imageEl.OriginHeight;
30455 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30457 height = this.thumbEl.getHeight();
30458 this.baseScale = height / this.imageEl.OriginHeight;
30460 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30461 width = this.thumbEl.getWidth();
30462 this.baseScale = width / this.imageEl.OriginWidth;
30470 getScaleLevel : function()
30472 return this.baseScale * Math.pow(1.1, this.scale);
30475 onTouchStart : function(e)
30477 if(!this.canvasLoaded){
30478 this.beforeSelectFile(e);
30482 var touches = e.browserEvent.touches;
30488 if(touches.length == 1){
30489 this.onMouseDown(e);
30493 if(touches.length != 2){
30499 for(var i = 0, finger; finger = touches[i]; i++){
30500 coords.push(finger.pageX, finger.pageY);
30503 var x = Math.pow(coords[0] - coords[2], 2);
30504 var y = Math.pow(coords[1] - coords[3], 2);
30506 this.startDistance = Math.sqrt(x + y);
30508 this.startScale = this.scale;
30510 this.pinching = true;
30511 this.dragable = false;
30515 onTouchMove : function(e)
30517 if(!this.pinching && !this.dragable){
30521 var touches = e.browserEvent.touches;
30528 this.onMouseMove(e);
30534 for(var i = 0, finger; finger = touches[i]; i++){
30535 coords.push(finger.pageX, finger.pageY);
30538 var x = Math.pow(coords[0] - coords[2], 2);
30539 var y = Math.pow(coords[1] - coords[3], 2);
30541 this.endDistance = Math.sqrt(x + y);
30543 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30545 if(!this.zoomable()){
30546 this.scale = this.startScale;
30554 onTouchEnd : function(e)
30556 this.pinching = false;
30557 this.dragable = false;
30561 process : function(file, crop)
30564 this.maskEl.mask(this.loadingText);
30567 this.xhr = new XMLHttpRequest();
30569 file.xhr = this.xhr;
30571 this.xhr.open(this.method, this.url, true);
30574 "Accept": "application/json",
30575 "Cache-Control": "no-cache",
30576 "X-Requested-With": "XMLHttpRequest"
30579 for (var headerName in headers) {
30580 var headerValue = headers[headerName];
30582 this.xhr.setRequestHeader(headerName, headerValue);
30588 this.xhr.onload = function()
30590 _this.xhrOnLoad(_this.xhr);
30593 this.xhr.onerror = function()
30595 _this.xhrOnError(_this.xhr);
30598 var formData = new FormData();
30600 formData.append('returnHTML', 'NO');
30603 formData.append('crop', crop);
30606 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30607 formData.append(this.paramName, file, file.name);
30610 if(typeof(file.filename) != 'undefined'){
30611 formData.append('filename', file.filename);
30614 if(typeof(file.mimetype) != 'undefined'){
30615 formData.append('mimetype', file.mimetype);
30618 if(this.fireEvent('arrange', this, formData) != false){
30619 this.xhr.send(formData);
30623 xhrOnLoad : function(xhr)
30626 this.maskEl.unmask();
30629 if (xhr.readyState !== 4) {
30630 this.fireEvent('exception', this, xhr);
30634 var response = Roo.decode(xhr.responseText);
30636 if(!response.success){
30637 this.fireEvent('exception', this, xhr);
30641 var response = Roo.decode(xhr.responseText);
30643 this.fireEvent('upload', this, response);
30647 xhrOnError : function()
30650 this.maskEl.unmask();
30653 Roo.log('xhr on error');
30655 var response = Roo.decode(xhr.responseText);
30661 prepare : function(file)
30664 this.maskEl.mask(this.loadingText);
30670 if(typeof(file) === 'string'){
30671 this.loadCanvas(file);
30675 if(!file || !this.urlAPI){
30680 this.cropType = file.type;
30684 if(this.fireEvent('prepare', this, this.file) != false){
30686 var reader = new FileReader();
30688 reader.onload = function (e) {
30689 if (e.target.error) {
30690 Roo.log(e.target.error);
30694 var buffer = e.target.result,
30695 dataView = new DataView(buffer),
30697 maxOffset = dataView.byteLength - 4,
30701 if (dataView.getUint16(0) === 0xffd8) {
30702 while (offset < maxOffset) {
30703 markerBytes = dataView.getUint16(offset);
30705 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30706 markerLength = dataView.getUint16(offset + 2) + 2;
30707 if (offset + markerLength > dataView.byteLength) {
30708 Roo.log('Invalid meta data: Invalid segment size.');
30712 if(markerBytes == 0xffe1){
30713 _this.parseExifData(
30720 offset += markerLength;
30730 var url = _this.urlAPI.createObjectURL(_this.file);
30732 _this.loadCanvas(url);
30737 reader.readAsArrayBuffer(this.file);
30743 parseExifData : function(dataView, offset, length)
30745 var tiffOffset = offset + 10,
30749 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30750 // No Exif data, might be XMP data instead
30754 // Check for the ASCII code for "Exif" (0x45786966):
30755 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30756 // No Exif data, might be XMP data instead
30759 if (tiffOffset + 8 > dataView.byteLength) {
30760 Roo.log('Invalid Exif data: Invalid segment size.');
30763 // Check for the two null bytes:
30764 if (dataView.getUint16(offset + 8) !== 0x0000) {
30765 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30768 // Check the byte alignment:
30769 switch (dataView.getUint16(tiffOffset)) {
30771 littleEndian = true;
30774 littleEndian = false;
30777 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30780 // Check for the TIFF tag marker (0x002A):
30781 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30782 Roo.log('Invalid Exif data: Missing TIFF marker.');
30785 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30786 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30788 this.parseExifTags(
30791 tiffOffset + dirOffset,
30796 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30801 if (dirOffset + 6 > dataView.byteLength) {
30802 Roo.log('Invalid Exif data: Invalid directory offset.');
30805 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30806 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30807 if (dirEndOffset + 4 > dataView.byteLength) {
30808 Roo.log('Invalid Exif data: Invalid directory size.');
30811 for (i = 0; i < tagsNumber; i += 1) {
30815 dirOffset + 2 + 12 * i, // tag offset
30819 // Return the offset to the next directory:
30820 return dataView.getUint32(dirEndOffset, littleEndian);
30823 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30825 var tag = dataView.getUint16(offset, littleEndian);
30827 this.exif[tag] = this.getExifValue(
30831 dataView.getUint16(offset + 2, littleEndian), // tag type
30832 dataView.getUint32(offset + 4, littleEndian), // tag length
30837 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30839 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30848 Roo.log('Invalid Exif data: Invalid tag type.');
30852 tagSize = tagType.size * length;
30853 // Determine if the value is contained in the dataOffset bytes,
30854 // or if the value at the dataOffset is a pointer to the actual data:
30855 dataOffset = tagSize > 4 ?
30856 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30857 if (dataOffset + tagSize > dataView.byteLength) {
30858 Roo.log('Invalid Exif data: Invalid data offset.');
30861 if (length === 1) {
30862 return tagType.getValue(dataView, dataOffset, littleEndian);
30865 for (i = 0; i < length; i += 1) {
30866 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30869 if (tagType.ascii) {
30871 // Concatenate the chars:
30872 for (i = 0; i < values.length; i += 1) {
30874 // Ignore the terminating NULL byte(s):
30875 if (c === '\u0000') {
30887 Roo.apply(Roo.bootstrap.UploadCropbox, {
30889 'Orientation': 0x0112
30893 1: 0, //'top-left',
30895 3: 180, //'bottom-right',
30896 // 4: 'bottom-left',
30898 6: 90, //'right-top',
30899 // 7: 'right-bottom',
30900 8: 270 //'left-bottom'
30904 // byte, 8-bit unsigned int:
30906 getValue: function (dataView, dataOffset) {
30907 return dataView.getUint8(dataOffset);
30911 // ascii, 8-bit byte:
30913 getValue: function (dataView, dataOffset) {
30914 return String.fromCharCode(dataView.getUint8(dataOffset));
30919 // short, 16 bit int:
30921 getValue: function (dataView, dataOffset, littleEndian) {
30922 return dataView.getUint16(dataOffset, littleEndian);
30926 // long, 32 bit int:
30928 getValue: function (dataView, dataOffset, littleEndian) {
30929 return dataView.getUint32(dataOffset, littleEndian);
30933 // rational = two long values, first is numerator, second is denominator:
30935 getValue: function (dataView, dataOffset, littleEndian) {
30936 return dataView.getUint32(dataOffset, littleEndian) /
30937 dataView.getUint32(dataOffset + 4, littleEndian);
30941 // slong, 32 bit signed int:
30943 getValue: function (dataView, dataOffset, littleEndian) {
30944 return dataView.getInt32(dataOffset, littleEndian);
30948 // srational, two slongs, first is numerator, second is denominator:
30950 getValue: function (dataView, dataOffset, littleEndian) {
30951 return dataView.getInt32(dataOffset, littleEndian) /
30952 dataView.getInt32(dataOffset + 4, littleEndian);
30962 cls : 'btn-group roo-upload-cropbox-rotate-left',
30963 action : 'rotate-left',
30967 cls : 'btn btn-default',
30968 html : '<i class="fa fa-undo"></i>'
30974 cls : 'btn-group roo-upload-cropbox-picture',
30975 action : 'picture',
30979 cls : 'btn btn-default',
30980 html : '<i class="fa fa-picture-o"></i>'
30986 cls : 'btn-group roo-upload-cropbox-rotate-right',
30987 action : 'rotate-right',
30991 cls : 'btn btn-default',
30992 html : '<i class="fa fa-repeat"></i>'
31000 cls : 'btn-group roo-upload-cropbox-rotate-left',
31001 action : 'rotate-left',
31005 cls : 'btn btn-default',
31006 html : '<i class="fa fa-undo"></i>'
31012 cls : 'btn-group roo-upload-cropbox-download',
31013 action : 'download',
31017 cls : 'btn btn-default',
31018 html : '<i class="fa fa-download"></i>'
31024 cls : 'btn-group roo-upload-cropbox-crop',
31029 cls : 'btn btn-default',
31030 html : '<i class="fa fa-crop"></i>'
31036 cls : 'btn-group roo-upload-cropbox-trash',
31041 cls : 'btn btn-default',
31042 html : '<i class="fa fa-trash"></i>'
31048 cls : 'btn-group roo-upload-cropbox-rotate-right',
31049 action : 'rotate-right',
31053 cls : 'btn btn-default',
31054 html : '<i class="fa fa-repeat"></i>'
31062 cls : 'btn-group roo-upload-cropbox-rotate-left',
31063 action : 'rotate-left',
31067 cls : 'btn btn-default',
31068 html : '<i class="fa fa-undo"></i>'
31074 cls : 'btn-group roo-upload-cropbox-rotate-right',
31075 action : 'rotate-right',
31079 cls : 'btn btn-default',
31080 html : '<i class="fa fa-repeat"></i>'
31093 * @class Roo.bootstrap.DocumentManager
31094 * @extends Roo.bootstrap.Component
31095 * Bootstrap DocumentManager class
31096 * @cfg {String} paramName default 'imageUpload'
31097 * @cfg {String} toolTipName default 'filename'
31098 * @cfg {String} method default POST
31099 * @cfg {String} url action url
31100 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31101 * @cfg {Boolean} multiple multiple upload default true
31102 * @cfg {Number} thumbSize default 300
31103 * @cfg {String} fieldLabel
31104 * @cfg {Number} labelWidth default 4
31105 * @cfg {String} labelAlign (left|top) default left
31106 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31107 * @cfg {Number} labellg set the width of label (1-12)
31108 * @cfg {Number} labelmd set the width of label (1-12)
31109 * @cfg {Number} labelsm set the width of label (1-12)
31110 * @cfg {Number} labelxs set the width of label (1-12)
31113 * Create a new DocumentManager
31114 * @param {Object} config The config object
31117 Roo.bootstrap.DocumentManager = function(config){
31118 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31121 this.delegates = [];
31126 * Fire when initial the DocumentManager
31127 * @param {Roo.bootstrap.DocumentManager} this
31132 * inspect selected file
31133 * @param {Roo.bootstrap.DocumentManager} this
31134 * @param {File} file
31139 * Fire when xhr load exception
31140 * @param {Roo.bootstrap.DocumentManager} this
31141 * @param {XMLHttpRequest} xhr
31143 "exception" : true,
31145 * @event afterupload
31146 * Fire when xhr load exception
31147 * @param {Roo.bootstrap.DocumentManager} this
31148 * @param {XMLHttpRequest} xhr
31150 "afterupload" : true,
31153 * prepare the form data
31154 * @param {Roo.bootstrap.DocumentManager} this
31155 * @param {Object} formData
31160 * Fire when remove the file
31161 * @param {Roo.bootstrap.DocumentManager} this
31162 * @param {Object} file
31167 * Fire after refresh the file
31168 * @param {Roo.bootstrap.DocumentManager} this
31173 * Fire after click the image
31174 * @param {Roo.bootstrap.DocumentManager} this
31175 * @param {Object} file
31180 * Fire when upload a image and editable set to true
31181 * @param {Roo.bootstrap.DocumentManager} this
31182 * @param {Object} file
31186 * @event beforeselectfile
31187 * Fire before select file
31188 * @param {Roo.bootstrap.DocumentManager} this
31190 "beforeselectfile" : true,
31193 * Fire before process file
31194 * @param {Roo.bootstrap.DocumentManager} this
31195 * @param {Object} file
31199 * @event previewrendered
31200 * Fire when preview rendered
31201 * @param {Roo.bootstrap.DocumentManager} this
31202 * @param {Object} file
31204 "previewrendered" : true,
31207 "previewResize" : true
31212 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31221 paramName : 'imageUpload',
31222 toolTipName : 'filename',
31225 labelAlign : 'left',
31235 getAutoCreate : function()
31237 var managerWidget = {
31239 cls : 'roo-document-manager',
31243 cls : 'roo-document-manager-selector',
31248 cls : 'roo-document-manager-uploader',
31252 cls : 'roo-document-manager-upload-btn',
31253 html : '<i class="fa fa-plus"></i>'
31264 cls : 'column col-md-12',
31269 if(this.fieldLabel.length){
31274 cls : 'column col-md-12',
31275 html : this.fieldLabel
31279 cls : 'column col-md-12',
31284 if(this.labelAlign == 'left'){
31289 html : this.fieldLabel
31298 if(this.labelWidth > 12){
31299 content[0].style = "width: " + this.labelWidth + 'px';
31302 if(this.labelWidth < 13 && this.labelmd == 0){
31303 this.labelmd = this.labelWidth;
31306 if(this.labellg > 0){
31307 content[0].cls += ' col-lg-' + this.labellg;
31308 content[1].cls += ' col-lg-' + (12 - this.labellg);
31311 if(this.labelmd > 0){
31312 content[0].cls += ' col-md-' + this.labelmd;
31313 content[1].cls += ' col-md-' + (12 - this.labelmd);
31316 if(this.labelsm > 0){
31317 content[0].cls += ' col-sm-' + this.labelsm;
31318 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31321 if(this.labelxs > 0){
31322 content[0].cls += ' col-xs-' + this.labelxs;
31323 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31331 cls : 'row clearfix',
31339 initEvents : function()
31341 this.managerEl = this.el.select('.roo-document-manager', true).first();
31342 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31344 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31345 this.selectorEl.hide();
31348 this.selectorEl.attr('multiple', 'multiple');
31351 this.selectorEl.on('change', this.onFileSelected, this);
31353 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31354 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31356 this.uploader.on('click', this.onUploaderClick, this);
31358 this.renderProgressDialog();
31362 window.addEventListener("resize", function() { _this.refresh(); } );
31364 this.fireEvent('initial', this);
31367 renderProgressDialog : function()
31371 this.progressDialog = new Roo.bootstrap.Modal({
31372 cls : 'roo-document-manager-progress-dialog',
31373 allow_close : false,
31384 btnclick : function() {
31385 _this.uploadCancel();
31391 this.progressDialog.render(Roo.get(document.body));
31393 this.progress = new Roo.bootstrap.Progress({
31394 cls : 'roo-document-manager-progress',
31399 this.progress.render(this.progressDialog.getChildContainer());
31401 this.progressBar = new Roo.bootstrap.ProgressBar({
31402 cls : 'roo-document-manager-progress-bar',
31405 aria_valuemax : 12,
31409 this.progressBar.render(this.progress.getChildContainer());
31412 onUploaderClick : function(e)
31414 e.preventDefault();
31416 if(this.fireEvent('beforeselectfile', this) != false){
31417 this.selectorEl.dom.click();
31422 onFileSelected : function(e)
31424 e.preventDefault();
31426 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31430 Roo.each(this.selectorEl.dom.files, function(file){
31431 if(this.fireEvent('inspect', this, file) != false){
31432 this.files.push(file);
31442 this.selectorEl.dom.value = '';
31444 if(!this.files || !this.files.length){
31448 if(this.boxes > 0 && this.files.length > this.boxes){
31449 this.files = this.files.slice(0, this.boxes);
31452 this.uploader.show();
31454 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31455 this.uploader.hide();
31464 Roo.each(this.files, function(file){
31466 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31467 var f = this.renderPreview(file);
31472 if(file.type.indexOf('image') != -1){
31473 this.delegates.push(
31475 _this.process(file);
31476 }).createDelegate(this)
31484 _this.process(file);
31485 }).createDelegate(this)
31490 this.files = files;
31492 this.delegates = this.delegates.concat(docs);
31494 if(!this.delegates.length){
31499 this.progressBar.aria_valuemax = this.delegates.length;
31506 arrange : function()
31508 if(!this.delegates.length){
31509 this.progressDialog.hide();
31514 var delegate = this.delegates.shift();
31516 this.progressDialog.show();
31518 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31520 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31525 refresh : function()
31527 this.uploader.show();
31529 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31530 this.uploader.hide();
31533 Roo.isTouch ? this.closable(false) : this.closable(true);
31535 this.fireEvent('refresh', this);
31538 onRemove : function(e, el, o)
31540 e.preventDefault();
31542 this.fireEvent('remove', this, o);
31546 remove : function(o)
31550 Roo.each(this.files, function(file){
31551 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31560 this.files = files;
31567 Roo.each(this.files, function(file){
31572 file.target.remove();
31581 onClick : function(e, el, o)
31583 e.preventDefault();
31585 this.fireEvent('click', this, o);
31589 closable : function(closable)
31591 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31593 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31605 xhrOnLoad : function(xhr)
31607 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31611 if (xhr.readyState !== 4) {
31613 this.fireEvent('exception', this, xhr);
31617 var response = Roo.decode(xhr.responseText);
31619 if(!response.success){
31621 this.fireEvent('exception', this, xhr);
31625 var file = this.renderPreview(response.data);
31627 this.files.push(file);
31631 this.fireEvent('afterupload', this, xhr);
31635 xhrOnError : function(xhr)
31637 Roo.log('xhr on error');
31639 var response = Roo.decode(xhr.responseText);
31646 process : function(file)
31648 if(this.fireEvent('process', this, file) !== false){
31649 if(this.editable && file.type.indexOf('image') != -1){
31650 this.fireEvent('edit', this, file);
31654 this.uploadStart(file, false);
31661 uploadStart : function(file, crop)
31663 this.xhr = new XMLHttpRequest();
31665 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31670 file.xhr = this.xhr;
31672 this.managerEl.createChild({
31674 cls : 'roo-document-manager-loading',
31678 tooltip : file.name,
31679 cls : 'roo-document-manager-thumb',
31680 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31686 this.xhr.open(this.method, this.url, true);
31689 "Accept": "application/json",
31690 "Cache-Control": "no-cache",
31691 "X-Requested-With": "XMLHttpRequest"
31694 for (var headerName in headers) {
31695 var headerValue = headers[headerName];
31697 this.xhr.setRequestHeader(headerName, headerValue);
31703 this.xhr.onload = function()
31705 _this.xhrOnLoad(_this.xhr);
31708 this.xhr.onerror = function()
31710 _this.xhrOnError(_this.xhr);
31713 var formData = new FormData();
31715 formData.append('returnHTML', 'NO');
31718 formData.append('crop', crop);
31721 formData.append(this.paramName, file, file.name);
31728 if(this.fireEvent('prepare', this, formData, options) != false){
31730 if(options.manually){
31734 this.xhr.send(formData);
31738 this.uploadCancel();
31741 uploadCancel : function()
31747 this.delegates = [];
31749 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31756 renderPreview : function(file)
31758 if(typeof(file.target) != 'undefined' && file.target){
31762 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31764 var previewEl = this.managerEl.createChild({
31766 cls : 'roo-document-manager-preview',
31770 tooltip : file[this.toolTipName],
31771 cls : 'roo-document-manager-thumb',
31772 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31777 html : '<i class="fa fa-times-circle"></i>'
31782 var close = previewEl.select('button.close', true).first();
31784 close.on('click', this.onRemove, this, file);
31786 file.target = previewEl;
31788 var image = previewEl.select('img', true).first();
31792 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31794 image.on('click', this.onClick, this, file);
31796 this.fireEvent('previewrendered', this, file);
31802 onPreviewLoad : function(file, image)
31804 if(typeof(file.target) == 'undefined' || !file.target){
31808 var width = image.dom.naturalWidth || image.dom.width;
31809 var height = image.dom.naturalHeight || image.dom.height;
31811 if(!this.previewResize) {
31815 if(width > height){
31816 file.target.addClass('wide');
31820 file.target.addClass('tall');
31825 uploadFromSource : function(file, crop)
31827 this.xhr = new XMLHttpRequest();
31829 this.managerEl.createChild({
31831 cls : 'roo-document-manager-loading',
31835 tooltip : file.name,
31836 cls : 'roo-document-manager-thumb',
31837 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31843 this.xhr.open(this.method, this.url, true);
31846 "Accept": "application/json",
31847 "Cache-Control": "no-cache",
31848 "X-Requested-With": "XMLHttpRequest"
31851 for (var headerName in headers) {
31852 var headerValue = headers[headerName];
31854 this.xhr.setRequestHeader(headerName, headerValue);
31860 this.xhr.onload = function()
31862 _this.xhrOnLoad(_this.xhr);
31865 this.xhr.onerror = function()
31867 _this.xhrOnError(_this.xhr);
31870 var formData = new FormData();
31872 formData.append('returnHTML', 'NO');
31874 formData.append('crop', crop);
31876 if(typeof(file.filename) != 'undefined'){
31877 formData.append('filename', file.filename);
31880 if(typeof(file.mimetype) != 'undefined'){
31881 formData.append('mimetype', file.mimetype);
31886 if(this.fireEvent('prepare', this, formData) != false){
31887 this.xhr.send(formData);
31897 * @class Roo.bootstrap.DocumentViewer
31898 * @extends Roo.bootstrap.Component
31899 * Bootstrap DocumentViewer class
31900 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31901 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31904 * Create a new DocumentViewer
31905 * @param {Object} config The config object
31908 Roo.bootstrap.DocumentViewer = function(config){
31909 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31914 * Fire after initEvent
31915 * @param {Roo.bootstrap.DocumentViewer} this
31921 * @param {Roo.bootstrap.DocumentViewer} this
31926 * Fire after download button
31927 * @param {Roo.bootstrap.DocumentViewer} this
31932 * Fire after trash button
31933 * @param {Roo.bootstrap.DocumentViewer} this
31940 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31942 showDownload : true,
31946 getAutoCreate : function()
31950 cls : 'roo-document-viewer',
31954 cls : 'roo-document-viewer-body',
31958 cls : 'roo-document-viewer-thumb',
31962 cls : 'roo-document-viewer-image'
31970 cls : 'roo-document-viewer-footer',
31973 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31977 cls : 'btn-group roo-document-viewer-download',
31981 cls : 'btn btn-default',
31982 html : '<i class="fa fa-download"></i>'
31988 cls : 'btn-group roo-document-viewer-trash',
31992 cls : 'btn btn-default',
31993 html : '<i class="fa fa-trash"></i>'
32006 initEvents : function()
32008 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32009 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32011 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32012 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32014 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32015 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32017 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32018 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32020 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32021 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32023 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32024 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32026 this.bodyEl.on('click', this.onClick, this);
32027 this.downloadBtn.on('click', this.onDownload, this);
32028 this.trashBtn.on('click', this.onTrash, this);
32030 this.downloadBtn.hide();
32031 this.trashBtn.hide();
32033 if(this.showDownload){
32034 this.downloadBtn.show();
32037 if(this.showTrash){
32038 this.trashBtn.show();
32041 if(!this.showDownload && !this.showTrash) {
32042 this.footerEl.hide();
32047 initial : function()
32049 this.fireEvent('initial', this);
32053 onClick : function(e)
32055 e.preventDefault();
32057 this.fireEvent('click', this);
32060 onDownload : function(e)
32062 e.preventDefault();
32064 this.fireEvent('download', this);
32067 onTrash : function(e)
32069 e.preventDefault();
32071 this.fireEvent('trash', this);
32083 * @class Roo.bootstrap.NavProgressBar
32084 * @extends Roo.bootstrap.Component
32085 * Bootstrap NavProgressBar class
32088 * Create a new nav progress bar
32089 * @param {Object} config The config object
32092 Roo.bootstrap.NavProgressBar = function(config){
32093 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32095 this.bullets = this.bullets || [];
32097 // Roo.bootstrap.NavProgressBar.register(this);
32101 * Fires when the active item changes
32102 * @param {Roo.bootstrap.NavProgressBar} this
32103 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32104 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32111 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32116 getAutoCreate : function()
32118 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32122 cls : 'roo-navigation-bar-group',
32126 cls : 'roo-navigation-top-bar'
32130 cls : 'roo-navigation-bullets-bar',
32134 cls : 'roo-navigation-bar'
32141 cls : 'roo-navigation-bottom-bar'
32151 initEvents: function()
32156 onRender : function(ct, position)
32158 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32160 if(this.bullets.length){
32161 Roo.each(this.bullets, function(b){
32170 addItem : function(cfg)
32172 var item = new Roo.bootstrap.NavProgressItem(cfg);
32174 item.parentId = this.id;
32175 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32178 var top = new Roo.bootstrap.Element({
32180 cls : 'roo-navigation-bar-text'
32183 var bottom = new Roo.bootstrap.Element({
32185 cls : 'roo-navigation-bar-text'
32188 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32189 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32191 var topText = new Roo.bootstrap.Element({
32193 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32196 var bottomText = new Roo.bootstrap.Element({
32198 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32201 topText.onRender(top.el, null);
32202 bottomText.onRender(bottom.el, null);
32205 item.bottomEl = bottom;
32208 this.barItems.push(item);
32213 getActive : function()
32215 var active = false;
32217 Roo.each(this.barItems, function(v){
32219 if (!v.isActive()) {
32231 setActiveItem : function(item)
32235 Roo.each(this.barItems, function(v){
32236 if (v.rid == item.rid) {
32240 if (v.isActive()) {
32241 v.setActive(false);
32246 item.setActive(true);
32248 this.fireEvent('changed', this, item, prev);
32251 getBarItem: function(rid)
32255 Roo.each(this.barItems, function(e) {
32256 if (e.rid != rid) {
32267 indexOfItem : function(item)
32271 Roo.each(this.barItems, function(v, i){
32273 if (v.rid != item.rid) {
32284 setActiveNext : function()
32286 var i = this.indexOfItem(this.getActive());
32288 if (i > this.barItems.length) {
32292 this.setActiveItem(this.barItems[i+1]);
32295 setActivePrev : function()
32297 var i = this.indexOfItem(this.getActive());
32303 this.setActiveItem(this.barItems[i-1]);
32306 format : function()
32308 if(!this.barItems.length){
32312 var width = 100 / this.barItems.length;
32314 Roo.each(this.barItems, function(i){
32315 i.el.setStyle('width', width + '%');
32316 i.topEl.el.setStyle('width', width + '%');
32317 i.bottomEl.el.setStyle('width', width + '%');
32326 * Nav Progress Item
32331 * @class Roo.bootstrap.NavProgressItem
32332 * @extends Roo.bootstrap.Component
32333 * Bootstrap NavProgressItem class
32334 * @cfg {String} rid the reference id
32335 * @cfg {Boolean} active (true|false) Is item active default false
32336 * @cfg {Boolean} disabled (true|false) Is item active default false
32337 * @cfg {String} html
32338 * @cfg {String} position (top|bottom) text position default bottom
32339 * @cfg {String} icon show icon instead of number
32342 * Create a new NavProgressItem
32343 * @param {Object} config The config object
32345 Roo.bootstrap.NavProgressItem = function(config){
32346 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32351 * The raw click event for the entire grid.
32352 * @param {Roo.bootstrap.NavProgressItem} this
32353 * @param {Roo.EventObject} e
32360 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32366 position : 'bottom',
32369 getAutoCreate : function()
32371 var iconCls = 'roo-navigation-bar-item-icon';
32373 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32377 cls: 'roo-navigation-bar-item',
32387 cfg.cls += ' active';
32390 cfg.cls += ' disabled';
32396 disable : function()
32398 this.setDisabled(true);
32401 enable : function()
32403 this.setDisabled(false);
32406 initEvents: function()
32408 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32410 this.iconEl.on('click', this.onClick, this);
32413 onClick : function(e)
32415 e.preventDefault();
32421 if(this.fireEvent('click', this, e) === false){
32425 this.parent().setActiveItem(this);
32428 isActive: function ()
32430 return this.active;
32433 setActive : function(state)
32435 if(this.active == state){
32439 this.active = state;
32442 this.el.addClass('active');
32446 this.el.removeClass('active');
32451 setDisabled : function(state)
32453 if(this.disabled == state){
32457 this.disabled = state;
32460 this.el.addClass('disabled');
32464 this.el.removeClass('disabled');
32467 tooltipEl : function()
32469 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32482 * @class Roo.bootstrap.FieldLabel
32483 * @extends Roo.bootstrap.Component
32484 * Bootstrap FieldLabel class
32485 * @cfg {String} html contents of the element
32486 * @cfg {String} tag tag of the element default label
32487 * @cfg {String} cls class of the element
32488 * @cfg {String} target label target
32489 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32490 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32491 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32492 * @cfg {String} iconTooltip default "This field is required"
32493 * @cfg {String} indicatorpos (left|right) default left
32496 * Create a new FieldLabel
32497 * @param {Object} config The config object
32500 Roo.bootstrap.FieldLabel = function(config){
32501 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32506 * Fires after the field has been marked as invalid.
32507 * @param {Roo.form.FieldLabel} this
32508 * @param {String} msg The validation message
32513 * Fires after the field has been validated with no errors.
32514 * @param {Roo.form.FieldLabel} this
32520 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32527 invalidClass : 'has-warning',
32528 validClass : 'has-success',
32529 iconTooltip : 'This field is required',
32530 indicatorpos : 'left',
32532 getAutoCreate : function(){
32535 if (!this.allowBlank) {
32541 cls : 'roo-bootstrap-field-label ' + this.cls,
32546 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32547 tooltip : this.iconTooltip
32556 if(this.indicatorpos == 'right'){
32559 cls : 'roo-bootstrap-field-label ' + this.cls,
32568 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32569 tooltip : this.iconTooltip
32578 initEvents: function()
32580 Roo.bootstrap.Element.superclass.initEvents.call(this);
32582 this.indicator = this.indicatorEl();
32584 if(this.indicator){
32585 this.indicator.removeClass('visible');
32586 this.indicator.addClass('invisible');
32589 Roo.bootstrap.FieldLabel.register(this);
32592 indicatorEl : function()
32594 var indicator = this.el.select('i.roo-required-indicator',true).first();
32605 * Mark this field as valid
32607 markValid : function()
32609 if(this.indicator){
32610 this.indicator.removeClass('visible');
32611 this.indicator.addClass('invisible');
32613 if (Roo.bootstrap.version == 3) {
32614 this.el.removeClass(this.invalidClass);
32615 this.el.addClass(this.validClass);
32617 this.el.removeClass('is-invalid');
32618 this.el.addClass('is-valid');
32622 this.fireEvent('valid', this);
32626 * Mark this field as invalid
32627 * @param {String} msg The validation message
32629 markInvalid : function(msg)
32631 if(this.indicator){
32632 this.indicator.removeClass('invisible');
32633 this.indicator.addClass('visible');
32635 if (Roo.bootstrap.version == 3) {
32636 this.el.removeClass(this.validClass);
32637 this.el.addClass(this.invalidClass);
32639 this.el.removeClass('is-valid');
32640 this.el.addClass('is-invalid');
32644 this.fireEvent('invalid', this, msg);
32650 Roo.apply(Roo.bootstrap.FieldLabel, {
32655 * register a FieldLabel Group
32656 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32658 register : function(label)
32660 if(this.groups.hasOwnProperty(label.target)){
32664 this.groups[label.target] = label;
32668 * fetch a FieldLabel Group based on the target
32669 * @param {string} target
32670 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32672 get: function(target) {
32673 if (typeof(this.groups[target]) == 'undefined') {
32677 return this.groups[target] ;
32686 * page DateSplitField.
32692 * @class Roo.bootstrap.DateSplitField
32693 * @extends Roo.bootstrap.Component
32694 * Bootstrap DateSplitField class
32695 * @cfg {string} fieldLabel - the label associated
32696 * @cfg {Number} labelWidth set the width of label (0-12)
32697 * @cfg {String} labelAlign (top|left)
32698 * @cfg {Boolean} dayAllowBlank (true|false) default false
32699 * @cfg {Boolean} monthAllowBlank (true|false) default false
32700 * @cfg {Boolean} yearAllowBlank (true|false) default false
32701 * @cfg {string} dayPlaceholder
32702 * @cfg {string} monthPlaceholder
32703 * @cfg {string} yearPlaceholder
32704 * @cfg {string} dayFormat default 'd'
32705 * @cfg {string} monthFormat default 'm'
32706 * @cfg {string} yearFormat default 'Y'
32707 * @cfg {Number} labellg set the width of label (1-12)
32708 * @cfg {Number} labelmd set the width of label (1-12)
32709 * @cfg {Number} labelsm set the width of label (1-12)
32710 * @cfg {Number} labelxs set the width of label (1-12)
32714 * Create a new DateSplitField
32715 * @param {Object} config The config object
32718 Roo.bootstrap.DateSplitField = function(config){
32719 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32725 * getting the data of years
32726 * @param {Roo.bootstrap.DateSplitField} this
32727 * @param {Object} years
32732 * getting the data of days
32733 * @param {Roo.bootstrap.DateSplitField} this
32734 * @param {Object} days
32739 * Fires after the field has been marked as invalid.
32740 * @param {Roo.form.Field} this
32741 * @param {String} msg The validation message
32746 * Fires after the field has been validated with no errors.
32747 * @param {Roo.form.Field} this
32753 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32756 labelAlign : 'top',
32758 dayAllowBlank : false,
32759 monthAllowBlank : false,
32760 yearAllowBlank : false,
32761 dayPlaceholder : '',
32762 monthPlaceholder : '',
32763 yearPlaceholder : '',
32767 isFormField : true,
32773 getAutoCreate : function()
32777 cls : 'row roo-date-split-field-group',
32782 cls : 'form-hidden-field roo-date-split-field-group-value',
32788 var labelCls = 'col-md-12';
32789 var contentCls = 'col-md-4';
32791 if(this.fieldLabel){
32795 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32799 html : this.fieldLabel
32804 if(this.labelAlign == 'left'){
32806 if(this.labelWidth > 12){
32807 label.style = "width: " + this.labelWidth + 'px';
32810 if(this.labelWidth < 13 && this.labelmd == 0){
32811 this.labelmd = this.labelWidth;
32814 if(this.labellg > 0){
32815 labelCls = ' col-lg-' + this.labellg;
32816 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32819 if(this.labelmd > 0){
32820 labelCls = ' col-md-' + this.labelmd;
32821 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32824 if(this.labelsm > 0){
32825 labelCls = ' col-sm-' + this.labelsm;
32826 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32829 if(this.labelxs > 0){
32830 labelCls = ' col-xs-' + this.labelxs;
32831 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32835 label.cls += ' ' + labelCls;
32837 cfg.cn.push(label);
32840 Roo.each(['day', 'month', 'year'], function(t){
32843 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32850 inputEl: function ()
32852 return this.el.select('.roo-date-split-field-group-value', true).first();
32855 onRender : function(ct, position)
32859 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32861 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32863 this.dayField = new Roo.bootstrap.ComboBox({
32864 allowBlank : this.dayAllowBlank,
32865 alwaysQuery : true,
32866 displayField : 'value',
32869 forceSelection : true,
32871 placeholder : this.dayPlaceholder,
32872 selectOnFocus : true,
32873 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32874 triggerAction : 'all',
32876 valueField : 'value',
32877 store : new Roo.data.SimpleStore({
32878 data : (function() {
32880 _this.fireEvent('days', _this, days);
32883 fields : [ 'value' ]
32886 select : function (_self, record, index)
32888 _this.setValue(_this.getValue());
32893 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32895 this.monthField = new Roo.bootstrap.MonthField({
32896 after : '<i class=\"fa fa-calendar\"></i>',
32897 allowBlank : this.monthAllowBlank,
32898 placeholder : this.monthPlaceholder,
32901 render : function (_self)
32903 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32904 e.preventDefault();
32908 select : function (_self, oldvalue, newvalue)
32910 _this.setValue(_this.getValue());
32915 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32917 this.yearField = new Roo.bootstrap.ComboBox({
32918 allowBlank : this.yearAllowBlank,
32919 alwaysQuery : true,
32920 displayField : 'value',
32923 forceSelection : true,
32925 placeholder : this.yearPlaceholder,
32926 selectOnFocus : true,
32927 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32928 triggerAction : 'all',
32930 valueField : 'value',
32931 store : new Roo.data.SimpleStore({
32932 data : (function() {
32934 _this.fireEvent('years', _this, years);
32937 fields : [ 'value' ]
32940 select : function (_self, record, index)
32942 _this.setValue(_this.getValue());
32947 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32950 setValue : function(v, format)
32952 this.inputEl.dom.value = v;
32954 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32956 var d = Date.parseDate(v, f);
32963 this.setDay(d.format(this.dayFormat));
32964 this.setMonth(d.format(this.monthFormat));
32965 this.setYear(d.format(this.yearFormat));
32972 setDay : function(v)
32974 this.dayField.setValue(v);
32975 this.inputEl.dom.value = this.getValue();
32980 setMonth : function(v)
32982 this.monthField.setValue(v, true);
32983 this.inputEl.dom.value = this.getValue();
32988 setYear : function(v)
32990 this.yearField.setValue(v);
32991 this.inputEl.dom.value = this.getValue();
32996 getDay : function()
32998 return this.dayField.getValue();
33001 getMonth : function()
33003 return this.monthField.getValue();
33006 getYear : function()
33008 return this.yearField.getValue();
33011 getValue : function()
33013 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33015 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33025 this.inputEl.dom.value = '';
33030 validate : function()
33032 var d = this.dayField.validate();
33033 var m = this.monthField.validate();
33034 var y = this.yearField.validate();
33039 (!this.dayAllowBlank && !d) ||
33040 (!this.monthAllowBlank && !m) ||
33041 (!this.yearAllowBlank && !y)
33046 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33055 this.markInvalid();
33060 markValid : function()
33063 var label = this.el.select('label', true).first();
33064 var icon = this.el.select('i.fa-star', true).first();
33070 this.fireEvent('valid', this);
33074 * Mark this field as invalid
33075 * @param {String} msg The validation message
33077 markInvalid : function(msg)
33080 var label = this.el.select('label', true).first();
33081 var icon = this.el.select('i.fa-star', true).first();
33083 if(label && !icon){
33084 this.el.select('.roo-date-split-field-label', true).createChild({
33086 cls : 'text-danger fa fa-lg fa-star',
33087 tooltip : 'This field is required',
33088 style : 'margin-right:5px;'
33092 this.fireEvent('invalid', this, msg);
33095 clearInvalid : function()
33097 var label = this.el.select('label', true).first();
33098 var icon = this.el.select('i.fa-star', true).first();
33104 this.fireEvent('valid', this);
33107 getName: function()
33117 * http://masonry.desandro.com
33119 * The idea is to render all the bricks based on vertical width...
33121 * The original code extends 'outlayer' - we might need to use that....
33127 * @class Roo.bootstrap.LayoutMasonry
33128 * @extends Roo.bootstrap.Component
33129 * Bootstrap Layout Masonry class
33132 * Create a new Element
33133 * @param {Object} config The config object
33136 Roo.bootstrap.LayoutMasonry = function(config){
33138 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33142 Roo.bootstrap.LayoutMasonry.register(this);
33148 * Fire after layout the items
33149 * @param {Roo.bootstrap.LayoutMasonry} this
33150 * @param {Roo.EventObject} e
33157 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33160 * @cfg {Boolean} isLayoutInstant = no animation?
33162 isLayoutInstant : false, // needed?
33165 * @cfg {Number} boxWidth width of the columns
33170 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33175 * @cfg {Number} padWidth padding below box..
33180 * @cfg {Number} gutter gutter width..
33185 * @cfg {Number} maxCols maximum number of columns
33191 * @cfg {Boolean} isAutoInitial defalut true
33193 isAutoInitial : true,
33198 * @cfg {Boolean} isHorizontal defalut false
33200 isHorizontal : false,
33202 currentSize : null,
33208 bricks: null, //CompositeElement
33212 _isLayoutInited : false,
33214 // isAlternative : false, // only use for vertical layout...
33217 * @cfg {Number} alternativePadWidth padding below box..
33219 alternativePadWidth : 50,
33221 selectedBrick : [],
33223 getAutoCreate : function(){
33225 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33229 cls: 'blog-masonary-wrapper ' + this.cls,
33231 cls : 'mas-boxes masonary'
33238 getChildContainer: function( )
33240 if (this.boxesEl) {
33241 return this.boxesEl;
33244 this.boxesEl = this.el.select('.mas-boxes').first();
33246 return this.boxesEl;
33250 initEvents : function()
33254 if(this.isAutoInitial){
33255 Roo.log('hook children rendered');
33256 this.on('childrenrendered', function() {
33257 Roo.log('children rendered');
33263 initial : function()
33265 this.selectedBrick = [];
33267 this.currentSize = this.el.getBox(true);
33269 Roo.EventManager.onWindowResize(this.resize, this);
33271 if(!this.isAutoInitial){
33279 //this.layout.defer(500,this);
33283 resize : function()
33285 var cs = this.el.getBox(true);
33288 this.currentSize.width == cs.width &&
33289 this.currentSize.x == cs.x &&
33290 this.currentSize.height == cs.height &&
33291 this.currentSize.y == cs.y
33293 Roo.log("no change in with or X or Y");
33297 this.currentSize = cs;
33303 layout : function()
33305 this._resetLayout();
33307 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33309 this.layoutItems( isInstant );
33311 this._isLayoutInited = true;
33313 this.fireEvent('layout', this);
33317 _resetLayout : function()
33319 if(this.isHorizontal){
33320 this.horizontalMeasureColumns();
33324 this.verticalMeasureColumns();
33328 verticalMeasureColumns : function()
33330 this.getContainerWidth();
33332 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33333 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33337 var boxWidth = this.boxWidth + this.padWidth;
33339 if(this.containerWidth < this.boxWidth){
33340 boxWidth = this.containerWidth
33343 var containerWidth = this.containerWidth;
33345 var cols = Math.floor(containerWidth / boxWidth);
33347 this.cols = Math.max( cols, 1 );
33349 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33351 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33353 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33355 this.colWidth = boxWidth + avail - this.padWidth;
33357 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33358 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33361 horizontalMeasureColumns : function()
33363 this.getContainerWidth();
33365 var boxWidth = this.boxWidth;
33367 if(this.containerWidth < boxWidth){
33368 boxWidth = this.containerWidth;
33371 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33373 this.el.setHeight(boxWidth);
33377 getContainerWidth : function()
33379 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33382 layoutItems : function( isInstant )
33384 Roo.log(this.bricks);
33386 var items = Roo.apply([], this.bricks);
33388 if(this.isHorizontal){
33389 this._horizontalLayoutItems( items , isInstant );
33393 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33394 // this._verticalAlternativeLayoutItems( items , isInstant );
33398 this._verticalLayoutItems( items , isInstant );
33402 _verticalLayoutItems : function ( items , isInstant)
33404 if ( !items || !items.length ) {
33409 ['xs', 'xs', 'xs', 'tall'],
33410 ['xs', 'xs', 'tall'],
33411 ['xs', 'xs', 'sm'],
33412 ['xs', 'xs', 'xs'],
33418 ['sm', 'xs', 'xs'],
33422 ['tall', 'xs', 'xs', 'xs'],
33423 ['tall', 'xs', 'xs'],
33435 Roo.each(items, function(item, k){
33437 switch (item.size) {
33438 // these layouts take up a full box,
33449 boxes.push([item]);
33472 var filterPattern = function(box, length)
33480 var pattern = box.slice(0, length);
33484 Roo.each(pattern, function(i){
33485 format.push(i.size);
33488 Roo.each(standard, function(s){
33490 if(String(s) != String(format)){
33499 if(!match && length == 1){
33504 filterPattern(box, length - 1);
33508 queue.push(pattern);
33510 box = box.slice(length, box.length);
33512 filterPattern(box, 4);
33518 Roo.each(boxes, function(box, k){
33524 if(box.length == 1){
33529 filterPattern(box, 4);
33533 this._processVerticalLayoutQueue( queue, isInstant );
33537 // _verticalAlternativeLayoutItems : function( items , isInstant )
33539 // if ( !items || !items.length ) {
33543 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33547 _horizontalLayoutItems : function ( items , isInstant)
33549 if ( !items || !items.length || items.length < 3) {
33555 var eItems = items.slice(0, 3);
33557 items = items.slice(3, items.length);
33560 ['xs', 'xs', 'xs', 'wide'],
33561 ['xs', 'xs', 'wide'],
33562 ['xs', 'xs', 'sm'],
33563 ['xs', 'xs', 'xs'],
33569 ['sm', 'xs', 'xs'],
33573 ['wide', 'xs', 'xs', 'xs'],
33574 ['wide', 'xs', 'xs'],
33587 Roo.each(items, function(item, k){
33589 switch (item.size) {
33600 boxes.push([item]);
33624 var filterPattern = function(box, length)
33632 var pattern = box.slice(0, length);
33636 Roo.each(pattern, function(i){
33637 format.push(i.size);
33640 Roo.each(standard, function(s){
33642 if(String(s) != String(format)){
33651 if(!match && length == 1){
33656 filterPattern(box, length - 1);
33660 queue.push(pattern);
33662 box = box.slice(length, box.length);
33664 filterPattern(box, 4);
33670 Roo.each(boxes, function(box, k){
33676 if(box.length == 1){
33681 filterPattern(box, 4);
33688 var pos = this.el.getBox(true);
33692 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33694 var hit_end = false;
33696 Roo.each(queue, function(box){
33700 Roo.each(box, function(b){
33702 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33712 Roo.each(box, function(b){
33714 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33717 mx = Math.max(mx, b.x);
33721 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33725 Roo.each(box, function(b){
33727 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33741 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33744 /** Sets position of item in DOM
33745 * @param {Element} item
33746 * @param {Number} x - horizontal position
33747 * @param {Number} y - vertical position
33748 * @param {Boolean} isInstant - disables transitions
33750 _processVerticalLayoutQueue : function( queue, isInstant )
33752 var pos = this.el.getBox(true);
33757 for (var i = 0; i < this.cols; i++){
33761 Roo.each(queue, function(box, k){
33763 var col = k % this.cols;
33765 Roo.each(box, function(b,kk){
33767 b.el.position('absolute');
33769 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33770 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33772 if(b.size == 'md-left' || b.size == 'md-right'){
33773 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33774 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33777 b.el.setWidth(width);
33778 b.el.setHeight(height);
33780 b.el.select('iframe',true).setSize(width,height);
33784 for (var i = 0; i < this.cols; i++){
33786 if(maxY[i] < maxY[col]){
33791 col = Math.min(col, i);
33795 x = pos.x + col * (this.colWidth + this.padWidth);
33799 var positions = [];
33801 switch (box.length){
33803 positions = this.getVerticalOneBoxColPositions(x, y, box);
33806 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33809 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33812 positions = this.getVerticalFourBoxColPositions(x, y, box);
33818 Roo.each(box, function(b,kk){
33820 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33822 var sz = b.el.getSize();
33824 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33832 for (var i = 0; i < this.cols; i++){
33833 mY = Math.max(mY, maxY[i]);
33836 this.el.setHeight(mY - pos.y);
33840 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33842 // var pos = this.el.getBox(true);
33845 // var maxX = pos.right;
33847 // var maxHeight = 0;
33849 // Roo.each(items, function(item, k){
33853 // item.el.position('absolute');
33855 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33857 // item.el.setWidth(width);
33859 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33861 // item.el.setHeight(height);
33864 // item.el.setXY([x, y], isInstant ? false : true);
33866 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33869 // y = y + height + this.alternativePadWidth;
33871 // maxHeight = maxHeight + height + this.alternativePadWidth;
33875 // this.el.setHeight(maxHeight);
33879 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33881 var pos = this.el.getBox(true);
33886 var maxX = pos.right;
33888 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33890 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33892 Roo.each(queue, function(box, k){
33894 Roo.each(box, function(b, kk){
33896 b.el.position('absolute');
33898 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33899 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33901 if(b.size == 'md-left' || b.size == 'md-right'){
33902 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33903 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33906 b.el.setWidth(width);
33907 b.el.setHeight(height);
33915 var positions = [];
33917 switch (box.length){
33919 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33922 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33925 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33928 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33934 Roo.each(box, function(b,kk){
33936 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33938 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33946 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33948 Roo.each(eItems, function(b,k){
33950 b.size = (k == 0) ? 'sm' : 'xs';
33951 b.x = (k == 0) ? 2 : 1;
33952 b.y = (k == 0) ? 2 : 1;
33954 b.el.position('absolute');
33956 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33958 b.el.setWidth(width);
33960 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33962 b.el.setHeight(height);
33966 var positions = [];
33969 x : maxX - this.unitWidth * 2 - this.gutter,
33974 x : maxX - this.unitWidth,
33975 y : minY + (this.unitWidth + this.gutter) * 2
33979 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33983 Roo.each(eItems, function(b,k){
33985 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33991 getVerticalOneBoxColPositions : function(x, y, box)
33995 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33997 if(box[0].size == 'md-left'){
34001 if(box[0].size == 'md-right'){
34006 x : x + (this.unitWidth + this.gutter) * rand,
34013 getVerticalTwoBoxColPositions : function(x, y, box)
34017 if(box[0].size == 'xs'){
34021 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34025 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34039 x : x + (this.unitWidth + this.gutter) * 2,
34040 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34047 getVerticalThreeBoxColPositions : function(x, y, box)
34051 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34059 x : x + (this.unitWidth + this.gutter) * 1,
34064 x : x + (this.unitWidth + this.gutter) * 2,
34072 if(box[0].size == 'xs' && box[1].size == 'xs'){
34081 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34085 x : x + (this.unitWidth + this.gutter) * 1,
34099 x : x + (this.unitWidth + this.gutter) * 2,
34104 x : x + (this.unitWidth + this.gutter) * 2,
34105 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34112 getVerticalFourBoxColPositions : function(x, y, box)
34116 if(box[0].size == 'xs'){
34125 y : y + (this.unitHeight + this.gutter) * 1
34130 y : y + (this.unitHeight + this.gutter) * 2
34134 x : x + (this.unitWidth + this.gutter) * 1,
34148 x : x + (this.unitWidth + this.gutter) * 2,
34153 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34154 y : y + (this.unitHeight + this.gutter) * 1
34158 x : x + (this.unitWidth + this.gutter) * 2,
34159 y : y + (this.unitWidth + this.gutter) * 2
34166 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34170 if(box[0].size == 'md-left'){
34172 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34179 if(box[0].size == 'md-right'){
34181 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34182 y : minY + (this.unitWidth + this.gutter) * 1
34188 var rand = Math.floor(Math.random() * (4 - box[0].y));
34191 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34192 y : minY + (this.unitWidth + this.gutter) * rand
34199 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34203 if(box[0].size == 'xs'){
34206 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34211 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34212 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34220 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34225 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34226 y : minY + (this.unitWidth + this.gutter) * 2
34233 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34237 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34240 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34245 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34246 y : minY + (this.unitWidth + this.gutter) * 1
34250 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34251 y : minY + (this.unitWidth + this.gutter) * 2
34258 if(box[0].size == 'xs' && box[1].size == 'xs'){
34261 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34266 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34271 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34272 y : minY + (this.unitWidth + this.gutter) * 1
34280 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34285 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34286 y : minY + (this.unitWidth + this.gutter) * 2
34290 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34291 y : minY + (this.unitWidth + this.gutter) * 2
34298 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34302 if(box[0].size == 'xs'){
34305 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34310 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34315 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),
34320 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34321 y : minY + (this.unitWidth + this.gutter) * 1
34329 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34334 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34335 y : minY + (this.unitWidth + this.gutter) * 2
34339 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34340 y : minY + (this.unitWidth + this.gutter) * 2
34344 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),
34345 y : minY + (this.unitWidth + this.gutter) * 2
34353 * remove a Masonry Brick
34354 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34356 removeBrick : function(brick_id)
34362 for (var i = 0; i<this.bricks.length; i++) {
34363 if (this.bricks[i].id == brick_id) {
34364 this.bricks.splice(i,1);
34365 this.el.dom.removeChild(Roo.get(brick_id).dom);
34372 * adds a Masonry Brick
34373 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34375 addBrick : function(cfg)
34377 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34378 //this.register(cn);
34379 cn.parentId = this.id;
34380 cn.render(this.el);
34385 * register a Masonry Brick
34386 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34389 register : function(brick)
34391 this.bricks.push(brick);
34392 brick.masonryId = this.id;
34396 * clear all the Masonry Brick
34398 clearAll : function()
34401 //this.getChildContainer().dom.innerHTML = "";
34402 this.el.dom.innerHTML = '';
34405 getSelected : function()
34407 if (!this.selectedBrick) {
34411 return this.selectedBrick;
34415 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34419 * register a Masonry Layout
34420 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34423 register : function(layout)
34425 this.groups[layout.id] = layout;
34428 * fetch a Masonry Layout based on the masonry layout ID
34429 * @param {string} the masonry layout to add
34430 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34433 get: function(layout_id) {
34434 if (typeof(this.groups[layout_id]) == 'undefined') {
34437 return this.groups[layout_id] ;
34449 * http://masonry.desandro.com
34451 * The idea is to render all the bricks based on vertical width...
34453 * The original code extends 'outlayer' - we might need to use that....
34459 * @class Roo.bootstrap.LayoutMasonryAuto
34460 * @extends Roo.bootstrap.Component
34461 * Bootstrap Layout Masonry class
34464 * Create a new Element
34465 * @param {Object} config The config object
34468 Roo.bootstrap.LayoutMasonryAuto = function(config){
34469 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34472 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34475 * @cfg {Boolean} isFitWidth - resize the width..
34477 isFitWidth : false, // options..
34479 * @cfg {Boolean} isOriginLeft = left align?
34481 isOriginLeft : true,
34483 * @cfg {Boolean} isOriginTop = top align?
34485 isOriginTop : false,
34487 * @cfg {Boolean} isLayoutInstant = no animation?
34489 isLayoutInstant : false, // needed?
34491 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34493 isResizingContainer : true,
34495 * @cfg {Number} columnWidth width of the columns
34501 * @cfg {Number} maxCols maximum number of columns
34506 * @cfg {Number} padHeight padding below box..
34512 * @cfg {Boolean} isAutoInitial defalut true
34515 isAutoInitial : true,
34521 initialColumnWidth : 0,
34522 currentSize : null,
34524 colYs : null, // array.
34531 bricks: null, //CompositeElement
34532 cols : 0, // array?
34533 // element : null, // wrapped now this.el
34534 _isLayoutInited : null,
34537 getAutoCreate : function(){
34541 cls: 'blog-masonary-wrapper ' + this.cls,
34543 cls : 'mas-boxes masonary'
34550 getChildContainer: function( )
34552 if (this.boxesEl) {
34553 return this.boxesEl;
34556 this.boxesEl = this.el.select('.mas-boxes').first();
34558 return this.boxesEl;
34562 initEvents : function()
34566 if(this.isAutoInitial){
34567 Roo.log('hook children rendered');
34568 this.on('childrenrendered', function() {
34569 Roo.log('children rendered');
34576 initial : function()
34578 this.reloadItems();
34580 this.currentSize = this.el.getBox(true);
34582 /// was window resize... - let's see if this works..
34583 Roo.EventManager.onWindowResize(this.resize, this);
34585 if(!this.isAutoInitial){
34590 this.layout.defer(500,this);
34593 reloadItems: function()
34595 this.bricks = this.el.select('.masonry-brick', true);
34597 this.bricks.each(function(b) {
34598 //Roo.log(b.getSize());
34599 if (!b.attr('originalwidth')) {
34600 b.attr('originalwidth', b.getSize().width);
34605 Roo.log(this.bricks.elements.length);
34608 resize : function()
34611 var cs = this.el.getBox(true);
34613 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34614 Roo.log("no change in with or X");
34617 this.currentSize = cs;
34621 layout : function()
34624 this._resetLayout();
34625 //this._manageStamps();
34627 // don't animate first layout
34628 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34629 this.layoutItems( isInstant );
34631 // flag for initalized
34632 this._isLayoutInited = true;
34635 layoutItems : function( isInstant )
34637 //var items = this._getItemsForLayout( this.items );
34638 // original code supports filtering layout items.. we just ignore it..
34640 this._layoutItems( this.bricks , isInstant );
34642 this._postLayout();
34644 _layoutItems : function ( items , isInstant)
34646 //this.fireEvent( 'layout', this, items );
34649 if ( !items || !items.elements.length ) {
34650 // no items, emit event with empty array
34655 items.each(function(item) {
34656 Roo.log("layout item");
34658 // get x/y object from method
34659 var position = this._getItemLayoutPosition( item );
34661 position.item = item;
34662 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34663 queue.push( position );
34666 this._processLayoutQueue( queue );
34668 /** Sets position of item in DOM
34669 * @param {Element} item
34670 * @param {Number} x - horizontal position
34671 * @param {Number} y - vertical position
34672 * @param {Boolean} isInstant - disables transitions
34674 _processLayoutQueue : function( queue )
34676 for ( var i=0, len = queue.length; i < len; i++ ) {
34677 var obj = queue[i];
34678 obj.item.position('absolute');
34679 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34685 * Any logic you want to do after each layout,
34686 * i.e. size the container
34688 _postLayout : function()
34690 this.resizeContainer();
34693 resizeContainer : function()
34695 if ( !this.isResizingContainer ) {
34698 var size = this._getContainerSize();
34700 this.el.setSize(size.width,size.height);
34701 this.boxesEl.setSize(size.width,size.height);
34707 _resetLayout : function()
34709 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34710 this.colWidth = this.el.getWidth();
34711 //this.gutter = this.el.getWidth();
34713 this.measureColumns();
34719 this.colYs.push( 0 );
34725 measureColumns : function()
34727 this.getContainerWidth();
34728 // if columnWidth is 0, default to outerWidth of first item
34729 if ( !this.columnWidth ) {
34730 var firstItem = this.bricks.first();
34731 Roo.log(firstItem);
34732 this.columnWidth = this.containerWidth;
34733 if (firstItem && firstItem.attr('originalwidth') ) {
34734 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34736 // columnWidth fall back to item of first element
34737 Roo.log("set column width?");
34738 this.initialColumnWidth = this.columnWidth ;
34740 // if first elem has no width, default to size of container
34745 if (this.initialColumnWidth) {
34746 this.columnWidth = this.initialColumnWidth;
34751 // column width is fixed at the top - however if container width get's smaller we should
34754 // this bit calcs how man columns..
34756 var columnWidth = this.columnWidth += this.gutter;
34758 // calculate columns
34759 var containerWidth = this.containerWidth + this.gutter;
34761 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34762 // fix rounding errors, typically with gutters
34763 var excess = columnWidth - containerWidth % columnWidth;
34766 // if overshoot is less than a pixel, round up, otherwise floor it
34767 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34768 cols = Math[ mathMethod ]( cols );
34769 this.cols = Math.max( cols, 1 );
34770 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34772 // padding positioning..
34773 var totalColWidth = this.cols * this.columnWidth;
34774 var padavail = this.containerWidth - totalColWidth;
34775 // so for 2 columns - we need 3 'pads'
34777 var padNeeded = (1+this.cols) * this.padWidth;
34779 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34781 this.columnWidth += padExtra
34782 //this.padWidth = Math.floor(padavail / ( this.cols));
34784 // adjust colum width so that padding is fixed??
34786 // we have 3 columns ... total = width * 3
34787 // we have X left over... that should be used by
34789 //if (this.expandC) {
34797 getContainerWidth : function()
34799 /* // container is parent if fit width
34800 var container = this.isFitWidth ? this.element.parentNode : this.element;
34801 // check that this.size and size are there
34802 // IE8 triggers resize on body size change, so they might not be
34804 var size = getSize( container ); //FIXME
34805 this.containerWidth = size && size.innerWidth; //FIXME
34808 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34812 _getItemLayoutPosition : function( item ) // what is item?
34814 // we resize the item to our columnWidth..
34816 item.setWidth(this.columnWidth);
34817 item.autoBoxAdjust = false;
34819 var sz = item.getSize();
34821 // how many columns does this brick span
34822 var remainder = this.containerWidth % this.columnWidth;
34824 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34825 // round if off by 1 pixel, otherwise use ceil
34826 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34827 colSpan = Math.min( colSpan, this.cols );
34829 // normally this should be '1' as we dont' currently allow multi width columns..
34831 var colGroup = this._getColGroup( colSpan );
34832 // get the minimum Y value from the columns
34833 var minimumY = Math.min.apply( Math, colGroup );
34834 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34836 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34838 // position the brick
34840 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34841 y: this.currentSize.y + minimumY + this.padHeight
34845 // apply setHeight to necessary columns
34846 var setHeight = minimumY + sz.height + this.padHeight;
34847 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34849 var setSpan = this.cols + 1 - colGroup.length;
34850 for ( var i = 0; i < setSpan; i++ ) {
34851 this.colYs[ shortColIndex + i ] = setHeight ;
34858 * @param {Number} colSpan - number of columns the element spans
34859 * @returns {Array} colGroup
34861 _getColGroup : function( colSpan )
34863 if ( colSpan < 2 ) {
34864 // if brick spans only one column, use all the column Ys
34869 // how many different places could this brick fit horizontally
34870 var groupCount = this.cols + 1 - colSpan;
34871 // for each group potential horizontal position
34872 for ( var i = 0; i < groupCount; i++ ) {
34873 // make an array of colY values for that one group
34874 var groupColYs = this.colYs.slice( i, i + colSpan );
34875 // and get the max value of the array
34876 colGroup[i] = Math.max.apply( Math, groupColYs );
34881 _manageStamp : function( stamp )
34883 var stampSize = stamp.getSize();
34884 var offset = stamp.getBox();
34885 // get the columns that this stamp affects
34886 var firstX = this.isOriginLeft ? offset.x : offset.right;
34887 var lastX = firstX + stampSize.width;
34888 var firstCol = Math.floor( firstX / this.columnWidth );
34889 firstCol = Math.max( 0, firstCol );
34891 var lastCol = Math.floor( lastX / this.columnWidth );
34892 // lastCol should not go over if multiple of columnWidth #425
34893 lastCol -= lastX % this.columnWidth ? 0 : 1;
34894 lastCol = Math.min( this.cols - 1, lastCol );
34896 // set colYs to bottom of the stamp
34897 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34900 for ( var i = firstCol; i <= lastCol; i++ ) {
34901 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34906 _getContainerSize : function()
34908 this.maxY = Math.max.apply( Math, this.colYs );
34913 if ( this.isFitWidth ) {
34914 size.width = this._getContainerFitWidth();
34920 _getContainerFitWidth : function()
34922 var unusedCols = 0;
34923 // count unused columns
34926 if ( this.colYs[i] !== 0 ) {
34931 // fit container to columns that have been used
34932 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34935 needsResizeLayout : function()
34937 var previousWidth = this.containerWidth;
34938 this.getContainerWidth();
34939 return previousWidth !== this.containerWidth;
34954 * @class Roo.bootstrap.MasonryBrick
34955 * @extends Roo.bootstrap.Component
34956 * Bootstrap MasonryBrick class
34959 * Create a new MasonryBrick
34960 * @param {Object} config The config object
34963 Roo.bootstrap.MasonryBrick = function(config){
34965 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34967 Roo.bootstrap.MasonryBrick.register(this);
34973 * When a MasonryBrick is clcik
34974 * @param {Roo.bootstrap.MasonryBrick} this
34975 * @param {Roo.EventObject} e
34981 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34984 * @cfg {String} title
34988 * @cfg {String} html
34992 * @cfg {String} bgimage
34996 * @cfg {String} videourl
35000 * @cfg {String} cls
35004 * @cfg {String} href
35008 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35013 * @cfg {String} placetitle (center|bottom)
35018 * @cfg {Boolean} isFitContainer defalut true
35020 isFitContainer : true,
35023 * @cfg {Boolean} preventDefault defalut false
35025 preventDefault : false,
35028 * @cfg {Boolean} inverse defalut false
35030 maskInverse : false,
35032 getAutoCreate : function()
35034 if(!this.isFitContainer){
35035 return this.getSplitAutoCreate();
35038 var cls = 'masonry-brick masonry-brick-full';
35040 if(this.href.length){
35041 cls += ' masonry-brick-link';
35044 if(this.bgimage.length){
35045 cls += ' masonry-brick-image';
35048 if(this.maskInverse){
35049 cls += ' mask-inverse';
35052 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35053 cls += ' enable-mask';
35057 cls += ' masonry-' + this.size + '-brick';
35060 if(this.placetitle.length){
35062 switch (this.placetitle) {
35064 cls += ' masonry-center-title';
35067 cls += ' masonry-bottom-title';
35074 if(!this.html.length && !this.bgimage.length){
35075 cls += ' masonry-center-title';
35078 if(!this.html.length && this.bgimage.length){
35079 cls += ' masonry-bottom-title';
35084 cls += ' ' + this.cls;
35088 tag: (this.href.length) ? 'a' : 'div',
35093 cls: 'masonry-brick-mask'
35097 cls: 'masonry-brick-paragraph',
35103 if(this.href.length){
35104 cfg.href = this.href;
35107 var cn = cfg.cn[1].cn;
35109 if(this.title.length){
35112 cls: 'masonry-brick-title',
35117 if(this.html.length){
35120 cls: 'masonry-brick-text',
35125 if (!this.title.length && !this.html.length) {
35126 cfg.cn[1].cls += ' hide';
35129 if(this.bgimage.length){
35132 cls: 'masonry-brick-image-view',
35137 if(this.videourl.length){
35138 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35139 // youtube support only?
35142 cls: 'masonry-brick-image-view',
35145 allowfullscreen : true
35153 getSplitAutoCreate : function()
35155 var cls = 'masonry-brick masonry-brick-split';
35157 if(this.href.length){
35158 cls += ' masonry-brick-link';
35161 if(this.bgimage.length){
35162 cls += ' masonry-brick-image';
35166 cls += ' masonry-' + this.size + '-brick';
35169 switch (this.placetitle) {
35171 cls += ' masonry-center-title';
35174 cls += ' masonry-bottom-title';
35177 if(!this.bgimage.length){
35178 cls += ' masonry-center-title';
35181 if(this.bgimage.length){
35182 cls += ' masonry-bottom-title';
35188 cls += ' ' + this.cls;
35192 tag: (this.href.length) ? 'a' : 'div',
35197 cls: 'masonry-brick-split-head',
35201 cls: 'masonry-brick-paragraph',
35208 cls: 'masonry-brick-split-body',
35214 if(this.href.length){
35215 cfg.href = this.href;
35218 if(this.title.length){
35219 cfg.cn[0].cn[0].cn.push({
35221 cls: 'masonry-brick-title',
35226 if(this.html.length){
35227 cfg.cn[1].cn.push({
35229 cls: 'masonry-brick-text',
35234 if(this.bgimage.length){
35235 cfg.cn[0].cn.push({
35237 cls: 'masonry-brick-image-view',
35242 if(this.videourl.length){
35243 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35244 // youtube support only?
35245 cfg.cn[0].cn.cn.push({
35247 cls: 'masonry-brick-image-view',
35250 allowfullscreen : true
35257 initEvents: function()
35259 switch (this.size) {
35292 this.el.on('touchstart', this.onTouchStart, this);
35293 this.el.on('touchmove', this.onTouchMove, this);
35294 this.el.on('touchend', this.onTouchEnd, this);
35295 this.el.on('contextmenu', this.onContextMenu, this);
35297 this.el.on('mouseenter' ,this.enter, this);
35298 this.el.on('mouseleave', this.leave, this);
35299 this.el.on('click', this.onClick, this);
35302 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35303 this.parent().bricks.push(this);
35308 onClick: function(e, el)
35310 var time = this.endTimer - this.startTimer;
35311 // Roo.log(e.preventDefault());
35314 e.preventDefault();
35319 if(!this.preventDefault){
35323 e.preventDefault();
35325 if (this.activeClass != '') {
35326 this.selectBrick();
35329 this.fireEvent('click', this, e);
35332 enter: function(e, el)
35334 e.preventDefault();
35336 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35340 if(this.bgimage.length && this.html.length){
35341 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35345 leave: function(e, el)
35347 e.preventDefault();
35349 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35353 if(this.bgimage.length && this.html.length){
35354 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35358 onTouchStart: function(e, el)
35360 // e.preventDefault();
35362 this.touchmoved = false;
35364 if(!this.isFitContainer){
35368 if(!this.bgimage.length || !this.html.length){
35372 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35374 this.timer = new Date().getTime();
35378 onTouchMove: function(e, el)
35380 this.touchmoved = true;
35383 onContextMenu : function(e,el)
35385 e.preventDefault();
35386 e.stopPropagation();
35390 onTouchEnd: function(e, el)
35392 // e.preventDefault();
35394 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35401 if(!this.bgimage.length || !this.html.length){
35403 if(this.href.length){
35404 window.location.href = this.href;
35410 if(!this.isFitContainer){
35414 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35416 window.location.href = this.href;
35419 //selection on single brick only
35420 selectBrick : function() {
35422 if (!this.parentId) {
35426 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35427 var index = m.selectedBrick.indexOf(this.id);
35430 m.selectedBrick.splice(index,1);
35431 this.el.removeClass(this.activeClass);
35435 for(var i = 0; i < m.selectedBrick.length; i++) {
35436 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35437 b.el.removeClass(b.activeClass);
35440 m.selectedBrick = [];
35442 m.selectedBrick.push(this.id);
35443 this.el.addClass(this.activeClass);
35447 isSelected : function(){
35448 return this.el.hasClass(this.activeClass);
35453 Roo.apply(Roo.bootstrap.MasonryBrick, {
35456 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35458 * register a Masonry Brick
35459 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35462 register : function(brick)
35464 //this.groups[brick.id] = brick;
35465 this.groups.add(brick.id, brick);
35468 * fetch a masonry brick based on the masonry brick ID
35469 * @param {string} the masonry brick to add
35470 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35473 get: function(brick_id)
35475 // if (typeof(this.groups[brick_id]) == 'undefined') {
35478 // return this.groups[brick_id] ;
35480 if(this.groups.key(brick_id)) {
35481 return this.groups.key(brick_id);
35499 * @class Roo.bootstrap.Brick
35500 * @extends Roo.bootstrap.Component
35501 * Bootstrap Brick class
35504 * Create a new Brick
35505 * @param {Object} config The config object
35508 Roo.bootstrap.Brick = function(config){
35509 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35515 * When a Brick is click
35516 * @param {Roo.bootstrap.Brick} this
35517 * @param {Roo.EventObject} e
35523 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35526 * @cfg {String} title
35530 * @cfg {String} html
35534 * @cfg {String} bgimage
35538 * @cfg {String} cls
35542 * @cfg {String} href
35546 * @cfg {String} video
35550 * @cfg {Boolean} square
35554 getAutoCreate : function()
35556 var cls = 'roo-brick';
35558 if(this.href.length){
35559 cls += ' roo-brick-link';
35562 if(this.bgimage.length){
35563 cls += ' roo-brick-image';
35566 if(!this.html.length && !this.bgimage.length){
35567 cls += ' roo-brick-center-title';
35570 if(!this.html.length && this.bgimage.length){
35571 cls += ' roo-brick-bottom-title';
35575 cls += ' ' + this.cls;
35579 tag: (this.href.length) ? 'a' : 'div',
35584 cls: 'roo-brick-paragraph',
35590 if(this.href.length){
35591 cfg.href = this.href;
35594 var cn = cfg.cn[0].cn;
35596 if(this.title.length){
35599 cls: 'roo-brick-title',
35604 if(this.html.length){
35607 cls: 'roo-brick-text',
35614 if(this.bgimage.length){
35617 cls: 'roo-brick-image-view',
35625 initEvents: function()
35627 if(this.title.length || this.html.length){
35628 this.el.on('mouseenter' ,this.enter, this);
35629 this.el.on('mouseleave', this.leave, this);
35632 Roo.EventManager.onWindowResize(this.resize, this);
35634 if(this.bgimage.length){
35635 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35636 this.imageEl.on('load', this.onImageLoad, this);
35643 onImageLoad : function()
35648 resize : function()
35650 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35652 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35654 if(this.bgimage.length){
35655 var image = this.el.select('.roo-brick-image-view', true).first();
35657 image.setWidth(paragraph.getWidth());
35660 image.setHeight(paragraph.getWidth());
35663 this.el.setHeight(image.getHeight());
35664 paragraph.setHeight(image.getHeight());
35670 enter: function(e, el)
35672 e.preventDefault();
35674 if(this.bgimage.length){
35675 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35676 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35680 leave: function(e, el)
35682 e.preventDefault();
35684 if(this.bgimage.length){
35685 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35686 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35701 * @class Roo.bootstrap.NumberField
35702 * @extends Roo.bootstrap.Input
35703 * Bootstrap NumberField class
35709 * Create a new NumberField
35710 * @param {Object} config The config object
35713 Roo.bootstrap.NumberField = function(config){
35714 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35717 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35720 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35722 allowDecimals : true,
35724 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35726 decimalSeparator : ".",
35728 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35730 decimalPrecision : 2,
35732 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35734 allowNegative : true,
35737 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35741 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35743 minValue : Number.NEGATIVE_INFINITY,
35745 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35747 maxValue : Number.MAX_VALUE,
35749 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35751 minText : "The minimum value for this field is {0}",
35753 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35755 maxText : "The maximum value for this field is {0}",
35757 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35758 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35760 nanText : "{0} is not a valid number",
35762 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35764 thousandsDelimiter : false,
35766 * @cfg {String} valueAlign alignment of value
35768 valueAlign : "left",
35770 getAutoCreate : function()
35772 var hiddenInput = {
35776 cls: 'hidden-number-input'
35780 hiddenInput.name = this.name;
35785 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35787 this.name = hiddenInput.name;
35789 if(cfg.cn.length > 0) {
35790 cfg.cn.push(hiddenInput);
35797 initEvents : function()
35799 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35801 var allowed = "0123456789";
35803 if(this.allowDecimals){
35804 allowed += this.decimalSeparator;
35807 if(this.allowNegative){
35811 if(this.thousandsDelimiter) {
35815 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35817 var keyPress = function(e){
35819 var k = e.getKey();
35821 var c = e.getCharCode();
35824 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35825 allowed.indexOf(String.fromCharCode(c)) === -1
35831 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35835 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35840 this.el.on("keypress", keyPress, this);
35843 validateValue : function(value)
35846 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35850 var num = this.parseValue(value);
35853 this.markInvalid(String.format(this.nanText, value));
35857 if(num < this.minValue){
35858 this.markInvalid(String.format(this.minText, this.minValue));
35862 if(num > this.maxValue){
35863 this.markInvalid(String.format(this.maxText, this.maxValue));
35870 getValue : function()
35872 var v = this.hiddenEl().getValue();
35874 return this.fixPrecision(this.parseValue(v));
35877 parseValue : function(value)
35879 if(this.thousandsDelimiter) {
35881 r = new RegExp(",", "g");
35882 value = value.replace(r, "");
35885 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35886 return isNaN(value) ? '' : value;
35889 fixPrecision : function(value)
35891 if(this.thousandsDelimiter) {
35893 r = new RegExp(",", "g");
35894 value = value.replace(r, "");
35897 var nan = isNaN(value);
35899 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35900 return nan ? '' : value;
35902 return parseFloat(value).toFixed(this.decimalPrecision);
35905 setValue : function(v)
35907 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35913 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35915 this.inputEl().dom.value = (v == '') ? '' :
35916 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35918 if(!this.allowZero && v === '0') {
35919 this.hiddenEl().dom.value = '';
35920 this.inputEl().dom.value = '';
35927 decimalPrecisionFcn : function(v)
35929 return Math.floor(v);
35932 beforeBlur : function()
35934 var v = this.parseValue(this.getRawValue());
35936 if(v || v === 0 || v === ''){
35941 hiddenEl : function()
35943 return this.el.select('input.hidden-number-input',true).first();
35955 * @class Roo.bootstrap.DocumentSlider
35956 * @extends Roo.bootstrap.Component
35957 * Bootstrap DocumentSlider class
35960 * Create a new DocumentViewer
35961 * @param {Object} config The config object
35964 Roo.bootstrap.DocumentSlider = function(config){
35965 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35972 * Fire after initEvent
35973 * @param {Roo.bootstrap.DocumentSlider} this
35978 * Fire after update
35979 * @param {Roo.bootstrap.DocumentSlider} this
35985 * @param {Roo.bootstrap.DocumentSlider} this
35991 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35997 getAutoCreate : function()
36001 cls : 'roo-document-slider',
36005 cls : 'roo-document-slider-header',
36009 cls : 'roo-document-slider-header-title'
36015 cls : 'roo-document-slider-body',
36019 cls : 'roo-document-slider-prev',
36023 cls : 'fa fa-chevron-left'
36029 cls : 'roo-document-slider-thumb',
36033 cls : 'roo-document-slider-image'
36039 cls : 'roo-document-slider-next',
36043 cls : 'fa fa-chevron-right'
36055 initEvents : function()
36057 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36058 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36060 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36061 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36063 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36064 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36066 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36067 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36069 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36070 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36072 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36073 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36075 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36076 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36078 this.thumbEl.on('click', this.onClick, this);
36080 this.prevIndicator.on('click', this.prev, this);
36082 this.nextIndicator.on('click', this.next, this);
36086 initial : function()
36088 if(this.files.length){
36089 this.indicator = 1;
36093 this.fireEvent('initial', this);
36096 update : function()
36098 this.imageEl.attr('src', this.files[this.indicator - 1]);
36100 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36102 this.prevIndicator.show();
36104 if(this.indicator == 1){
36105 this.prevIndicator.hide();
36108 this.nextIndicator.show();
36110 if(this.indicator == this.files.length){
36111 this.nextIndicator.hide();
36114 this.thumbEl.scrollTo('top');
36116 this.fireEvent('update', this);
36119 onClick : function(e)
36121 e.preventDefault();
36123 this.fireEvent('click', this);
36128 e.preventDefault();
36130 this.indicator = Math.max(1, this.indicator - 1);
36137 e.preventDefault();
36139 this.indicator = Math.min(this.files.length, this.indicator + 1);
36153 * @class Roo.bootstrap.RadioSet
36154 * @extends Roo.bootstrap.Input
36155 * Bootstrap RadioSet class
36156 * @cfg {String} indicatorpos (left|right) default left
36157 * @cfg {Boolean} inline (true|false) inline the element (default true)
36158 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36160 * Create a new RadioSet
36161 * @param {Object} config The config object
36164 Roo.bootstrap.RadioSet = function(config){
36166 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36170 Roo.bootstrap.RadioSet.register(this);
36175 * Fires when the element is checked or unchecked.
36176 * @param {Roo.bootstrap.RadioSet} this This radio
36177 * @param {Roo.bootstrap.Radio} item The checked item
36182 * Fires when the element is click.
36183 * @param {Roo.bootstrap.RadioSet} this This radio set
36184 * @param {Roo.bootstrap.Radio} item The checked item
36185 * @param {Roo.EventObject} e The event object
36192 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36200 indicatorpos : 'left',
36202 getAutoCreate : function()
36206 cls : 'roo-radio-set-label',
36210 html : this.fieldLabel
36214 if (Roo.bootstrap.version == 3) {
36217 if(this.indicatorpos == 'left'){
36220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36221 tooltip : 'This field is required'
36226 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36227 tooltip : 'This field is required'
36233 cls : 'roo-radio-set-items'
36236 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36238 if (align === 'left' && this.fieldLabel.length) {
36241 cls : "roo-radio-set-right",
36247 if(this.labelWidth > 12){
36248 label.style = "width: " + this.labelWidth + 'px';
36251 if(this.labelWidth < 13 && this.labelmd == 0){
36252 this.labelmd = this.labelWidth;
36255 if(this.labellg > 0){
36256 label.cls += ' col-lg-' + this.labellg;
36257 items.cls += ' col-lg-' + (12 - this.labellg);
36260 if(this.labelmd > 0){
36261 label.cls += ' col-md-' + this.labelmd;
36262 items.cls += ' col-md-' + (12 - this.labelmd);
36265 if(this.labelsm > 0){
36266 label.cls += ' col-sm-' + this.labelsm;
36267 items.cls += ' col-sm-' + (12 - this.labelsm);
36270 if(this.labelxs > 0){
36271 label.cls += ' col-xs-' + this.labelxs;
36272 items.cls += ' col-xs-' + (12 - this.labelxs);
36278 cls : 'roo-radio-set',
36282 cls : 'roo-radio-set-input',
36285 value : this.value ? this.value : ''
36292 if(this.weight.length){
36293 cfg.cls += ' roo-radio-' + this.weight;
36297 cfg.cls += ' roo-radio-set-inline';
36301 ['xs','sm','md','lg'].map(function(size){
36302 if (settings[size]) {
36303 cfg.cls += ' col-' + size + '-' + settings[size];
36311 initEvents : function()
36313 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36314 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36316 if(!this.fieldLabel.length){
36317 this.labelEl.hide();
36320 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36321 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36323 this.indicator = this.indicatorEl();
36325 if(this.indicator){
36326 this.indicator.addClass('invisible');
36329 this.originalValue = this.getValue();
36333 inputEl: function ()
36335 return this.el.select('.roo-radio-set-input', true).first();
36338 getChildContainer : function()
36340 return this.itemsEl;
36343 register : function(item)
36345 this.radioes.push(item);
36349 validate : function()
36351 if(this.getVisibilityEl().hasClass('hidden')){
36357 Roo.each(this.radioes, function(i){
36366 if(this.allowBlank) {
36370 if(this.disabled || valid){
36375 this.markInvalid();
36380 markValid : function()
36382 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36383 this.indicatorEl().removeClass('visible');
36384 this.indicatorEl().addClass('invisible');
36388 if (Roo.bootstrap.version == 3) {
36389 this.el.removeClass([this.invalidClass, this.validClass]);
36390 this.el.addClass(this.validClass);
36392 this.el.removeClass(['is-invalid','is-valid']);
36393 this.el.addClass(['is-valid']);
36395 this.fireEvent('valid', this);
36398 markInvalid : function(msg)
36400 if(this.allowBlank || this.disabled){
36404 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36405 this.indicatorEl().removeClass('invisible');
36406 this.indicatorEl().addClass('visible');
36408 if (Roo.bootstrap.version == 3) {
36409 this.el.removeClass([this.invalidClass, this.validClass]);
36410 this.el.addClass(this.invalidClass);
36412 this.el.removeClass(['is-invalid','is-valid']);
36413 this.el.addClass(['is-invalid']);
36416 this.fireEvent('invalid', this, msg);
36420 setValue : function(v, suppressEvent)
36422 if(this.value === v){
36429 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36432 Roo.each(this.radioes, function(i){
36434 i.el.removeClass('checked');
36437 Roo.each(this.radioes, function(i){
36439 if(i.value === v || i.value.toString() === v.toString()){
36441 i.el.addClass('checked');
36443 if(suppressEvent !== true){
36444 this.fireEvent('check', this, i);
36455 clearInvalid : function(){
36457 if(!this.el || this.preventMark){
36461 this.el.removeClass([this.invalidClass]);
36463 this.fireEvent('valid', this);
36468 Roo.apply(Roo.bootstrap.RadioSet, {
36472 register : function(set)
36474 this.groups[set.name] = set;
36477 get: function(name)
36479 if (typeof(this.groups[name]) == 'undefined') {
36483 return this.groups[name] ;
36489 * Ext JS Library 1.1.1
36490 * Copyright(c) 2006-2007, Ext JS, LLC.
36492 * Originally Released Under LGPL - original licence link has changed is not relivant.
36495 * <script type="text/javascript">
36500 * @class Roo.bootstrap.SplitBar
36501 * @extends Roo.util.Observable
36502 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36506 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36507 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36508 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36509 split.minSize = 100;
36510 split.maxSize = 600;
36511 split.animate = true;
36512 split.on('moved', splitterMoved);
36515 * Create a new SplitBar
36516 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36517 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36518 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36519 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36520 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36521 position of the SplitBar).
36523 Roo.bootstrap.SplitBar = function(cfg){
36528 // dragElement : elm
36529 // resizingElement: el,
36531 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36532 // placement : Roo.bootstrap.SplitBar.LEFT ,
36533 // existingProxy ???
36536 this.el = Roo.get(cfg.dragElement, true);
36537 this.el.dom.unselectable = "on";
36539 this.resizingEl = Roo.get(cfg.resizingElement, true);
36543 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36544 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36547 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36550 * The minimum size of the resizing element. (Defaults to 0)
36556 * The maximum size of the resizing element. (Defaults to 2000)
36559 this.maxSize = 2000;
36562 * Whether to animate the transition to the new size
36565 this.animate = false;
36568 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36571 this.useShim = false;
36576 if(!cfg.existingProxy){
36578 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36580 this.proxy = Roo.get(cfg.existingProxy).dom;
36583 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36586 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36589 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36592 this.dragSpecs = {};
36595 * @private The adapter to use to positon and resize elements
36597 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36598 this.adapter.init(this);
36600 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36602 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36603 this.el.addClass("roo-splitbar-h");
36606 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36607 this.el.addClass("roo-splitbar-v");
36613 * Fires when the splitter is moved (alias for {@link #event-moved})
36614 * @param {Roo.bootstrap.SplitBar} this
36615 * @param {Number} newSize the new width or height
36620 * Fires when the splitter is moved
36621 * @param {Roo.bootstrap.SplitBar} this
36622 * @param {Number} newSize the new width or height
36626 * @event beforeresize
36627 * Fires before the splitter is dragged
36628 * @param {Roo.bootstrap.SplitBar} this
36630 "beforeresize" : true,
36632 "beforeapply" : true
36635 Roo.util.Observable.call(this);
36638 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36639 onStartProxyDrag : function(x, y){
36640 this.fireEvent("beforeresize", this);
36642 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36644 o.enableDisplayMode("block");
36645 // all splitbars share the same overlay
36646 Roo.bootstrap.SplitBar.prototype.overlay = o;
36648 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36649 this.overlay.show();
36650 Roo.get(this.proxy).setDisplayed("block");
36651 var size = this.adapter.getElementSize(this);
36652 this.activeMinSize = this.getMinimumSize();;
36653 this.activeMaxSize = this.getMaximumSize();;
36654 var c1 = size - this.activeMinSize;
36655 var c2 = Math.max(this.activeMaxSize - size, 0);
36656 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36657 this.dd.resetConstraints();
36658 this.dd.setXConstraint(
36659 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36660 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36662 this.dd.setYConstraint(0, 0);
36664 this.dd.resetConstraints();
36665 this.dd.setXConstraint(0, 0);
36666 this.dd.setYConstraint(
36667 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36668 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36671 this.dragSpecs.startSize = size;
36672 this.dragSpecs.startPoint = [x, y];
36673 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36677 * @private Called after the drag operation by the DDProxy
36679 onEndProxyDrag : function(e){
36680 Roo.get(this.proxy).setDisplayed(false);
36681 var endPoint = Roo.lib.Event.getXY(e);
36683 this.overlay.hide();
36686 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36687 newSize = this.dragSpecs.startSize +
36688 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36689 endPoint[0] - this.dragSpecs.startPoint[0] :
36690 this.dragSpecs.startPoint[0] - endPoint[0]
36693 newSize = this.dragSpecs.startSize +
36694 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36695 endPoint[1] - this.dragSpecs.startPoint[1] :
36696 this.dragSpecs.startPoint[1] - endPoint[1]
36699 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36700 if(newSize != this.dragSpecs.startSize){
36701 if(this.fireEvent('beforeapply', this, newSize) !== false){
36702 this.adapter.setElementSize(this, newSize);
36703 this.fireEvent("moved", this, newSize);
36704 this.fireEvent("resize", this, newSize);
36710 * Get the adapter this SplitBar uses
36711 * @return The adapter object
36713 getAdapter : function(){
36714 return this.adapter;
36718 * Set the adapter this SplitBar uses
36719 * @param {Object} adapter A SplitBar adapter object
36721 setAdapter : function(adapter){
36722 this.adapter = adapter;
36723 this.adapter.init(this);
36727 * Gets the minimum size for the resizing element
36728 * @return {Number} The minimum size
36730 getMinimumSize : function(){
36731 return this.minSize;
36735 * Sets the minimum size for the resizing element
36736 * @param {Number} minSize The minimum size
36738 setMinimumSize : function(minSize){
36739 this.minSize = minSize;
36743 * Gets the maximum size for the resizing element
36744 * @return {Number} The maximum size
36746 getMaximumSize : function(){
36747 return this.maxSize;
36751 * Sets the maximum size for the resizing element
36752 * @param {Number} maxSize The maximum size
36754 setMaximumSize : function(maxSize){
36755 this.maxSize = maxSize;
36759 * Sets the initialize size for the resizing element
36760 * @param {Number} size The initial size
36762 setCurrentSize : function(size){
36763 var oldAnimate = this.animate;
36764 this.animate = false;
36765 this.adapter.setElementSize(this, size);
36766 this.animate = oldAnimate;
36770 * Destroy this splitbar.
36771 * @param {Boolean} removeEl True to remove the element
36773 destroy : function(removeEl){
36775 this.shim.remove();
36778 this.proxy.parentNode.removeChild(this.proxy);
36786 * @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.
36788 Roo.bootstrap.SplitBar.createProxy = function(dir){
36789 var proxy = new Roo.Element(document.createElement("div"));
36790 proxy.unselectable();
36791 var cls = 'roo-splitbar-proxy';
36792 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36793 document.body.appendChild(proxy.dom);
36798 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36799 * Default Adapter. It assumes the splitter and resizing element are not positioned
36800 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36802 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36805 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36806 // do nothing for now
36807 init : function(s){
36811 * Called before drag operations to get the current size of the resizing element.
36812 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36814 getElementSize : function(s){
36815 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36816 return s.resizingEl.getWidth();
36818 return s.resizingEl.getHeight();
36823 * Called after drag operations to set the size of the resizing element.
36824 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36825 * @param {Number} newSize The new size to set
36826 * @param {Function} onComplete A function to be invoked when resizing is complete
36828 setElementSize : function(s, newSize, onComplete){
36829 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36831 s.resizingEl.setWidth(newSize);
36833 onComplete(s, newSize);
36836 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36841 s.resizingEl.setHeight(newSize);
36843 onComplete(s, newSize);
36846 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36853 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36854 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36855 * Adapter that moves the splitter element to align with the resized sizing element.
36856 * Used with an absolute positioned SplitBar.
36857 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36858 * document.body, make sure you assign an id to the body element.
36860 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36861 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36862 this.container = Roo.get(container);
36865 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36866 init : function(s){
36867 this.basic.init(s);
36870 getElementSize : function(s){
36871 return this.basic.getElementSize(s);
36874 setElementSize : function(s, newSize, onComplete){
36875 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36878 moveSplitter : function(s){
36879 var yes = Roo.bootstrap.SplitBar;
36880 switch(s.placement){
36882 s.el.setX(s.resizingEl.getRight());
36885 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36888 s.el.setY(s.resizingEl.getBottom());
36891 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36898 * Orientation constant - Create a vertical SplitBar
36902 Roo.bootstrap.SplitBar.VERTICAL = 1;
36905 * Orientation constant - Create a horizontal SplitBar
36909 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36912 * Placement constant - The resizing element is to the left of the splitter element
36916 Roo.bootstrap.SplitBar.LEFT = 1;
36919 * Placement constant - The resizing element is to the right of the splitter element
36923 Roo.bootstrap.SplitBar.RIGHT = 2;
36926 * Placement constant - The resizing element is positioned above the splitter element
36930 Roo.bootstrap.SplitBar.TOP = 3;
36933 * Placement constant - The resizing element is positioned under splitter element
36937 Roo.bootstrap.SplitBar.BOTTOM = 4;
36938 Roo.namespace("Roo.bootstrap.layout");/*
36940 * Ext JS Library 1.1.1
36941 * Copyright(c) 2006-2007, Ext JS, LLC.
36943 * Originally Released Under LGPL - original licence link has changed is not relivant.
36946 * <script type="text/javascript">
36950 * @class Roo.bootstrap.layout.Manager
36951 * @extends Roo.bootstrap.Component
36952 * Base class for layout managers.
36954 Roo.bootstrap.layout.Manager = function(config)
36956 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36962 /** false to disable window resize monitoring @type Boolean */
36963 this.monitorWindowResize = true;
36968 * Fires when a layout is performed.
36969 * @param {Roo.LayoutManager} this
36973 * @event regionresized
36974 * Fires when the user resizes a region.
36975 * @param {Roo.LayoutRegion} region The resized region
36976 * @param {Number} newSize The new size (width for east/west, height for north/south)
36978 "regionresized" : true,
36980 * @event regioncollapsed
36981 * Fires when a region is collapsed.
36982 * @param {Roo.LayoutRegion} region The collapsed region
36984 "regioncollapsed" : true,
36986 * @event regionexpanded
36987 * Fires when a region is expanded.
36988 * @param {Roo.LayoutRegion} region The expanded region
36990 "regionexpanded" : true
36992 this.updating = false;
36995 this.el = Roo.get(config.el);
37001 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37006 monitorWindowResize : true,
37012 onRender : function(ct, position)
37015 this.el = Roo.get(ct);
37018 //this.fireEvent('render',this);
37022 initEvents: function()
37026 // ie scrollbar fix
37027 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37028 document.body.scroll = "no";
37029 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37030 this.el.position('relative');
37032 this.id = this.el.id;
37033 this.el.addClass("roo-layout-container");
37034 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37035 if(this.el.dom != document.body ) {
37036 this.el.on('resize', this.layout,this);
37037 this.el.on('show', this.layout,this);
37043 * Returns true if this layout is currently being updated
37044 * @return {Boolean}
37046 isUpdating : function(){
37047 return this.updating;
37051 * Suspend the LayoutManager from doing auto-layouts while
37052 * making multiple add or remove calls
37054 beginUpdate : function(){
37055 this.updating = true;
37059 * Restore auto-layouts and optionally disable the manager from performing a layout
37060 * @param {Boolean} noLayout true to disable a layout update
37062 endUpdate : function(noLayout){
37063 this.updating = false;
37069 layout: function(){
37073 onRegionResized : function(region, newSize){
37074 this.fireEvent("regionresized", region, newSize);
37078 onRegionCollapsed : function(region){
37079 this.fireEvent("regioncollapsed", region);
37082 onRegionExpanded : function(region){
37083 this.fireEvent("regionexpanded", region);
37087 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37088 * performs box-model adjustments.
37089 * @return {Object} The size as an object {width: (the width), height: (the height)}
37091 getViewSize : function()
37094 if(this.el.dom != document.body){
37095 size = this.el.getSize();
37097 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37099 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37100 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37105 * Returns the Element this layout is bound to.
37106 * @return {Roo.Element}
37108 getEl : function(){
37113 * Returns the specified region.
37114 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37115 * @return {Roo.LayoutRegion}
37117 getRegion : function(target){
37118 return this.regions[target.toLowerCase()];
37121 onWindowResize : function(){
37122 if(this.monitorWindowResize){
37129 * Ext JS Library 1.1.1
37130 * Copyright(c) 2006-2007, Ext JS, LLC.
37132 * Originally Released Under LGPL - original licence link has changed is not relivant.
37135 * <script type="text/javascript">
37138 * @class Roo.bootstrap.layout.Border
37139 * @extends Roo.bootstrap.layout.Manager
37140 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37141 * please see: examples/bootstrap/nested.html<br><br>
37143 <b>The container the layout is rendered into can be either the body element or any other element.
37144 If it is not the body element, the container needs to either be an absolute positioned element,
37145 or you will need to add "position:relative" to the css of the container. You will also need to specify
37146 the container size if it is not the body element.</b>
37149 * Create a new Border
37150 * @param {Object} config Configuration options
37152 Roo.bootstrap.layout.Border = function(config){
37153 config = config || {};
37154 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37158 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37159 if(config[region]){
37160 config[region].region = region;
37161 this.addRegion(config[region]);
37167 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37169 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37171 parent : false, // this might point to a 'nest' or a ???
37174 * Creates and adds a new region if it doesn't already exist.
37175 * @param {String} target The target region key (north, south, east, west or center).
37176 * @param {Object} config The regions config object
37177 * @return {BorderLayoutRegion} The new region
37179 addRegion : function(config)
37181 if(!this.regions[config.region]){
37182 var r = this.factory(config);
37183 this.bindRegion(r);
37185 return this.regions[config.region];
37189 bindRegion : function(r){
37190 this.regions[r.config.region] = r;
37192 r.on("visibilitychange", this.layout, this);
37193 r.on("paneladded", this.layout, this);
37194 r.on("panelremoved", this.layout, this);
37195 r.on("invalidated", this.layout, this);
37196 r.on("resized", this.onRegionResized, this);
37197 r.on("collapsed", this.onRegionCollapsed, this);
37198 r.on("expanded", this.onRegionExpanded, this);
37202 * Performs a layout update.
37204 layout : function()
37206 if(this.updating) {
37210 // render all the rebions if they have not been done alreayd?
37211 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37212 if(this.regions[region] && !this.regions[region].bodyEl){
37213 this.regions[region].onRender(this.el)
37217 var size = this.getViewSize();
37218 var w = size.width;
37219 var h = size.height;
37224 //var x = 0, y = 0;
37226 var rs = this.regions;
37227 var north = rs["north"];
37228 var south = rs["south"];
37229 var west = rs["west"];
37230 var east = rs["east"];
37231 var center = rs["center"];
37232 //if(this.hideOnLayout){ // not supported anymore
37233 //c.el.setStyle("display", "none");
37235 if(north && north.isVisible()){
37236 var b = north.getBox();
37237 var m = north.getMargins();
37238 b.width = w - (m.left+m.right);
37241 centerY = b.height + b.y + m.bottom;
37242 centerH -= centerY;
37243 north.updateBox(this.safeBox(b));
37245 if(south && south.isVisible()){
37246 var b = south.getBox();
37247 var m = south.getMargins();
37248 b.width = w - (m.left+m.right);
37250 var totalHeight = (b.height + m.top + m.bottom);
37251 b.y = h - totalHeight + m.top;
37252 centerH -= totalHeight;
37253 south.updateBox(this.safeBox(b));
37255 if(west && west.isVisible()){
37256 var b = west.getBox();
37257 var m = west.getMargins();
37258 b.height = centerH - (m.top+m.bottom);
37260 b.y = centerY + m.top;
37261 var totalWidth = (b.width + m.left + m.right);
37262 centerX += totalWidth;
37263 centerW -= totalWidth;
37264 west.updateBox(this.safeBox(b));
37266 if(east && east.isVisible()){
37267 var b = east.getBox();
37268 var m = east.getMargins();
37269 b.height = centerH - (m.top+m.bottom);
37270 var totalWidth = (b.width + m.left + m.right);
37271 b.x = w - totalWidth + m.left;
37272 b.y = centerY + m.top;
37273 centerW -= totalWidth;
37274 east.updateBox(this.safeBox(b));
37277 var m = center.getMargins();
37279 x: centerX + m.left,
37280 y: centerY + m.top,
37281 width: centerW - (m.left+m.right),
37282 height: centerH - (m.top+m.bottom)
37284 //if(this.hideOnLayout){
37285 //center.el.setStyle("display", "block");
37287 center.updateBox(this.safeBox(centerBox));
37290 this.fireEvent("layout", this);
37294 safeBox : function(box){
37295 box.width = Math.max(0, box.width);
37296 box.height = Math.max(0, box.height);
37301 * Adds a ContentPanel (or subclass) to this layout.
37302 * @param {String} target The target region key (north, south, east, west or center).
37303 * @param {Roo.ContentPanel} panel The panel to add
37304 * @return {Roo.ContentPanel} The added panel
37306 add : function(target, panel){
37308 target = target.toLowerCase();
37309 return this.regions[target].add(panel);
37313 * Remove a ContentPanel (or subclass) to this layout.
37314 * @param {String} target The target region key (north, south, east, west or center).
37315 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37316 * @return {Roo.ContentPanel} The removed panel
37318 remove : function(target, panel){
37319 target = target.toLowerCase();
37320 return this.regions[target].remove(panel);
37324 * Searches all regions for a panel with the specified id
37325 * @param {String} panelId
37326 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37328 findPanel : function(panelId){
37329 var rs = this.regions;
37330 for(var target in rs){
37331 if(typeof rs[target] != "function"){
37332 var p = rs[target].getPanel(panelId);
37342 * Searches all regions for a panel with the specified id and activates (shows) it.
37343 * @param {String/ContentPanel} panelId The panels id or the panel itself
37344 * @return {Roo.ContentPanel} The shown panel or null
37346 showPanel : function(panelId) {
37347 var rs = this.regions;
37348 for(var target in rs){
37349 var r = rs[target];
37350 if(typeof r != "function"){
37351 if(r.hasPanel(panelId)){
37352 return r.showPanel(panelId);
37360 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37361 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37364 restoreState : function(provider){
37366 provider = Roo.state.Manager;
37368 var sm = new Roo.LayoutStateManager();
37369 sm.init(this, provider);
37375 * Adds a xtype elements to the layout.
37379 xtype : 'ContentPanel',
37386 xtype : 'NestedLayoutPanel',
37392 items : [ ... list of content panels or nested layout panels.. ]
37396 * @param {Object} cfg Xtype definition of item to add.
37398 addxtype : function(cfg)
37400 // basically accepts a pannel...
37401 // can accept a layout region..!?!?
37402 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37405 // theory? children can only be panels??
37407 //if (!cfg.xtype.match(/Panel$/)) {
37412 if (typeof(cfg.region) == 'undefined') {
37413 Roo.log("Failed to add Panel, region was not set");
37417 var region = cfg.region;
37423 xitems = cfg.items;
37428 if ( region == 'center') {
37429 Roo.log("Center: " + cfg.title);
37435 case 'Content': // ContentPanel (el, cfg)
37436 case 'Scroll': // ContentPanel (el, cfg)
37438 cfg.autoCreate = cfg.autoCreate || true;
37439 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37441 // var el = this.el.createChild();
37442 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37445 this.add(region, ret);
37449 case 'TreePanel': // our new panel!
37450 cfg.el = this.el.createChild();
37451 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37452 this.add(region, ret);
37457 // create a new Layout (which is a Border Layout...
37459 var clayout = cfg.layout;
37460 clayout.el = this.el.createChild();
37461 clayout.items = clayout.items || [];
37465 // replace this exitems with the clayout ones..
37466 xitems = clayout.items;
37468 // force background off if it's in center...
37469 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37470 cfg.background = false;
37472 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37475 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37476 //console.log('adding nested layout panel ' + cfg.toSource());
37477 this.add(region, ret);
37478 nb = {}; /// find first...
37483 // needs grid and region
37485 //var el = this.getRegion(region).el.createChild();
37487 *var el = this.el.createChild();
37488 // create the grid first...
37489 cfg.grid.container = el;
37490 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37493 if (region == 'center' && this.active ) {
37494 cfg.background = false;
37497 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37499 this.add(region, ret);
37501 if (cfg.background) {
37502 // render grid on panel activation (if panel background)
37503 ret.on('activate', function(gp) {
37504 if (!gp.grid.rendered) {
37505 // gp.grid.render(el);
37509 // cfg.grid.render(el);
37515 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37516 // it was the old xcomponent building that caused this before.
37517 // espeically if border is the top element in the tree.
37527 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37529 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37530 this.add(region, ret);
37534 throw "Can not add '" + cfg.xtype + "' to Border";
37540 this.beginUpdate();
37544 Roo.each(xitems, function(i) {
37545 region = nb && i.region ? i.region : false;
37547 var add = ret.addxtype(i);
37550 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37551 if (!i.background) {
37552 abn[region] = nb[region] ;
37559 // make the last non-background panel active..
37560 //if (nb) { Roo.log(abn); }
37563 for(var r in abn) {
37564 region = this.getRegion(r);
37566 // tried using nb[r], but it does not work..
37568 region.showPanel(abn[r]);
37579 factory : function(cfg)
37582 var validRegions = Roo.bootstrap.layout.Border.regions;
37584 var target = cfg.region;
37587 var r = Roo.bootstrap.layout;
37591 return new r.North(cfg);
37593 return new r.South(cfg);
37595 return new r.East(cfg);
37597 return new r.West(cfg);
37599 return new r.Center(cfg);
37601 throw 'Layout region "'+target+'" not supported.';
37608 * Ext JS Library 1.1.1
37609 * Copyright(c) 2006-2007, Ext JS, LLC.
37611 * Originally Released Under LGPL - original licence link has changed is not relivant.
37614 * <script type="text/javascript">
37618 * @class Roo.bootstrap.layout.Basic
37619 * @extends Roo.util.Observable
37620 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37621 * and does not have a titlebar, tabs or any other features. All it does is size and position
37622 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37623 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37624 * @cfg {string} region the region that it inhabits..
37625 * @cfg {bool} skipConfig skip config?
37629 Roo.bootstrap.layout.Basic = function(config){
37631 this.mgr = config.mgr;
37633 this.position = config.region;
37635 var skipConfig = config.skipConfig;
37639 * @scope Roo.BasicLayoutRegion
37643 * @event beforeremove
37644 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37645 * @param {Roo.LayoutRegion} this
37646 * @param {Roo.ContentPanel} panel The panel
37647 * @param {Object} e The cancel event object
37649 "beforeremove" : true,
37651 * @event invalidated
37652 * Fires when the layout for this region is changed.
37653 * @param {Roo.LayoutRegion} this
37655 "invalidated" : true,
37657 * @event visibilitychange
37658 * Fires when this region is shown or hidden
37659 * @param {Roo.LayoutRegion} this
37660 * @param {Boolean} visibility true or false
37662 "visibilitychange" : true,
37664 * @event paneladded
37665 * Fires when a panel is added.
37666 * @param {Roo.LayoutRegion} this
37667 * @param {Roo.ContentPanel} panel The panel
37669 "paneladded" : true,
37671 * @event panelremoved
37672 * Fires when a panel is removed.
37673 * @param {Roo.LayoutRegion} this
37674 * @param {Roo.ContentPanel} panel The panel
37676 "panelremoved" : true,
37678 * @event beforecollapse
37679 * Fires when this region before collapse.
37680 * @param {Roo.LayoutRegion} this
37682 "beforecollapse" : true,
37685 * Fires when this region is collapsed.
37686 * @param {Roo.LayoutRegion} this
37688 "collapsed" : true,
37691 * Fires when this region is expanded.
37692 * @param {Roo.LayoutRegion} this
37697 * Fires when this region is slid into view.
37698 * @param {Roo.LayoutRegion} this
37700 "slideshow" : true,
37703 * Fires when this region slides out of view.
37704 * @param {Roo.LayoutRegion} this
37706 "slidehide" : true,
37708 * @event panelactivated
37709 * Fires when a panel is activated.
37710 * @param {Roo.LayoutRegion} this
37711 * @param {Roo.ContentPanel} panel The activated panel
37713 "panelactivated" : true,
37716 * Fires when the user resizes this region.
37717 * @param {Roo.LayoutRegion} this
37718 * @param {Number} newSize The new size (width for east/west, height for north/south)
37722 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37723 this.panels = new Roo.util.MixedCollection();
37724 this.panels.getKey = this.getPanelId.createDelegate(this);
37726 this.activePanel = null;
37727 // ensure listeners are added...
37729 if (config.listeners || config.events) {
37730 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37731 listeners : config.listeners || {},
37732 events : config.events || {}
37736 if(skipConfig !== true){
37737 this.applyConfig(config);
37741 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37743 getPanelId : function(p){
37747 applyConfig : function(config){
37748 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37749 this.config = config;
37754 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37755 * the width, for horizontal (north, south) the height.
37756 * @param {Number} newSize The new width or height
37758 resizeTo : function(newSize){
37759 var el = this.el ? this.el :
37760 (this.activePanel ? this.activePanel.getEl() : null);
37762 switch(this.position){
37765 el.setWidth(newSize);
37766 this.fireEvent("resized", this, newSize);
37770 el.setHeight(newSize);
37771 this.fireEvent("resized", this, newSize);
37777 getBox : function(){
37778 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37781 getMargins : function(){
37782 return this.margins;
37785 updateBox : function(box){
37787 var el = this.activePanel.getEl();
37788 el.dom.style.left = box.x + "px";
37789 el.dom.style.top = box.y + "px";
37790 this.activePanel.setSize(box.width, box.height);
37794 * Returns the container element for this region.
37795 * @return {Roo.Element}
37797 getEl : function(){
37798 return this.activePanel;
37802 * Returns true if this region is currently visible.
37803 * @return {Boolean}
37805 isVisible : function(){
37806 return this.activePanel ? true : false;
37809 setActivePanel : function(panel){
37810 panel = this.getPanel(panel);
37811 if(this.activePanel && this.activePanel != panel){
37812 this.activePanel.setActiveState(false);
37813 this.activePanel.getEl().setLeftTop(-10000,-10000);
37815 this.activePanel = panel;
37816 panel.setActiveState(true);
37818 panel.setSize(this.box.width, this.box.height);
37820 this.fireEvent("panelactivated", this, panel);
37821 this.fireEvent("invalidated");
37825 * Show the specified panel.
37826 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37827 * @return {Roo.ContentPanel} The shown panel or null
37829 showPanel : function(panel){
37830 panel = this.getPanel(panel);
37832 this.setActivePanel(panel);
37838 * Get the active panel for this region.
37839 * @return {Roo.ContentPanel} The active panel or null
37841 getActivePanel : function(){
37842 return this.activePanel;
37846 * Add the passed ContentPanel(s)
37847 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37848 * @return {Roo.ContentPanel} The panel added (if only one was added)
37850 add : function(panel){
37851 if(arguments.length > 1){
37852 for(var i = 0, len = arguments.length; i < len; i++) {
37853 this.add(arguments[i]);
37857 if(this.hasPanel(panel)){
37858 this.showPanel(panel);
37861 var el = panel.getEl();
37862 if(el.dom.parentNode != this.mgr.el.dom){
37863 this.mgr.el.dom.appendChild(el.dom);
37865 if(panel.setRegion){
37866 panel.setRegion(this);
37868 this.panels.add(panel);
37869 el.setStyle("position", "absolute");
37870 if(!panel.background){
37871 this.setActivePanel(panel);
37872 if(this.config.initialSize && this.panels.getCount()==1){
37873 this.resizeTo(this.config.initialSize);
37876 this.fireEvent("paneladded", this, panel);
37881 * Returns true if the panel is in this region.
37882 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37883 * @return {Boolean}
37885 hasPanel : function(panel){
37886 if(typeof panel == "object"){ // must be panel obj
37887 panel = panel.getId();
37889 return this.getPanel(panel) ? true : false;
37893 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37894 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37895 * @param {Boolean} preservePanel Overrides the config preservePanel option
37896 * @return {Roo.ContentPanel} The panel that was removed
37898 remove : function(panel, preservePanel){
37899 panel = this.getPanel(panel);
37904 this.fireEvent("beforeremove", this, panel, e);
37905 if(e.cancel === true){
37908 var panelId = panel.getId();
37909 this.panels.removeKey(panelId);
37914 * Returns the panel specified or null if it's not in this region.
37915 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37916 * @return {Roo.ContentPanel}
37918 getPanel : function(id){
37919 if(typeof id == "object"){ // must be panel obj
37922 return this.panels.get(id);
37926 * Returns this regions position (north/south/east/west/center).
37929 getPosition: function(){
37930 return this.position;
37934 * Ext JS Library 1.1.1
37935 * Copyright(c) 2006-2007, Ext JS, LLC.
37937 * Originally Released Under LGPL - original licence link has changed is not relivant.
37940 * <script type="text/javascript">
37944 * @class Roo.bootstrap.layout.Region
37945 * @extends Roo.bootstrap.layout.Basic
37946 * This class represents a region in a layout manager.
37948 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37949 * @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})
37950 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37951 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37952 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37953 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37954 * @cfg {String} title The title for the region (overrides panel titles)
37955 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37956 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37957 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37958 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37959 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37960 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37961 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37962 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37963 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37964 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37966 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37967 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37968 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37969 * @cfg {Number} width For East/West panels
37970 * @cfg {Number} height For North/South panels
37971 * @cfg {Boolean} split To show the splitter
37972 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37974 * @cfg {string} cls Extra CSS classes to add to region
37976 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37977 * @cfg {string} region the region that it inhabits..
37980 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37981 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37983 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37984 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37985 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37987 Roo.bootstrap.layout.Region = function(config)
37989 this.applyConfig(config);
37991 var mgr = config.mgr;
37992 var pos = config.region;
37993 config.skipConfig = true;
37994 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37997 this.onRender(mgr.el);
38000 this.visible = true;
38001 this.collapsed = false;
38002 this.unrendered_panels = [];
38005 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38007 position: '', // set by wrapper (eg. north/south etc..)
38008 unrendered_panels : null, // unrendered panels.
38010 tabPosition : false,
38012 mgr: false, // points to 'Border'
38015 createBody : function(){
38016 /** This region's body element
38017 * @type Roo.Element */
38018 this.bodyEl = this.el.createChild({
38020 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38024 onRender: function(ctr, pos)
38026 var dh = Roo.DomHelper;
38027 /** This region's container element
38028 * @type Roo.Element */
38029 this.el = dh.append(ctr.dom, {
38031 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38033 /** This region's title element
38034 * @type Roo.Element */
38036 this.titleEl = dh.append(this.el.dom, {
38038 unselectable: "on",
38039 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38041 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38042 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38046 this.titleEl.enableDisplayMode();
38047 /** This region's title text element
38048 * @type HTMLElement */
38049 this.titleTextEl = this.titleEl.dom.firstChild;
38050 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38052 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38053 this.closeBtn.enableDisplayMode();
38054 this.closeBtn.on("click", this.closeClicked, this);
38055 this.closeBtn.hide();
38057 this.createBody(this.config);
38058 if(this.config.hideWhenEmpty){
38060 this.on("paneladded", this.validateVisibility, this);
38061 this.on("panelremoved", this.validateVisibility, this);
38063 if(this.autoScroll){
38064 this.bodyEl.setStyle("overflow", "auto");
38066 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38068 //if(c.titlebar !== false){
38069 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38070 this.titleEl.hide();
38072 this.titleEl.show();
38073 if(this.config.title){
38074 this.titleTextEl.innerHTML = this.config.title;
38078 if(this.config.collapsed){
38079 this.collapse(true);
38081 if(this.config.hidden){
38085 if (this.unrendered_panels && this.unrendered_panels.length) {
38086 for (var i =0;i< this.unrendered_panels.length; i++) {
38087 this.add(this.unrendered_panels[i]);
38089 this.unrendered_panels = null;
38095 applyConfig : function(c)
38098 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38099 var dh = Roo.DomHelper;
38100 if(c.titlebar !== false){
38101 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38102 this.collapseBtn.on("click", this.collapse, this);
38103 this.collapseBtn.enableDisplayMode();
38105 if(c.showPin === true || this.showPin){
38106 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38107 this.stickBtn.enableDisplayMode();
38108 this.stickBtn.on("click", this.expand, this);
38109 this.stickBtn.hide();
38114 /** This region's collapsed element
38115 * @type Roo.Element */
38118 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38119 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38122 if(c.floatable !== false){
38123 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38124 this.collapsedEl.on("click", this.collapseClick, this);
38127 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38128 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38129 id: "message", unselectable: "on", style:{"float":"left"}});
38130 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38132 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38133 this.expandBtn.on("click", this.expand, this);
38137 if(this.collapseBtn){
38138 this.collapseBtn.setVisible(c.collapsible == true);
38141 this.cmargins = c.cmargins || this.cmargins ||
38142 (this.position == "west" || this.position == "east" ?
38143 {top: 0, left: 2, right:2, bottom: 0} :
38144 {top: 2, left: 0, right:0, bottom: 2});
38146 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38149 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38151 this.autoScroll = c.autoScroll || false;
38156 this.duration = c.duration || .30;
38157 this.slideDuration = c.slideDuration || .45;
38162 * Returns true if this region is currently visible.
38163 * @return {Boolean}
38165 isVisible : function(){
38166 return this.visible;
38170 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38171 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38173 //setCollapsedTitle : function(title){
38174 // title = title || " ";
38175 // if(this.collapsedTitleTextEl){
38176 // this.collapsedTitleTextEl.innerHTML = title;
38180 getBox : function(){
38182 // if(!this.collapsed){
38183 b = this.el.getBox(false, true);
38185 // b = this.collapsedEl.getBox(false, true);
38190 getMargins : function(){
38191 return this.margins;
38192 //return this.collapsed ? this.cmargins : this.margins;
38195 highlight : function(){
38196 this.el.addClass("x-layout-panel-dragover");
38199 unhighlight : function(){
38200 this.el.removeClass("x-layout-panel-dragover");
38203 updateBox : function(box)
38205 if (!this.bodyEl) {
38206 return; // not rendered yet..
38210 if(!this.collapsed){
38211 this.el.dom.style.left = box.x + "px";
38212 this.el.dom.style.top = box.y + "px";
38213 this.updateBody(box.width, box.height);
38215 this.collapsedEl.dom.style.left = box.x + "px";
38216 this.collapsedEl.dom.style.top = box.y + "px";
38217 this.collapsedEl.setSize(box.width, box.height);
38220 this.tabs.autoSizeTabs();
38224 updateBody : function(w, h)
38227 this.el.setWidth(w);
38228 w -= this.el.getBorderWidth("rl");
38229 if(this.config.adjustments){
38230 w += this.config.adjustments[0];
38233 if(h !== null && h > 0){
38234 this.el.setHeight(h);
38235 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38236 h -= this.el.getBorderWidth("tb");
38237 if(this.config.adjustments){
38238 h += this.config.adjustments[1];
38240 this.bodyEl.setHeight(h);
38242 h = this.tabs.syncHeight(h);
38245 if(this.panelSize){
38246 w = w !== null ? w : this.panelSize.width;
38247 h = h !== null ? h : this.panelSize.height;
38249 if(this.activePanel){
38250 var el = this.activePanel.getEl();
38251 w = w !== null ? w : el.getWidth();
38252 h = h !== null ? h : el.getHeight();
38253 this.panelSize = {width: w, height: h};
38254 this.activePanel.setSize(w, h);
38256 if(Roo.isIE && this.tabs){
38257 this.tabs.el.repaint();
38262 * Returns the container element for this region.
38263 * @return {Roo.Element}
38265 getEl : function(){
38270 * Hides this region.
38273 //if(!this.collapsed){
38274 this.el.dom.style.left = "-2000px";
38277 // this.collapsedEl.dom.style.left = "-2000px";
38278 // this.collapsedEl.hide();
38280 this.visible = false;
38281 this.fireEvent("visibilitychange", this, false);
38285 * Shows this region if it was previously hidden.
38288 //if(!this.collapsed){
38291 // this.collapsedEl.show();
38293 this.visible = true;
38294 this.fireEvent("visibilitychange", this, true);
38297 closeClicked : function(){
38298 if(this.activePanel){
38299 this.remove(this.activePanel);
38303 collapseClick : function(e){
38305 e.stopPropagation();
38308 e.stopPropagation();
38314 * Collapses this region.
38315 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38318 collapse : function(skipAnim, skipCheck = false){
38319 if(this.collapsed) {
38323 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38325 this.collapsed = true;
38327 this.split.el.hide();
38329 if(this.config.animate && skipAnim !== true){
38330 this.fireEvent("invalidated", this);
38331 this.animateCollapse();
38333 this.el.setLocation(-20000,-20000);
38335 this.collapsedEl.show();
38336 this.fireEvent("collapsed", this);
38337 this.fireEvent("invalidated", this);
38343 animateCollapse : function(){
38348 * Expands this region if it was previously collapsed.
38349 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38350 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38353 expand : function(e, skipAnim){
38355 e.stopPropagation();
38357 if(!this.collapsed || this.el.hasActiveFx()) {
38361 this.afterSlideIn();
38364 this.collapsed = false;
38365 if(this.config.animate && skipAnim !== true){
38366 this.animateExpand();
38370 this.split.el.show();
38372 this.collapsedEl.setLocation(-2000,-2000);
38373 this.collapsedEl.hide();
38374 this.fireEvent("invalidated", this);
38375 this.fireEvent("expanded", this);
38379 animateExpand : function(){
38383 initTabs : function()
38385 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38387 var ts = new Roo.bootstrap.panel.Tabs({
38388 el: this.bodyEl.dom,
38390 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38391 disableTooltips: this.config.disableTabTips,
38392 toolbar : this.config.toolbar
38395 if(this.config.hideTabs){
38396 ts.stripWrap.setDisplayed(false);
38399 ts.resizeTabs = this.config.resizeTabs === true;
38400 ts.minTabWidth = this.config.minTabWidth || 40;
38401 ts.maxTabWidth = this.config.maxTabWidth || 250;
38402 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38403 ts.monitorResize = false;
38404 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38405 ts.bodyEl.addClass('roo-layout-tabs-body');
38406 this.panels.each(this.initPanelAsTab, this);
38409 initPanelAsTab : function(panel){
38410 var ti = this.tabs.addTab(
38414 this.config.closeOnTab && panel.isClosable(),
38417 if(panel.tabTip !== undefined){
38418 ti.setTooltip(panel.tabTip);
38420 ti.on("activate", function(){
38421 this.setActivePanel(panel);
38424 if(this.config.closeOnTab){
38425 ti.on("beforeclose", function(t, e){
38427 this.remove(panel);
38431 panel.tabItem = ti;
38436 updatePanelTitle : function(panel, title)
38438 if(this.activePanel == panel){
38439 this.updateTitle(title);
38442 var ti = this.tabs.getTab(panel.getEl().id);
38444 if(panel.tabTip !== undefined){
38445 ti.setTooltip(panel.tabTip);
38450 updateTitle : function(title){
38451 if(this.titleTextEl && !this.config.title){
38452 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38456 setActivePanel : function(panel)
38458 panel = this.getPanel(panel);
38459 if(this.activePanel && this.activePanel != panel){
38460 if(this.activePanel.setActiveState(false) === false){
38464 this.activePanel = panel;
38465 panel.setActiveState(true);
38466 if(this.panelSize){
38467 panel.setSize(this.panelSize.width, this.panelSize.height);
38470 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38472 this.updateTitle(panel.getTitle());
38474 this.fireEvent("invalidated", this);
38476 this.fireEvent("panelactivated", this, panel);
38480 * Shows the specified panel.
38481 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38482 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38484 showPanel : function(panel)
38486 panel = this.getPanel(panel);
38489 var tab = this.tabs.getTab(panel.getEl().id);
38490 if(tab.isHidden()){
38491 this.tabs.unhideTab(tab.id);
38495 this.setActivePanel(panel);
38502 * Get the active panel for this region.
38503 * @return {Roo.ContentPanel} The active panel or null
38505 getActivePanel : function(){
38506 return this.activePanel;
38509 validateVisibility : function(){
38510 if(this.panels.getCount() < 1){
38511 this.updateTitle(" ");
38512 this.closeBtn.hide();
38515 if(!this.isVisible()){
38522 * Adds the passed ContentPanel(s) to this region.
38523 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38524 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38526 add : function(panel)
38528 if(arguments.length > 1){
38529 for(var i = 0, len = arguments.length; i < len; i++) {
38530 this.add(arguments[i]);
38535 // if we have not been rendered yet, then we can not really do much of this..
38536 if (!this.bodyEl) {
38537 this.unrendered_panels.push(panel);
38544 if(this.hasPanel(panel)){
38545 this.showPanel(panel);
38548 panel.setRegion(this);
38549 this.panels.add(panel);
38550 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38551 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38552 // and hide them... ???
38553 this.bodyEl.dom.appendChild(panel.getEl().dom);
38554 if(panel.background !== true){
38555 this.setActivePanel(panel);
38557 this.fireEvent("paneladded", this, panel);
38564 this.initPanelAsTab(panel);
38568 if(panel.background !== true){
38569 this.tabs.activate(panel.getEl().id);
38571 this.fireEvent("paneladded", this, panel);
38576 * Hides the tab for the specified panel.
38577 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38579 hidePanel : function(panel){
38580 if(this.tabs && (panel = this.getPanel(panel))){
38581 this.tabs.hideTab(panel.getEl().id);
38586 * Unhides the tab for a previously hidden panel.
38587 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38589 unhidePanel : function(panel){
38590 if(this.tabs && (panel = this.getPanel(panel))){
38591 this.tabs.unhideTab(panel.getEl().id);
38595 clearPanels : function(){
38596 while(this.panels.getCount() > 0){
38597 this.remove(this.panels.first());
38602 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38603 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38604 * @param {Boolean} preservePanel Overrides the config preservePanel option
38605 * @return {Roo.ContentPanel} The panel that was removed
38607 remove : function(panel, preservePanel)
38609 panel = this.getPanel(panel);
38614 this.fireEvent("beforeremove", this, panel, e);
38615 if(e.cancel === true){
38618 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38619 var panelId = panel.getId();
38620 this.panels.removeKey(panelId);
38622 document.body.appendChild(panel.getEl().dom);
38625 this.tabs.removeTab(panel.getEl().id);
38626 }else if (!preservePanel){
38627 this.bodyEl.dom.removeChild(panel.getEl().dom);
38629 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38630 var p = this.panels.first();
38631 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38632 tempEl.appendChild(p.getEl().dom);
38633 this.bodyEl.update("");
38634 this.bodyEl.dom.appendChild(p.getEl().dom);
38636 this.updateTitle(p.getTitle());
38638 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38639 this.setActivePanel(p);
38641 panel.setRegion(null);
38642 if(this.activePanel == panel){
38643 this.activePanel = null;
38645 if(this.config.autoDestroy !== false && preservePanel !== true){
38646 try{panel.destroy();}catch(e){}
38648 this.fireEvent("panelremoved", this, panel);
38653 * Returns the TabPanel component used by this region
38654 * @return {Roo.TabPanel}
38656 getTabs : function(){
38660 createTool : function(parentEl, className){
38661 var btn = Roo.DomHelper.append(parentEl, {
38663 cls: "x-layout-tools-button",
38666 cls: "roo-layout-tools-button-inner " + className,
38670 btn.addClassOnOver("roo-layout-tools-button-over");
38675 * Ext JS Library 1.1.1
38676 * Copyright(c) 2006-2007, Ext JS, LLC.
38678 * Originally Released Under LGPL - original licence link has changed is not relivant.
38681 * <script type="text/javascript">
38687 * @class Roo.SplitLayoutRegion
38688 * @extends Roo.LayoutRegion
38689 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38691 Roo.bootstrap.layout.Split = function(config){
38692 this.cursor = config.cursor;
38693 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38696 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38698 splitTip : "Drag to resize.",
38699 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38700 useSplitTips : false,
38702 applyConfig : function(config){
38703 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38706 onRender : function(ctr,pos) {
38708 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38709 if(!this.config.split){
38714 var splitEl = Roo.DomHelper.append(ctr.dom, {
38716 id: this.el.id + "-split",
38717 cls: "roo-layout-split roo-layout-split-"+this.position,
38720 /** The SplitBar for this region
38721 * @type Roo.SplitBar */
38722 // does not exist yet...
38723 Roo.log([this.position, this.orientation]);
38725 this.split = new Roo.bootstrap.SplitBar({
38726 dragElement : splitEl,
38727 resizingElement: this.el,
38728 orientation : this.orientation
38731 this.split.on("moved", this.onSplitMove, this);
38732 this.split.useShim = this.config.useShim === true;
38733 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38734 if(this.useSplitTips){
38735 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38737 //if(config.collapsible){
38738 // this.split.el.on("dblclick", this.collapse, this);
38741 if(typeof this.config.minSize != "undefined"){
38742 this.split.minSize = this.config.minSize;
38744 if(typeof this.config.maxSize != "undefined"){
38745 this.split.maxSize = this.config.maxSize;
38747 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38748 this.hideSplitter();
38753 getHMaxSize : function(){
38754 var cmax = this.config.maxSize || 10000;
38755 var center = this.mgr.getRegion("center");
38756 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38759 getVMaxSize : function(){
38760 var cmax = this.config.maxSize || 10000;
38761 var center = this.mgr.getRegion("center");
38762 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38765 onSplitMove : function(split, newSize){
38766 this.fireEvent("resized", this, newSize);
38770 * Returns the {@link Roo.SplitBar} for this region.
38771 * @return {Roo.SplitBar}
38773 getSplitBar : function(){
38778 this.hideSplitter();
38779 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38782 hideSplitter : function(){
38784 this.split.el.setLocation(-2000,-2000);
38785 this.split.el.hide();
38791 this.split.el.show();
38793 Roo.bootstrap.layout.Split.superclass.show.call(this);
38796 beforeSlide: function(){
38797 if(Roo.isGecko){// firefox overflow auto bug workaround
38798 this.bodyEl.clip();
38800 this.tabs.bodyEl.clip();
38802 if(this.activePanel){
38803 this.activePanel.getEl().clip();
38805 if(this.activePanel.beforeSlide){
38806 this.activePanel.beforeSlide();
38812 afterSlide : function(){
38813 if(Roo.isGecko){// firefox overflow auto bug workaround
38814 this.bodyEl.unclip();
38816 this.tabs.bodyEl.unclip();
38818 if(this.activePanel){
38819 this.activePanel.getEl().unclip();
38820 if(this.activePanel.afterSlide){
38821 this.activePanel.afterSlide();
38827 initAutoHide : function(){
38828 if(this.autoHide !== false){
38829 if(!this.autoHideHd){
38830 var st = new Roo.util.DelayedTask(this.slideIn, this);
38831 this.autoHideHd = {
38832 "mouseout": function(e){
38833 if(!e.within(this.el, true)){
38837 "mouseover" : function(e){
38843 this.el.on(this.autoHideHd);
38847 clearAutoHide : function(){
38848 if(this.autoHide !== false){
38849 this.el.un("mouseout", this.autoHideHd.mouseout);
38850 this.el.un("mouseover", this.autoHideHd.mouseover);
38854 clearMonitor : function(){
38855 Roo.get(document).un("click", this.slideInIf, this);
38858 // these names are backwards but not changed for compat
38859 slideOut : function(){
38860 if(this.isSlid || this.el.hasActiveFx()){
38863 this.isSlid = true;
38864 if(this.collapseBtn){
38865 this.collapseBtn.hide();
38867 this.closeBtnState = this.closeBtn.getStyle('display');
38868 this.closeBtn.hide();
38870 this.stickBtn.show();
38873 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38874 this.beforeSlide();
38875 this.el.setStyle("z-index", 10001);
38876 this.el.slideIn(this.getSlideAnchor(), {
38877 callback: function(){
38879 this.initAutoHide();
38880 Roo.get(document).on("click", this.slideInIf, this);
38881 this.fireEvent("slideshow", this);
38888 afterSlideIn : function(){
38889 this.clearAutoHide();
38890 this.isSlid = false;
38891 this.clearMonitor();
38892 this.el.setStyle("z-index", "");
38893 if(this.collapseBtn){
38894 this.collapseBtn.show();
38896 this.closeBtn.setStyle('display', this.closeBtnState);
38898 this.stickBtn.hide();
38900 this.fireEvent("slidehide", this);
38903 slideIn : function(cb){
38904 if(!this.isSlid || this.el.hasActiveFx()){
38908 this.isSlid = false;
38909 this.beforeSlide();
38910 this.el.slideOut(this.getSlideAnchor(), {
38911 callback: function(){
38912 this.el.setLeftTop(-10000, -10000);
38914 this.afterSlideIn();
38922 slideInIf : function(e){
38923 if(!e.within(this.el)){
38928 animateCollapse : function(){
38929 this.beforeSlide();
38930 this.el.setStyle("z-index", 20000);
38931 var anchor = this.getSlideAnchor();
38932 this.el.slideOut(anchor, {
38933 callback : function(){
38934 this.el.setStyle("z-index", "");
38935 this.collapsedEl.slideIn(anchor, {duration:.3});
38937 this.el.setLocation(-10000,-10000);
38939 this.fireEvent("collapsed", this);
38946 animateExpand : function(){
38947 this.beforeSlide();
38948 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38949 this.el.setStyle("z-index", 20000);
38950 this.collapsedEl.hide({
38953 this.el.slideIn(this.getSlideAnchor(), {
38954 callback : function(){
38955 this.el.setStyle("z-index", "");
38958 this.split.el.show();
38960 this.fireEvent("invalidated", this);
38961 this.fireEvent("expanded", this);
38989 getAnchor : function(){
38990 return this.anchors[this.position];
38993 getCollapseAnchor : function(){
38994 return this.canchors[this.position];
38997 getSlideAnchor : function(){
38998 return this.sanchors[this.position];
39001 getAlignAdj : function(){
39002 var cm = this.cmargins;
39003 switch(this.position){
39019 getExpandAdj : function(){
39020 var c = this.collapsedEl, cm = this.cmargins;
39021 switch(this.position){
39023 return [-(cm.right+c.getWidth()+cm.left), 0];
39026 return [cm.right+c.getWidth()+cm.left, 0];
39029 return [0, -(cm.top+cm.bottom+c.getHeight())];
39032 return [0, cm.top+cm.bottom+c.getHeight()];
39038 * Ext JS Library 1.1.1
39039 * Copyright(c) 2006-2007, Ext JS, LLC.
39041 * Originally Released Under LGPL - original licence link has changed is not relivant.
39044 * <script type="text/javascript">
39047 * These classes are private internal classes
39049 Roo.bootstrap.layout.Center = function(config){
39050 config.region = "center";
39051 Roo.bootstrap.layout.Region.call(this, config);
39052 this.visible = true;
39053 this.minWidth = config.minWidth || 20;
39054 this.minHeight = config.minHeight || 20;
39057 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39059 // center panel can't be hidden
39063 // center panel can't be hidden
39066 getMinWidth: function(){
39067 return this.minWidth;
39070 getMinHeight: function(){
39071 return this.minHeight;
39085 Roo.bootstrap.layout.North = function(config)
39087 config.region = 'north';
39088 config.cursor = 'n-resize';
39090 Roo.bootstrap.layout.Split.call(this, config);
39094 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39095 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39096 this.split.el.addClass("roo-layout-split-v");
39098 var size = config.initialSize || config.height;
39099 if(typeof size != "undefined"){
39100 this.el.setHeight(size);
39103 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39105 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39109 getBox : function(){
39110 if(this.collapsed){
39111 return this.collapsedEl.getBox();
39113 var box = this.el.getBox();
39115 box.height += this.split.el.getHeight();
39120 updateBox : function(box){
39121 if(this.split && !this.collapsed){
39122 box.height -= this.split.el.getHeight();
39123 this.split.el.setLeft(box.x);
39124 this.split.el.setTop(box.y+box.height);
39125 this.split.el.setWidth(box.width);
39127 if(this.collapsed){
39128 this.updateBody(box.width, null);
39130 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39138 Roo.bootstrap.layout.South = function(config){
39139 config.region = 'south';
39140 config.cursor = 's-resize';
39141 Roo.bootstrap.layout.Split.call(this, config);
39143 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39144 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39145 this.split.el.addClass("roo-layout-split-v");
39147 var size = config.initialSize || config.height;
39148 if(typeof size != "undefined"){
39149 this.el.setHeight(size);
39153 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39154 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39155 getBox : function(){
39156 if(this.collapsed){
39157 return this.collapsedEl.getBox();
39159 var box = this.el.getBox();
39161 var sh = this.split.el.getHeight();
39168 updateBox : function(box){
39169 if(this.split && !this.collapsed){
39170 var sh = this.split.el.getHeight();
39173 this.split.el.setLeft(box.x);
39174 this.split.el.setTop(box.y-sh);
39175 this.split.el.setWidth(box.width);
39177 if(this.collapsed){
39178 this.updateBody(box.width, null);
39180 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39184 Roo.bootstrap.layout.East = function(config){
39185 config.region = "east";
39186 config.cursor = "e-resize";
39187 Roo.bootstrap.layout.Split.call(this, config);
39189 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39190 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39191 this.split.el.addClass("roo-layout-split-h");
39193 var size = config.initialSize || config.width;
39194 if(typeof size != "undefined"){
39195 this.el.setWidth(size);
39198 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39199 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39200 getBox : function(){
39201 if(this.collapsed){
39202 return this.collapsedEl.getBox();
39204 var box = this.el.getBox();
39206 var sw = this.split.el.getWidth();
39213 updateBox : function(box){
39214 if(this.split && !this.collapsed){
39215 var sw = this.split.el.getWidth();
39217 this.split.el.setLeft(box.x);
39218 this.split.el.setTop(box.y);
39219 this.split.el.setHeight(box.height);
39222 if(this.collapsed){
39223 this.updateBody(null, box.height);
39225 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39229 Roo.bootstrap.layout.West = function(config){
39230 config.region = "west";
39231 config.cursor = "w-resize";
39233 Roo.bootstrap.layout.Split.call(this, config);
39235 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39236 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39237 this.split.el.addClass("roo-layout-split-h");
39241 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39242 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39244 onRender: function(ctr, pos)
39246 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39247 var size = this.config.initialSize || this.config.width;
39248 if(typeof size != "undefined"){
39249 this.el.setWidth(size);
39253 getBox : function(){
39254 if(this.collapsed){
39255 return this.collapsedEl.getBox();
39257 var box = this.el.getBox();
39259 box.width += this.split.el.getWidth();
39264 updateBox : function(box){
39265 if(this.split && !this.collapsed){
39266 var sw = this.split.el.getWidth();
39268 this.split.el.setLeft(box.x+box.width);
39269 this.split.el.setTop(box.y);
39270 this.split.el.setHeight(box.height);
39272 if(this.collapsed){
39273 this.updateBody(null, box.height);
39275 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39277 });Roo.namespace("Roo.bootstrap.panel");/*
39279 * Ext JS Library 1.1.1
39280 * Copyright(c) 2006-2007, Ext JS, LLC.
39282 * Originally Released Under LGPL - original licence link has changed is not relivant.
39285 * <script type="text/javascript">
39288 * @class Roo.ContentPanel
39289 * @extends Roo.util.Observable
39290 * A basic ContentPanel element.
39291 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39292 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39293 * @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
39294 * @cfg {Boolean} closable True if the panel can be closed/removed
39295 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39296 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39297 * @cfg {Toolbar} toolbar A toolbar for this panel
39298 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39299 * @cfg {String} title The title for this panel
39300 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39301 * @cfg {String} url Calls {@link #setUrl} with this value
39302 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39303 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39304 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39305 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39306 * @cfg {Boolean} badges render the badges
39307 * @cfg {String} cls extra classes to use
39308 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39311 * Create a new ContentPanel.
39312 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39313 * @param {String/Object} config A string to set only the title or a config object
39314 * @param {String} content (optional) Set the HTML content for this panel
39315 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39317 Roo.bootstrap.panel.Content = function( config){
39319 this.tpl = config.tpl || false;
39321 var el = config.el;
39322 var content = config.content;
39324 if(config.autoCreate){ // xtype is available if this is called from factory
39327 this.el = Roo.get(el);
39328 if(!this.el && config && config.autoCreate){
39329 if(typeof config.autoCreate == "object"){
39330 if(!config.autoCreate.id){
39331 config.autoCreate.id = config.id||el;
39333 this.el = Roo.DomHelper.append(document.body,
39334 config.autoCreate, true);
39338 cls: (config.cls || '') +
39339 (config.background ? ' bg-' + config.background : '') +
39340 " roo-layout-inactive-content",
39344 elcfg.html = config.html;
39348 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39351 this.closable = false;
39352 this.loaded = false;
39353 this.active = false;
39356 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39358 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39360 this.wrapEl = this.el; //this.el.wrap();
39362 if (config.toolbar.items) {
39363 ti = config.toolbar.items ;
39364 delete config.toolbar.items ;
39368 this.toolbar.render(this.wrapEl, 'before');
39369 for(var i =0;i < ti.length;i++) {
39370 // Roo.log(['add child', items[i]]);
39371 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39373 this.toolbar.items = nitems;
39374 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39375 delete config.toolbar;
39379 // xtype created footer. - not sure if will work as we normally have to render first..
39380 if (this.footer && !this.footer.el && this.footer.xtype) {
39381 if (!this.wrapEl) {
39382 this.wrapEl = this.el.wrap();
39385 this.footer.container = this.wrapEl.createChild();
39387 this.footer = Roo.factory(this.footer, Roo);
39392 if(typeof config == "string"){
39393 this.title = config;
39395 Roo.apply(this, config);
39399 this.resizeEl = Roo.get(this.resizeEl, true);
39401 this.resizeEl = this.el;
39403 // handle view.xtype
39411 * Fires when this panel is activated.
39412 * @param {Roo.ContentPanel} this
39416 * @event deactivate
39417 * Fires when this panel is activated.
39418 * @param {Roo.ContentPanel} this
39420 "deactivate" : true,
39424 * Fires when this panel is resized if fitToFrame is true.
39425 * @param {Roo.ContentPanel} this
39426 * @param {Number} width The width after any component adjustments
39427 * @param {Number} height The height after any component adjustments
39433 * Fires when this tab is created
39434 * @param {Roo.ContentPanel} this
39445 if(this.autoScroll){
39446 this.resizeEl.setStyle("overflow", "auto");
39448 // fix randome scrolling
39449 //this.el.on('scroll', function() {
39450 // Roo.log('fix random scolling');
39451 // this.scrollTo('top',0);
39454 content = content || this.content;
39456 this.setContent(content);
39458 if(config && config.url){
39459 this.setUrl(this.url, this.params, this.loadOnce);
39464 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39466 if (this.view && typeof(this.view.xtype) != 'undefined') {
39467 this.view.el = this.el.appendChild(document.createElement("div"));
39468 this.view = Roo.factory(this.view);
39469 this.view.render && this.view.render(false, '');
39473 this.fireEvent('render', this);
39476 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39483 setRegion : function(region){
39484 this.region = region;
39485 this.setActiveClass(region && !this.background);
39489 setActiveClass: function(state)
39492 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39493 this.el.setStyle('position','relative');
39495 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39496 this.el.setStyle('position', 'absolute');
39501 * Returns the toolbar for this Panel if one was configured.
39502 * @return {Roo.Toolbar}
39504 getToolbar : function(){
39505 return this.toolbar;
39508 setActiveState : function(active)
39510 this.active = active;
39511 this.setActiveClass(active);
39513 if(this.fireEvent("deactivate", this) === false){
39518 this.fireEvent("activate", this);
39522 * Updates this panel's element
39523 * @param {String} content The new content
39524 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39526 setContent : function(content, loadScripts){
39527 this.el.update(content, loadScripts);
39530 ignoreResize : function(w, h){
39531 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39534 this.lastSize = {width: w, height: h};
39539 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39540 * @return {Roo.UpdateManager} The UpdateManager
39542 getUpdateManager : function(){
39543 return this.el.getUpdateManager();
39546 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39547 * @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:
39550 url: "your-url.php",
39551 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39552 callback: yourFunction,
39553 scope: yourObject, //(optional scope)
39556 text: "Loading...",
39561 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39562 * 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.
39563 * @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}
39564 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39565 * @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.
39566 * @return {Roo.ContentPanel} this
39569 var um = this.el.getUpdateManager();
39570 um.update.apply(um, arguments);
39576 * 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.
39577 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39578 * @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)
39579 * @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)
39580 * @return {Roo.UpdateManager} The UpdateManager
39582 setUrl : function(url, params, loadOnce){
39583 if(this.refreshDelegate){
39584 this.removeListener("activate", this.refreshDelegate);
39586 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39587 this.on("activate", this.refreshDelegate);
39588 return this.el.getUpdateManager();
39591 _handleRefresh : function(url, params, loadOnce){
39592 if(!loadOnce || !this.loaded){
39593 var updater = this.el.getUpdateManager();
39594 updater.update(url, params, this._setLoaded.createDelegate(this));
39598 _setLoaded : function(){
39599 this.loaded = true;
39603 * Returns this panel's id
39606 getId : function(){
39611 * Returns this panel's element - used by regiosn to add.
39612 * @return {Roo.Element}
39614 getEl : function(){
39615 return this.wrapEl || this.el;
39620 adjustForComponents : function(width, height)
39622 //Roo.log('adjustForComponents ');
39623 if(this.resizeEl != this.el){
39624 width -= this.el.getFrameWidth('lr');
39625 height -= this.el.getFrameWidth('tb');
39628 var te = this.toolbar.getEl();
39629 te.setWidth(width);
39630 height -= te.getHeight();
39633 var te = this.footer.getEl();
39634 te.setWidth(width);
39635 height -= te.getHeight();
39639 if(this.adjustments){
39640 width += this.adjustments[0];
39641 height += this.adjustments[1];
39643 return {"width": width, "height": height};
39646 setSize : function(width, height){
39647 if(this.fitToFrame && !this.ignoreResize(width, height)){
39648 if(this.fitContainer && this.resizeEl != this.el){
39649 this.el.setSize(width, height);
39651 var size = this.adjustForComponents(width, height);
39652 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39653 this.fireEvent('resize', this, size.width, size.height);
39658 * Returns this panel's title
39661 getTitle : function(){
39663 if (typeof(this.title) != 'object') {
39668 for (var k in this.title) {
39669 if (!this.title.hasOwnProperty(k)) {
39673 if (k.indexOf('-') >= 0) {
39674 var s = k.split('-');
39675 for (var i = 0; i<s.length; i++) {
39676 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39679 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39686 * Set this panel's title
39687 * @param {String} title
39689 setTitle : function(title){
39690 this.title = title;
39692 this.region.updatePanelTitle(this, title);
39697 * Returns true is this panel was configured to be closable
39698 * @return {Boolean}
39700 isClosable : function(){
39701 return this.closable;
39704 beforeSlide : function(){
39706 this.resizeEl.clip();
39709 afterSlide : function(){
39711 this.resizeEl.unclip();
39715 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39716 * Will fail silently if the {@link #setUrl} method has not been called.
39717 * This does not activate the panel, just updates its content.
39719 refresh : function(){
39720 if(this.refreshDelegate){
39721 this.loaded = false;
39722 this.refreshDelegate();
39727 * Destroys this panel
39729 destroy : function(){
39730 this.el.removeAllListeners();
39731 var tempEl = document.createElement("span");
39732 tempEl.appendChild(this.el.dom);
39733 tempEl.innerHTML = "";
39739 * form - if the content panel contains a form - this is a reference to it.
39740 * @type {Roo.form.Form}
39744 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39745 * This contains a reference to it.
39751 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39761 * @param {Object} cfg Xtype definition of item to add.
39765 getChildContainer: function () {
39766 return this.getEl();
39771 var ret = new Roo.factory(cfg);
39776 if (cfg.xtype.match(/^Form$/)) {
39779 //if (this.footer) {
39780 // el = this.footer.container.insertSibling(false, 'before');
39782 el = this.el.createChild();
39785 this.form = new Roo.form.Form(cfg);
39788 if ( this.form.allItems.length) {
39789 this.form.render(el.dom);
39793 // should only have one of theses..
39794 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39795 // views.. should not be just added - used named prop 'view''
39797 cfg.el = this.el.appendChild(document.createElement("div"));
39800 var ret = new Roo.factory(cfg);
39802 ret.render && ret.render(false, ''); // render blank..
39812 * @class Roo.bootstrap.panel.Grid
39813 * @extends Roo.bootstrap.panel.Content
39815 * Create a new GridPanel.
39816 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39817 * @param {Object} config A the config object
39823 Roo.bootstrap.panel.Grid = function(config)
39827 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39828 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39830 config.el = this.wrapper;
39831 //this.el = this.wrapper;
39833 if (config.container) {
39834 // ctor'ed from a Border/panel.grid
39837 this.wrapper.setStyle("overflow", "hidden");
39838 this.wrapper.addClass('roo-grid-container');
39843 if(config.toolbar){
39844 var tool_el = this.wrapper.createChild();
39845 this.toolbar = Roo.factory(config.toolbar);
39847 if (config.toolbar.items) {
39848 ti = config.toolbar.items ;
39849 delete config.toolbar.items ;
39853 this.toolbar.render(tool_el);
39854 for(var i =0;i < ti.length;i++) {
39855 // Roo.log(['add child', items[i]]);
39856 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39858 this.toolbar.items = nitems;
39860 delete config.toolbar;
39863 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39864 config.grid.scrollBody = true;;
39865 config.grid.monitorWindowResize = false; // turn off autosizing
39866 config.grid.autoHeight = false;
39867 config.grid.autoWidth = false;
39869 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39871 if (config.background) {
39872 // render grid on panel activation (if panel background)
39873 this.on('activate', function(gp) {
39874 if (!gp.grid.rendered) {
39875 gp.grid.render(this.wrapper);
39876 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39881 this.grid.render(this.wrapper);
39882 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39885 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39886 // ??? needed ??? config.el = this.wrapper;
39891 // xtype created footer. - not sure if will work as we normally have to render first..
39892 if (this.footer && !this.footer.el && this.footer.xtype) {
39894 var ctr = this.grid.getView().getFooterPanel(true);
39895 this.footer.dataSource = this.grid.dataSource;
39896 this.footer = Roo.factory(this.footer, Roo);
39897 this.footer.render(ctr);
39907 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39908 getId : function(){
39909 return this.grid.id;
39913 * Returns the grid for this panel
39914 * @return {Roo.bootstrap.Table}
39916 getGrid : function(){
39920 setSize : function(width, height){
39921 if(!this.ignoreResize(width, height)){
39922 var grid = this.grid;
39923 var size = this.adjustForComponents(width, height);
39924 // tfoot is not a footer?
39927 var gridel = grid.getGridEl();
39928 gridel.setSize(size.width, size.height);
39930 var tbd = grid.getGridEl().select('tbody', true).first();
39931 var thd = grid.getGridEl().select('thead',true).first();
39932 var tbf= grid.getGridEl().select('tfoot', true).first();
39935 size.height -= thd.getHeight();
39938 size.height -= thd.getHeight();
39941 tbd.setSize(size.width, size.height );
39942 // this is for the account management tab -seems to work there.
39943 var thd = grid.getGridEl().select('thead',true).first();
39945 // tbd.setSize(size.width, size.height - thd.getHeight());
39954 beforeSlide : function(){
39955 this.grid.getView().scroller.clip();
39958 afterSlide : function(){
39959 this.grid.getView().scroller.unclip();
39962 destroy : function(){
39963 this.grid.destroy();
39965 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39970 * @class Roo.bootstrap.panel.Nest
39971 * @extends Roo.bootstrap.panel.Content
39973 * Create a new Panel, that can contain a layout.Border.
39976 * @param {Roo.BorderLayout} layout The layout for this panel
39977 * @param {String/Object} config A string to set only the title or a config object
39979 Roo.bootstrap.panel.Nest = function(config)
39981 // construct with only one argument..
39982 /* FIXME - implement nicer consturctors
39983 if (layout.layout) {
39985 layout = config.layout;
39986 delete config.layout;
39988 if (layout.xtype && !layout.getEl) {
39989 // then layout needs constructing..
39990 layout = Roo.factory(layout, Roo);
39994 config.el = config.layout.getEl();
39996 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39998 config.layout.monitorWindowResize = false; // turn off autosizing
39999 this.layout = config.layout;
40000 this.layout.getEl().addClass("roo-layout-nested-layout");
40001 this.layout.parent = this;
40008 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40010 setSize : function(width, height){
40011 if(!this.ignoreResize(width, height)){
40012 var size = this.adjustForComponents(width, height);
40013 var el = this.layout.getEl();
40014 if (size.height < 1) {
40015 el.setWidth(size.width);
40017 el.setSize(size.width, size.height);
40019 var touch = el.dom.offsetWidth;
40020 this.layout.layout();
40021 // ie requires a double layout on the first pass
40022 if(Roo.isIE && !this.initialized){
40023 this.initialized = true;
40024 this.layout.layout();
40029 // activate all subpanels if not currently active..
40031 setActiveState : function(active){
40032 this.active = active;
40033 this.setActiveClass(active);
40036 this.fireEvent("deactivate", this);
40040 this.fireEvent("activate", this);
40041 // not sure if this should happen before or after..
40042 if (!this.layout) {
40043 return; // should not happen..
40046 for (var r in this.layout.regions) {
40047 reg = this.layout.getRegion(r);
40048 if (reg.getActivePanel()) {
40049 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40050 reg.setActivePanel(reg.getActivePanel());
40053 if (!reg.panels.length) {
40056 reg.showPanel(reg.getPanel(0));
40065 * Returns the nested BorderLayout for this panel
40066 * @return {Roo.BorderLayout}
40068 getLayout : function(){
40069 return this.layout;
40073 * Adds a xtype elements to the layout of the nested panel
40077 xtype : 'ContentPanel',
40084 xtype : 'NestedLayoutPanel',
40090 items : [ ... list of content panels or nested layout panels.. ]
40094 * @param {Object} cfg Xtype definition of item to add.
40096 addxtype : function(cfg) {
40097 return this.layout.addxtype(cfg);
40102 * Ext JS Library 1.1.1
40103 * Copyright(c) 2006-2007, Ext JS, LLC.
40105 * Originally Released Under LGPL - original licence link has changed is not relivant.
40108 * <script type="text/javascript">
40111 * @class Roo.TabPanel
40112 * @extends Roo.util.Observable
40113 * A lightweight tab container.
40117 // basic tabs 1, built from existing content
40118 var tabs = new Roo.TabPanel("tabs1");
40119 tabs.addTab("script", "View Script");
40120 tabs.addTab("markup", "View Markup");
40121 tabs.activate("script");
40123 // more advanced tabs, built from javascript
40124 var jtabs = new Roo.TabPanel("jtabs");
40125 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40127 // set up the UpdateManager
40128 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40129 var updater = tab2.getUpdateManager();
40130 updater.setDefaultUrl("ajax1.htm");
40131 tab2.on('activate', updater.refresh, updater, true);
40133 // Use setUrl for Ajax loading
40134 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40135 tab3.setUrl("ajax2.htm", null, true);
40138 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40141 jtabs.activate("jtabs-1");
40144 * Create a new TabPanel.
40145 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40146 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40148 Roo.bootstrap.panel.Tabs = function(config){
40150 * The container element for this TabPanel.
40151 * @type Roo.Element
40153 this.el = Roo.get(config.el);
40156 if(typeof config == "boolean"){
40157 this.tabPosition = config ? "bottom" : "top";
40159 Roo.apply(this, config);
40163 if(this.tabPosition == "bottom"){
40164 // if tabs are at the bottom = create the body first.
40165 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40166 this.el.addClass("roo-tabs-bottom");
40168 // next create the tabs holders
40170 if (this.tabPosition == "west"){
40172 var reg = this.region; // fake it..
40174 if (!reg.mgr.parent) {
40177 reg = reg.mgr.parent.region;
40179 Roo.log("got nest?");
40181 if (reg.mgr.getRegion('west')) {
40182 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40183 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40184 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40185 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40186 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40194 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40195 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40196 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40197 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40202 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40205 // finally - if tabs are at the top, then create the body last..
40206 if(this.tabPosition != "bottom"){
40207 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40208 * @type Roo.Element
40210 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40211 this.el.addClass("roo-tabs-top");
40215 this.bodyEl.setStyle("position", "relative");
40217 this.active = null;
40218 this.activateDelegate = this.activate.createDelegate(this);
40223 * Fires when the active tab changes
40224 * @param {Roo.TabPanel} this
40225 * @param {Roo.TabPanelItem} activePanel The new active tab
40229 * @event beforetabchange
40230 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40231 * @param {Roo.TabPanel} this
40232 * @param {Object} e Set cancel to true on this object to cancel the tab change
40233 * @param {Roo.TabPanelItem} tab The tab being changed to
40235 "beforetabchange" : true
40238 Roo.EventManager.onWindowResize(this.onResize, this);
40239 this.cpad = this.el.getPadding("lr");
40240 this.hiddenCount = 0;
40243 // toolbar on the tabbar support...
40244 if (this.toolbar) {
40245 alert("no toolbar support yet");
40246 this.toolbar = false;
40248 var tcfg = this.toolbar;
40249 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40250 this.toolbar = new Roo.Toolbar(tcfg);
40251 if (Roo.isSafari) {
40252 var tbl = tcfg.container.child('table', true);
40253 tbl.setAttribute('width', '100%');
40261 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40264 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40266 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40268 tabPosition : "top",
40270 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40272 currentTabWidth : 0,
40274 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40278 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40282 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40284 preferredTabWidth : 175,
40286 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40288 resizeTabs : false,
40290 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40292 monitorResize : true,
40294 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40296 toolbar : false, // set by caller..
40298 region : false, /// set by caller
40300 disableTooltips : true, // not used yet...
40303 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40304 * @param {String} id The id of the div to use <b>or create</b>
40305 * @param {String} text The text for the tab
40306 * @param {String} content (optional) Content to put in the TabPanelItem body
40307 * @param {Boolean} closable (optional) True to create a close icon on the tab
40308 * @return {Roo.TabPanelItem} The created TabPanelItem
40310 addTab : function(id, text, content, closable, tpl)
40312 var item = new Roo.bootstrap.panel.TabItem({
40316 closable : closable,
40319 this.addTabItem(item);
40321 item.setContent(content);
40327 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40328 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40329 * @return {Roo.TabPanelItem}
40331 getTab : function(id){
40332 return this.items[id];
40336 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40337 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40339 hideTab : function(id){
40340 var t = this.items[id];
40343 this.hiddenCount++;
40344 this.autoSizeTabs();
40349 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40350 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40352 unhideTab : function(id){
40353 var t = this.items[id];
40355 t.setHidden(false);
40356 this.hiddenCount--;
40357 this.autoSizeTabs();
40362 * Adds an existing {@link Roo.TabPanelItem}.
40363 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40365 addTabItem : function(item)
40367 this.items[item.id] = item;
40368 this.items.push(item);
40369 this.autoSizeTabs();
40370 // if(this.resizeTabs){
40371 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40372 // this.autoSizeTabs();
40374 // item.autoSize();
40379 * Removes a {@link Roo.TabPanelItem}.
40380 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40382 removeTab : function(id){
40383 var items = this.items;
40384 var tab = items[id];
40385 if(!tab) { return; }
40386 var index = items.indexOf(tab);
40387 if(this.active == tab && items.length > 1){
40388 var newTab = this.getNextAvailable(index);
40393 this.stripEl.dom.removeChild(tab.pnode.dom);
40394 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40395 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40397 items.splice(index, 1);
40398 delete this.items[tab.id];
40399 tab.fireEvent("close", tab);
40400 tab.purgeListeners();
40401 this.autoSizeTabs();
40404 getNextAvailable : function(start){
40405 var items = this.items;
40407 // look for a next tab that will slide over to
40408 // replace the one being removed
40409 while(index < items.length){
40410 var item = items[++index];
40411 if(item && !item.isHidden()){
40415 // if one isn't found select the previous tab (on the left)
40418 var item = items[--index];
40419 if(item && !item.isHidden()){
40427 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40428 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40430 disableTab : function(id){
40431 var tab = this.items[id];
40432 if(tab && this.active != tab){
40438 * Enables a {@link Roo.TabPanelItem} that is disabled.
40439 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40441 enableTab : function(id){
40442 var tab = this.items[id];
40447 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40448 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40449 * @return {Roo.TabPanelItem} The TabPanelItem.
40451 activate : function(id)
40453 //Roo.log('activite:' + id);
40455 var tab = this.items[id];
40459 if(tab == this.active || tab.disabled){
40463 this.fireEvent("beforetabchange", this, e, tab);
40464 if(e.cancel !== true && !tab.disabled){
40466 this.active.hide();
40468 this.active = this.items[id];
40469 this.active.show();
40470 this.fireEvent("tabchange", this, this.active);
40476 * Gets the active {@link Roo.TabPanelItem}.
40477 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40479 getActiveTab : function(){
40480 return this.active;
40484 * Updates the tab body element to fit the height of the container element
40485 * for overflow scrolling
40486 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40488 syncHeight : function(targetHeight){
40489 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40490 var bm = this.bodyEl.getMargins();
40491 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40492 this.bodyEl.setHeight(newHeight);
40496 onResize : function(){
40497 if(this.monitorResize){
40498 this.autoSizeTabs();
40503 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40505 beginUpdate : function(){
40506 this.updating = true;
40510 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40512 endUpdate : function(){
40513 this.updating = false;
40514 this.autoSizeTabs();
40518 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40520 autoSizeTabs : function()
40522 var count = this.items.length;
40523 var vcount = count - this.hiddenCount;
40526 this.stripEl.hide();
40528 this.stripEl.show();
40531 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40536 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40537 var availWidth = Math.floor(w / vcount);
40538 var b = this.stripBody;
40539 if(b.getWidth() > w){
40540 var tabs = this.items;
40541 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40542 if(availWidth < this.minTabWidth){
40543 /*if(!this.sleft){ // incomplete scrolling code
40544 this.createScrollButtons();
40547 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40550 if(this.currentTabWidth < this.preferredTabWidth){
40551 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40557 * Returns the number of tabs in this TabPanel.
40560 getCount : function(){
40561 return this.items.length;
40565 * Resizes all the tabs to the passed width
40566 * @param {Number} The new width
40568 setTabWidth : function(width){
40569 this.currentTabWidth = width;
40570 for(var i = 0, len = this.items.length; i < len; i++) {
40571 if(!this.items[i].isHidden()) {
40572 this.items[i].setWidth(width);
40578 * Destroys this TabPanel
40579 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40581 destroy : function(removeEl){
40582 Roo.EventManager.removeResizeListener(this.onResize, this);
40583 for(var i = 0, len = this.items.length; i < len; i++){
40584 this.items[i].purgeListeners();
40586 if(removeEl === true){
40587 this.el.update("");
40592 createStrip : function(container)
40594 var strip = document.createElement("nav");
40595 strip.className = Roo.bootstrap.version == 4 ?
40596 "navbar-light bg-light" :
40597 "navbar navbar-default"; //"x-tabs-wrap";
40598 container.appendChild(strip);
40602 createStripList : function(strip)
40604 // div wrapper for retard IE
40605 // returns the "tr" element.
40606 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40607 //'<div class="x-tabs-strip-wrap">'+
40608 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40609 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40610 return strip.firstChild; //.firstChild.firstChild.firstChild;
40612 createBody : function(container)
40614 var body = document.createElement("div");
40615 Roo.id(body, "tab-body");
40616 //Roo.fly(body).addClass("x-tabs-body");
40617 Roo.fly(body).addClass("tab-content");
40618 container.appendChild(body);
40621 createItemBody :function(bodyEl, id){
40622 var body = Roo.getDom(id);
40624 body = document.createElement("div");
40627 //Roo.fly(body).addClass("x-tabs-item-body");
40628 Roo.fly(body).addClass("tab-pane");
40629 bodyEl.insertBefore(body, bodyEl.firstChild);
40633 createStripElements : function(stripEl, text, closable, tpl)
40635 var td = document.createElement("li"); // was td..
40636 td.className = 'nav-item';
40638 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40641 stripEl.appendChild(td);
40643 td.className = "x-tabs-closable";
40644 if(!this.closeTpl){
40645 this.closeTpl = new Roo.Template(
40646 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40647 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40648 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40651 var el = this.closeTpl.overwrite(td, {"text": text});
40652 var close = el.getElementsByTagName("div")[0];
40653 var inner = el.getElementsByTagName("em")[0];
40654 return {"el": el, "close": close, "inner": inner};
40657 // not sure what this is..
40658 // if(!this.tabTpl){
40659 //this.tabTpl = new Roo.Template(
40660 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40661 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40663 // this.tabTpl = new Roo.Template(
40664 // '<a href="#">' +
40665 // '<span unselectable="on"' +
40666 // (this.disableTooltips ? '' : ' title="{text}"') +
40667 // ' >{text}</span></a>'
40673 var template = tpl || this.tabTpl || false;
40676 template = new Roo.Template(
40677 Roo.bootstrap.version == 4 ?
40679 '<a class="nav-link" href="#" unselectable="on"' +
40680 (this.disableTooltips ? '' : ' title="{text}"') +
40683 '<a class="nav-link" href="#">' +
40684 '<span unselectable="on"' +
40685 (this.disableTooltips ? '' : ' title="{text}"') +
40686 ' >{text}</span></a>'
40691 switch (typeof(template)) {
40695 template = new Roo.Template(template);
40701 var el = template.overwrite(td, {"text": text});
40703 var inner = el.getElementsByTagName("span")[0];
40705 return {"el": el, "inner": inner};
40713 * @class Roo.TabPanelItem
40714 * @extends Roo.util.Observable
40715 * Represents an individual item (tab plus body) in a TabPanel.
40716 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40717 * @param {String} id The id of this TabPanelItem
40718 * @param {String} text The text for the tab of this TabPanelItem
40719 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40721 Roo.bootstrap.panel.TabItem = function(config){
40723 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40724 * @type Roo.TabPanel
40726 this.tabPanel = config.panel;
40728 * The id for this TabPanelItem
40731 this.id = config.id;
40733 this.disabled = false;
40735 this.text = config.text;
40737 this.loaded = false;
40738 this.closable = config.closable;
40741 * The body element for this TabPanelItem.
40742 * @type Roo.Element
40744 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40745 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40746 this.bodyEl.setStyle("display", "block");
40747 this.bodyEl.setStyle("zoom", "1");
40748 //this.hideAction();
40750 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40752 this.el = Roo.get(els.el);
40753 this.inner = Roo.get(els.inner, true);
40754 this.textEl = Roo.bootstrap.version == 4 ?
40755 this.el : Roo.get(this.el.dom.firstChild, true);
40757 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40758 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40761 // this.el.on("mousedown", this.onTabMouseDown, this);
40762 this.el.on("click", this.onTabClick, this);
40764 if(config.closable){
40765 var c = Roo.get(els.close, true);
40766 c.dom.title = this.closeText;
40767 c.addClassOnOver("close-over");
40768 c.on("click", this.closeClick, this);
40774 * Fires when this tab becomes the active tab.
40775 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40776 * @param {Roo.TabPanelItem} this
40780 * @event beforeclose
40781 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40782 * @param {Roo.TabPanelItem} this
40783 * @param {Object} e Set cancel to true on this object to cancel the close.
40785 "beforeclose": true,
40788 * Fires when this tab is closed.
40789 * @param {Roo.TabPanelItem} this
40793 * @event deactivate
40794 * Fires when this tab is no longer the active tab.
40795 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40796 * @param {Roo.TabPanelItem} this
40798 "deactivate" : true
40800 this.hidden = false;
40802 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40807 purgeListeners : function(){
40808 Roo.util.Observable.prototype.purgeListeners.call(this);
40809 this.el.removeAllListeners();
40812 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40815 this.status_node.addClass("active");
40818 this.tabPanel.stripWrap.repaint();
40820 this.fireEvent("activate", this.tabPanel, this);
40824 * Returns true if this tab is the active tab.
40825 * @return {Boolean}
40827 isActive : function(){
40828 return this.tabPanel.getActiveTab() == this;
40832 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40835 this.status_node.removeClass("active");
40837 this.fireEvent("deactivate", this.tabPanel, this);
40840 hideAction : function(){
40841 this.bodyEl.hide();
40842 this.bodyEl.setStyle("position", "absolute");
40843 this.bodyEl.setLeft("-20000px");
40844 this.bodyEl.setTop("-20000px");
40847 showAction : function(){
40848 this.bodyEl.setStyle("position", "relative");
40849 this.bodyEl.setTop("");
40850 this.bodyEl.setLeft("");
40851 this.bodyEl.show();
40855 * Set the tooltip for the tab.
40856 * @param {String} tooltip The tab's tooltip
40858 setTooltip : function(text){
40859 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40860 this.textEl.dom.qtip = text;
40861 this.textEl.dom.removeAttribute('title');
40863 this.textEl.dom.title = text;
40867 onTabClick : function(e){
40868 e.preventDefault();
40869 this.tabPanel.activate(this.id);
40872 onTabMouseDown : function(e){
40873 e.preventDefault();
40874 this.tabPanel.activate(this.id);
40877 getWidth : function(){
40878 return this.inner.getWidth();
40881 setWidth : function(width){
40882 var iwidth = width - this.linode.getPadding("lr");
40883 this.inner.setWidth(iwidth);
40884 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40885 this.linode.setWidth(width);
40889 * Show or hide the tab
40890 * @param {Boolean} hidden True to hide or false to show.
40892 setHidden : function(hidden){
40893 this.hidden = hidden;
40894 this.linode.setStyle("display", hidden ? "none" : "");
40898 * Returns true if this tab is "hidden"
40899 * @return {Boolean}
40901 isHidden : function(){
40902 return this.hidden;
40906 * Returns the text for this tab
40909 getText : function(){
40913 autoSize : function(){
40914 //this.el.beginMeasure();
40915 this.textEl.setWidth(1);
40917 * #2804 [new] Tabs in Roojs
40918 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40920 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40921 //this.el.endMeasure();
40925 * Sets the text for the tab (Note: this also sets the tooltip text)
40926 * @param {String} text The tab's text and tooltip
40928 setText : function(text){
40930 this.textEl.update(text);
40931 this.setTooltip(text);
40932 //if(!this.tabPanel.resizeTabs){
40933 // this.autoSize();
40937 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40939 activate : function(){
40940 this.tabPanel.activate(this.id);
40944 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40946 disable : function(){
40947 if(this.tabPanel.active != this){
40948 this.disabled = true;
40949 this.status_node.addClass("disabled");
40954 * Enables this TabPanelItem if it was previously disabled.
40956 enable : function(){
40957 this.disabled = false;
40958 this.status_node.removeClass("disabled");
40962 * Sets the content for this TabPanelItem.
40963 * @param {String} content The content
40964 * @param {Boolean} loadScripts true to look for and load scripts
40966 setContent : function(content, loadScripts){
40967 this.bodyEl.update(content, loadScripts);
40971 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40972 * @return {Roo.UpdateManager} The UpdateManager
40974 getUpdateManager : function(){
40975 return this.bodyEl.getUpdateManager();
40979 * Set a URL to be used to load the content for this TabPanelItem.
40980 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40981 * @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)
40982 * @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)
40983 * @return {Roo.UpdateManager} The UpdateManager
40985 setUrl : function(url, params, loadOnce){
40986 if(this.refreshDelegate){
40987 this.un('activate', this.refreshDelegate);
40989 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40990 this.on("activate", this.refreshDelegate);
40991 return this.bodyEl.getUpdateManager();
40995 _handleRefresh : function(url, params, loadOnce){
40996 if(!loadOnce || !this.loaded){
40997 var updater = this.bodyEl.getUpdateManager();
40998 updater.update(url, params, this._setLoaded.createDelegate(this));
41003 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41004 * Will fail silently if the setUrl method has not been called.
41005 * This does not activate the panel, just updates its content.
41007 refresh : function(){
41008 if(this.refreshDelegate){
41009 this.loaded = false;
41010 this.refreshDelegate();
41015 _setLoaded : function(){
41016 this.loaded = true;
41020 closeClick : function(e){
41023 this.fireEvent("beforeclose", this, o);
41024 if(o.cancel !== true){
41025 this.tabPanel.removeTab(this.id);
41029 * The text displayed in the tooltip for the close icon.
41032 closeText : "Close this tab"
41035 * This script refer to:
41036 * Title: International Telephone Input
41037 * Author: Jack O'Connor
41038 * Code version: v12.1.12
41039 * Availability: https://github.com/jackocnr/intl-tel-input.git
41042 Roo.bootstrap.PhoneInputData = function() {
41045 "Afghanistan (افغانستان)",
41050 "Albania (Shqipëri)",
41055 "Algeria (الجزائر)",
41080 "Antigua and Barbuda",
41090 "Armenia (Հայաստան)",
41106 "Austria (Österreich)",
41111 "Azerbaijan (Azərbaycan)",
41121 "Bahrain (البحرين)",
41126 "Bangladesh (বাংলাদেশ)",
41136 "Belarus (Беларусь)",
41141 "Belgium (België)",
41171 "Bosnia and Herzegovina (Босна и Херцеговина)",
41186 "British Indian Ocean Territory",
41191 "British Virgin Islands",
41201 "Bulgaria (България)",
41211 "Burundi (Uburundi)",
41216 "Cambodia (កម្ពុជា)",
41221 "Cameroon (Cameroun)",
41230 ["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"]
41233 "Cape Verde (Kabu Verdi)",
41238 "Caribbean Netherlands",
41249 "Central African Republic (République centrafricaine)",
41269 "Christmas Island",
41275 "Cocos (Keeling) Islands",
41286 "Comoros (جزر القمر)",
41291 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41296 "Congo (Republic) (Congo-Brazzaville)",
41316 "Croatia (Hrvatska)",
41337 "Czech Republic (Česká republika)",
41342 "Denmark (Danmark)",
41357 "Dominican Republic (República Dominicana)",
41361 ["809", "829", "849"]
41379 "Equatorial Guinea (Guinea Ecuatorial)",
41399 "Falkland Islands (Islas Malvinas)",
41404 "Faroe Islands (Føroyar)",
41425 "French Guiana (Guyane française)",
41430 "French Polynesia (Polynésie française)",
41445 "Georgia (საქართველო)",
41450 "Germany (Deutschland)",
41470 "Greenland (Kalaallit Nunaat)",
41507 "Guinea-Bissau (Guiné Bissau)",
41532 "Hungary (Magyarország)",
41537 "Iceland (Ísland)",
41557 "Iraq (العراق)",
41573 "Israel (ישראל)",
41600 "Jordan (الأردن)",
41605 "Kazakhstan (Казахстан)",
41626 "Kuwait (الكويت)",
41631 "Kyrgyzstan (Кыргызстан)",
41641 "Latvia (Latvija)",
41646 "Lebanon (لبنان)",
41661 "Libya (ليبيا)",
41671 "Lithuania (Lietuva)",
41686 "Macedonia (FYROM) (Македонија)",
41691 "Madagascar (Madagasikara)",
41721 "Marshall Islands",
41731 "Mauritania (موريتانيا)",
41736 "Mauritius (Moris)",
41757 "Moldova (Republica Moldova)",
41767 "Mongolia (Монгол)",
41772 "Montenegro (Crna Gora)",
41782 "Morocco (المغرب)",
41788 "Mozambique (Moçambique)",
41793 "Myanmar (Burma) (မြန်မာ)",
41798 "Namibia (Namibië)",
41813 "Netherlands (Nederland)",
41818 "New Caledonia (Nouvelle-Calédonie)",
41853 "North Korea (조선 민주주의 인민 공화국)",
41858 "Northern Mariana Islands",
41874 "Pakistan (پاکستان)",
41884 "Palestine (فلسطين)",
41894 "Papua New Guinea",
41936 "Réunion (La Réunion)",
41942 "Romania (România)",
41958 "Saint Barthélemy",
41969 "Saint Kitts and Nevis",
41979 "Saint Martin (Saint-Martin (partie française))",
41985 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41990 "Saint Vincent and the Grenadines",
42005 "São Tomé and Príncipe (São Tomé e Príncipe)",
42010 "Saudi Arabia (المملكة العربية السعودية)",
42015 "Senegal (Sénégal)",
42045 "Slovakia (Slovensko)",
42050 "Slovenia (Slovenija)",
42060 "Somalia (Soomaaliya)",
42070 "South Korea (대한민국)",
42075 "South Sudan (جنوب السودان)",
42085 "Sri Lanka (ශ්රී ලංකාව)",
42090 "Sudan (السودان)",
42100 "Svalbard and Jan Mayen",
42111 "Sweden (Sverige)",
42116 "Switzerland (Schweiz)",
42121 "Syria (سوريا)",
42166 "Trinidad and Tobago",
42171 "Tunisia (تونس)",
42176 "Turkey (Türkiye)",
42186 "Turks and Caicos Islands",
42196 "U.S. Virgin Islands",
42206 "Ukraine (Україна)",
42211 "United Arab Emirates (الإمارات العربية المتحدة)",
42233 "Uzbekistan (Oʻzbekiston)",
42243 "Vatican City (Città del Vaticano)",
42254 "Vietnam (Việt Nam)",
42259 "Wallis and Futuna (Wallis-et-Futuna)",
42264 "Western Sahara (الصحراء الغربية)",
42270 "Yemen (اليمن)",
42294 * This script refer to:
42295 * Title: International Telephone Input
42296 * Author: Jack O'Connor
42297 * Code version: v12.1.12
42298 * Availability: https://github.com/jackocnr/intl-tel-input.git
42302 * @class Roo.bootstrap.PhoneInput
42303 * @extends Roo.bootstrap.TriggerField
42304 * An input with International dial-code selection
42306 * @cfg {String} defaultDialCode default '+852'
42307 * @cfg {Array} preferedCountries default []
42310 * Create a new PhoneInput.
42311 * @param {Object} config Configuration options
42314 Roo.bootstrap.PhoneInput = function(config) {
42315 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42318 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42320 listWidth: undefined,
42322 selectedClass: 'active',
42324 invalidClass : "has-warning",
42326 validClass: 'has-success',
42328 allowed: '0123456789',
42333 * @cfg {String} defaultDialCode The default dial code when initializing the input
42335 defaultDialCode: '+852',
42338 * @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
42340 preferedCountries: false,
42342 getAutoCreate : function()
42344 var data = Roo.bootstrap.PhoneInputData();
42345 var align = this.labelAlign || this.parentLabelAlign();
42348 this.allCountries = [];
42349 this.dialCodeMapping = [];
42351 for (var i = 0; i < data.length; i++) {
42353 this.allCountries[i] = {
42357 priority: c[3] || 0,
42358 areaCodes: c[4] || null
42360 this.dialCodeMapping[c[2]] = {
42363 priority: c[3] || 0,
42364 areaCodes: c[4] || null
42376 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42377 maxlength: this.max_length,
42378 cls : 'form-control tel-input',
42379 autocomplete: 'new-password'
42382 var hiddenInput = {
42385 cls: 'hidden-tel-input'
42389 hiddenInput.name = this.name;
42392 if (this.disabled) {
42393 input.disabled = true;
42396 var flag_container = {
42413 cls: this.hasFeedback ? 'has-feedback' : '',
42419 cls: 'dial-code-holder',
42426 cls: 'roo-select2-container input-group',
42433 if (this.fieldLabel.length) {
42436 tooltip: 'This field is required'
42442 cls: 'control-label',
42448 html: this.fieldLabel
42451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42457 if(this.indicatorpos == 'right') {
42458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42465 if(align == 'left') {
42473 if(this.labelWidth > 12){
42474 label.style = "width: " + this.labelWidth + 'px';
42476 if(this.labelWidth < 13 && this.labelmd == 0){
42477 this.labelmd = this.labelWidth;
42479 if(this.labellg > 0){
42480 label.cls += ' col-lg-' + this.labellg;
42481 input.cls += ' col-lg-' + (12 - this.labellg);
42483 if(this.labelmd > 0){
42484 label.cls += ' col-md-' + this.labelmd;
42485 container.cls += ' col-md-' + (12 - this.labelmd);
42487 if(this.labelsm > 0){
42488 label.cls += ' col-sm-' + this.labelsm;
42489 container.cls += ' col-sm-' + (12 - this.labelsm);
42491 if(this.labelxs > 0){
42492 label.cls += ' col-xs-' + this.labelxs;
42493 container.cls += ' col-xs-' + (12 - this.labelxs);
42503 var settings = this;
42505 ['xs','sm','md','lg'].map(function(size){
42506 if (settings[size]) {
42507 cfg.cls += ' col-' + size + '-' + settings[size];
42511 this.store = new Roo.data.Store({
42512 proxy : new Roo.data.MemoryProxy({}),
42513 reader : new Roo.data.JsonReader({
42524 'name' : 'dialCode',
42528 'name' : 'priority',
42532 'name' : 'areaCodes',
42539 if(!this.preferedCountries) {
42540 this.preferedCountries = [
42547 var p = this.preferedCountries.reverse();
42550 for (var i = 0; i < p.length; i++) {
42551 for (var j = 0; j < this.allCountries.length; j++) {
42552 if(this.allCountries[j].iso2 == p[i]) {
42553 var t = this.allCountries[j];
42554 this.allCountries.splice(j,1);
42555 this.allCountries.unshift(t);
42561 this.store.proxy.data = {
42563 data: this.allCountries
42569 initEvents : function()
42572 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42574 this.indicator = this.indicatorEl();
42575 this.flag = this.flagEl();
42576 this.dialCodeHolder = this.dialCodeHolderEl();
42578 this.trigger = this.el.select('div.flag-box',true).first();
42579 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42584 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42585 _this.list.setWidth(lw);
42588 this.list.on('mouseover', this.onViewOver, this);
42589 this.list.on('mousemove', this.onViewMove, this);
42590 this.inputEl().on("keyup", this.onKeyUp, this);
42591 this.inputEl().on("keypress", this.onKeyPress, this);
42593 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42595 this.view = new Roo.View(this.list, this.tpl, {
42596 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42599 this.view.on('click', this.onViewClick, this);
42600 this.setValue(this.defaultDialCode);
42603 onTriggerClick : function(e)
42605 Roo.log('trigger click');
42610 if(this.isExpanded()){
42612 this.hasFocus = false;
42614 this.store.load({});
42615 this.hasFocus = true;
42620 isExpanded : function()
42622 return this.list.isVisible();
42625 collapse : function()
42627 if(!this.isExpanded()){
42631 Roo.get(document).un('mousedown', this.collapseIf, this);
42632 Roo.get(document).un('mousewheel', this.collapseIf, this);
42633 this.fireEvent('collapse', this);
42637 expand : function()
42641 if(this.isExpanded() || !this.hasFocus){
42645 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42646 this.list.setWidth(lw);
42649 this.restrictHeight();
42651 Roo.get(document).on('mousedown', this.collapseIf, this);
42652 Roo.get(document).on('mousewheel', this.collapseIf, this);
42654 this.fireEvent('expand', this);
42657 restrictHeight : function()
42659 this.list.alignTo(this.inputEl(), this.listAlign);
42660 this.list.alignTo(this.inputEl(), this.listAlign);
42663 onViewOver : function(e, t)
42665 if(this.inKeyMode){
42668 var item = this.view.findItemFromChild(t);
42671 var index = this.view.indexOf(item);
42672 this.select(index, false);
42677 onViewClick : function(view, doFocus, el, e)
42679 var index = this.view.getSelectedIndexes()[0];
42681 var r = this.store.getAt(index);
42684 this.onSelect(r, index);
42686 if(doFocus !== false && !this.blockFocus){
42687 this.inputEl().focus();
42691 onViewMove : function(e, t)
42693 this.inKeyMode = false;
42696 select : function(index, scrollIntoView)
42698 this.selectedIndex = index;
42699 this.view.select(index);
42700 if(scrollIntoView !== false){
42701 var el = this.view.getNode(index);
42703 this.list.scrollChildIntoView(el, false);
42708 createList : function()
42710 this.list = Roo.get(document.body).createChild({
42712 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42713 style: 'display:none'
42716 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42719 collapseIf : function(e)
42721 var in_combo = e.within(this.el);
42722 var in_list = e.within(this.list);
42723 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42725 if (in_combo || in_list || is_list) {
42731 onSelect : function(record, index)
42733 if(this.fireEvent('beforeselect', this, record, index) !== false){
42735 this.setFlagClass(record.data.iso2);
42736 this.setDialCode(record.data.dialCode);
42737 this.hasFocus = false;
42739 this.fireEvent('select', this, record, index);
42743 flagEl : function()
42745 var flag = this.el.select('div.flag',true).first();
42752 dialCodeHolderEl : function()
42754 var d = this.el.select('input.dial-code-holder',true).first();
42761 setDialCode : function(v)
42763 this.dialCodeHolder.dom.value = '+'+v;
42766 setFlagClass : function(n)
42768 this.flag.dom.className = 'flag '+n;
42771 getValue : function()
42773 var v = this.inputEl().getValue();
42774 if(this.dialCodeHolder) {
42775 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42780 setValue : function(v)
42782 var d = this.getDialCode(v);
42784 //invalid dial code
42785 if(v.length == 0 || !d || d.length == 0) {
42787 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42788 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42794 this.setFlagClass(this.dialCodeMapping[d].iso2);
42795 this.setDialCode(d);
42796 this.inputEl().dom.value = v.replace('+'+d,'');
42797 this.hiddenEl().dom.value = this.getValue();
42802 getDialCode : function(v)
42806 if (v.length == 0) {
42807 return this.dialCodeHolder.dom.value;
42811 if (v.charAt(0) != "+") {
42814 var numericChars = "";
42815 for (var i = 1; i < v.length; i++) {
42816 var c = v.charAt(i);
42819 if (this.dialCodeMapping[numericChars]) {
42820 dialCode = v.substr(1, i);
42822 if (numericChars.length == 4) {
42832 this.setValue(this.defaultDialCode);
42836 hiddenEl : function()
42838 return this.el.select('input.hidden-tel-input',true).first();
42841 // after setting val
42842 onKeyUp : function(e){
42843 this.setValue(this.getValue());
42846 onKeyPress : function(e){
42847 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42854 * @class Roo.bootstrap.MoneyField
42855 * @extends Roo.bootstrap.ComboBox
42856 * Bootstrap MoneyField class
42859 * Create a new MoneyField.
42860 * @param {Object} config Configuration options
42863 Roo.bootstrap.MoneyField = function(config) {
42865 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42869 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42872 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42874 allowDecimals : true,
42876 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42878 decimalSeparator : ".",
42880 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42882 decimalPrecision : 0,
42884 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42886 allowNegative : true,
42888 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42892 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42894 minValue : Number.NEGATIVE_INFINITY,
42896 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42898 maxValue : Number.MAX_VALUE,
42900 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42902 minText : "The minimum value for this field is {0}",
42904 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42906 maxText : "The maximum value for this field is {0}",
42908 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42909 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42911 nanText : "{0} is not a valid number",
42913 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42917 * @cfg {String} defaults currency of the MoneyField
42918 * value should be in lkey
42920 defaultCurrency : false,
42922 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42924 thousandsDelimiter : false,
42926 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42937 getAutoCreate : function()
42939 var align = this.labelAlign || this.parentLabelAlign();
42951 cls : 'form-control roo-money-amount-input',
42952 autocomplete: 'new-password'
42955 var hiddenInput = {
42959 cls: 'hidden-number-input'
42962 if(this.max_length) {
42963 input.maxlength = this.max_length;
42967 hiddenInput.name = this.name;
42970 if (this.disabled) {
42971 input.disabled = true;
42974 var clg = 12 - this.inputlg;
42975 var cmd = 12 - this.inputmd;
42976 var csm = 12 - this.inputsm;
42977 var cxs = 12 - this.inputxs;
42981 cls : 'row roo-money-field',
42985 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42989 cls: 'roo-select2-container input-group',
42993 cls : 'form-control roo-money-currency-input',
42994 autocomplete: 'new-password',
42996 name : this.currencyName
43000 cls : 'input-group-addon',
43014 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43018 cls: this.hasFeedback ? 'has-feedback' : '',
43029 if (this.fieldLabel.length) {
43032 tooltip: 'This field is required'
43038 cls: 'control-label',
43044 html: this.fieldLabel
43047 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43053 if(this.indicatorpos == 'right') {
43054 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43061 if(align == 'left') {
43069 if(this.labelWidth > 12){
43070 label.style = "width: " + this.labelWidth + 'px';
43072 if(this.labelWidth < 13 && this.labelmd == 0){
43073 this.labelmd = this.labelWidth;
43075 if(this.labellg > 0){
43076 label.cls += ' col-lg-' + this.labellg;
43077 input.cls += ' col-lg-' + (12 - this.labellg);
43079 if(this.labelmd > 0){
43080 label.cls += ' col-md-' + this.labelmd;
43081 container.cls += ' col-md-' + (12 - this.labelmd);
43083 if(this.labelsm > 0){
43084 label.cls += ' col-sm-' + this.labelsm;
43085 container.cls += ' col-sm-' + (12 - this.labelsm);
43087 if(this.labelxs > 0){
43088 label.cls += ' col-xs-' + this.labelxs;
43089 container.cls += ' col-xs-' + (12 - this.labelxs);
43100 var settings = this;
43102 ['xs','sm','md','lg'].map(function(size){
43103 if (settings[size]) {
43104 cfg.cls += ' col-' + size + '-' + settings[size];
43111 initEvents : function()
43113 this.indicator = this.indicatorEl();
43115 this.initCurrencyEvent();
43117 this.initNumberEvent();
43120 initCurrencyEvent : function()
43123 throw "can not find store for combo";
43126 this.store = Roo.factory(this.store, Roo.data);
43127 this.store.parent = this;
43131 this.triggerEl = this.el.select('.input-group-addon', true).first();
43133 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43138 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43139 _this.list.setWidth(lw);
43142 this.list.on('mouseover', this.onViewOver, this);
43143 this.list.on('mousemove', this.onViewMove, this);
43144 this.list.on('scroll', this.onViewScroll, this);
43147 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43150 this.view = new Roo.View(this.list, this.tpl, {
43151 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43154 this.view.on('click', this.onViewClick, this);
43156 this.store.on('beforeload', this.onBeforeLoad, this);
43157 this.store.on('load', this.onLoad, this);
43158 this.store.on('loadexception', this.onLoadException, this);
43160 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43161 "up" : function(e){
43162 this.inKeyMode = true;
43166 "down" : function(e){
43167 if(!this.isExpanded()){
43168 this.onTriggerClick();
43170 this.inKeyMode = true;
43175 "enter" : function(e){
43178 if(this.fireEvent("specialkey", this, e)){
43179 this.onViewClick(false);
43185 "esc" : function(e){
43189 "tab" : function(e){
43192 if(this.fireEvent("specialkey", this, e)){
43193 this.onViewClick(false);
43201 doRelay : function(foo, bar, hname){
43202 if(hname == 'down' || this.scope.isExpanded()){
43203 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43211 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43215 initNumberEvent : function(e)
43217 this.inputEl().on("keydown" , this.fireKey, this);
43218 this.inputEl().on("focus", this.onFocus, this);
43219 this.inputEl().on("blur", this.onBlur, this);
43221 this.inputEl().relayEvent('keyup', this);
43223 if(this.indicator){
43224 this.indicator.addClass('invisible');
43227 this.originalValue = this.getValue();
43229 if(this.validationEvent == 'keyup'){
43230 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43231 this.inputEl().on('keyup', this.filterValidation, this);
43233 else if(this.validationEvent !== false){
43234 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43237 if(this.selectOnFocus){
43238 this.on("focus", this.preFocus, this);
43241 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43242 this.inputEl().on("keypress", this.filterKeys, this);
43244 this.inputEl().relayEvent('keypress', this);
43247 var allowed = "0123456789";
43249 if(this.allowDecimals){
43250 allowed += this.decimalSeparator;
43253 if(this.allowNegative){
43257 if(this.thousandsDelimiter) {
43261 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43263 var keyPress = function(e){
43265 var k = e.getKey();
43267 var c = e.getCharCode();
43270 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43271 allowed.indexOf(String.fromCharCode(c)) === -1
43277 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43281 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43286 this.inputEl().on("keypress", keyPress, this);
43290 onTriggerClick : function(e)
43297 this.loadNext = false;
43299 if(this.isExpanded()){
43304 this.hasFocus = true;
43306 if(this.triggerAction == 'all') {
43307 this.doQuery(this.allQuery, true);
43311 this.doQuery(this.getRawValue());
43314 getCurrency : function()
43316 var v = this.currencyEl().getValue();
43321 restrictHeight : function()
43323 this.list.alignTo(this.currencyEl(), this.listAlign);
43324 this.list.alignTo(this.currencyEl(), this.listAlign);
43327 onViewClick : function(view, doFocus, el, e)
43329 var index = this.view.getSelectedIndexes()[0];
43331 var r = this.store.getAt(index);
43334 this.onSelect(r, index);
43338 onSelect : function(record, index){
43340 if(this.fireEvent('beforeselect', this, record, index) !== false){
43342 this.setFromCurrencyData(index > -1 ? record.data : false);
43346 this.fireEvent('select', this, record, index);
43350 setFromCurrencyData : function(o)
43354 this.lastCurrency = o;
43356 if (this.currencyField) {
43357 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43359 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43362 this.lastSelectionText = currency;
43364 //setting default currency
43365 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43366 this.setCurrency(this.defaultCurrency);
43370 this.setCurrency(currency);
43373 setFromData : function(o)
43377 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43379 this.setFromCurrencyData(c);
43384 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43386 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43389 this.setValue(value);
43393 setCurrency : function(v)
43395 this.currencyValue = v;
43398 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43403 setValue : function(v)
43405 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43411 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43413 this.inputEl().dom.value = (v == '') ? '' :
43414 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43416 if(!this.allowZero && v === '0') {
43417 this.hiddenEl().dom.value = '';
43418 this.inputEl().dom.value = '';
43425 getRawValue : function()
43427 var v = this.inputEl().getValue();
43432 getValue : function()
43434 return this.fixPrecision(this.parseValue(this.getRawValue()));
43437 parseValue : function(value)
43439 if(this.thousandsDelimiter) {
43441 r = new RegExp(",", "g");
43442 value = value.replace(r, "");
43445 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43446 return isNaN(value) ? '' : value;
43450 fixPrecision : function(value)
43452 if(this.thousandsDelimiter) {
43454 r = new RegExp(",", "g");
43455 value = value.replace(r, "");
43458 var nan = isNaN(value);
43460 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43461 return nan ? '' : value;
43463 return parseFloat(value).toFixed(this.decimalPrecision);
43466 decimalPrecisionFcn : function(v)
43468 return Math.floor(v);
43471 validateValue : function(value)
43473 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43477 var num = this.parseValue(value);
43480 this.markInvalid(String.format(this.nanText, value));
43484 if(num < this.minValue){
43485 this.markInvalid(String.format(this.minText, this.minValue));
43489 if(num > this.maxValue){
43490 this.markInvalid(String.format(this.maxText, this.maxValue));
43497 validate : function()
43499 if(this.disabled || this.allowBlank){
43504 var currency = this.getCurrency();
43506 if(this.validateValue(this.getRawValue()) && currency.length){
43511 this.markInvalid();
43515 getName: function()
43520 beforeBlur : function()
43526 var v = this.parseValue(this.getRawValue());
43533 onBlur : function()
43537 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43538 //this.el.removeClass(this.focusClass);
43541 this.hasFocus = false;
43543 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43547 var v = this.getValue();
43549 if(String(v) !== String(this.startValue)){
43550 this.fireEvent('change', this, v, this.startValue);
43553 this.fireEvent("blur", this);
43556 inputEl : function()
43558 return this.el.select('.roo-money-amount-input', true).first();
43561 currencyEl : function()
43563 return this.el.select('.roo-money-currency-input', true).first();
43566 hiddenEl : function()
43568 return this.el.select('input.hidden-number-input',true).first();
43572 * @class Roo.bootstrap.BezierSignature
43573 * @extends Roo.bootstrap.Component
43574 * Bootstrap BezierSignature class
43575 * This script refer to:
43576 * Title: Signature Pad
43578 * Availability: https://github.com/szimek/signature_pad
43581 * Create a new BezierSignature
43582 * @param {Object} config The config object
43585 Roo.bootstrap.BezierSignature = function(config){
43586 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43592 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43599 mouse_btn_down: true,
43602 * @cfg {int} canvas height
43604 canvas_height: '200px',
43607 * @cfg {float|function} Radius of a single dot.
43612 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43617 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43622 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43627 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43632 * @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.
43634 bg_color: 'rgba(0, 0, 0, 0)',
43637 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43639 dot_color: 'black',
43642 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43644 velocity_filter_weight: 0.7,
43647 * @cfg {function} Callback when stroke begin.
43652 * @cfg {function} Callback when stroke end.
43656 getAutoCreate : function()
43658 var cls = 'roo-signature column';
43661 cls += ' ' + this.cls;
43671 for(var i = 0; i < col_sizes.length; i++) {
43672 if(this[col_sizes[i]]) {
43673 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43683 cls: 'roo-signature-body',
43687 cls: 'roo-signature-body-canvas',
43688 height: this.canvas_height,
43689 width: this.canvas_width
43696 style: 'display: none'
43704 initEvents: function()
43706 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43708 var canvas = this.canvasEl();
43710 // mouse && touch event swapping...
43711 canvas.dom.style.touchAction = 'none';
43712 canvas.dom.style.msTouchAction = 'none';
43714 this.mouse_btn_down = false;
43715 canvas.on('mousedown', this._handleMouseDown, this);
43716 canvas.on('mousemove', this._handleMouseMove, this);
43717 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43719 if (window.PointerEvent) {
43720 canvas.on('pointerdown', this._handleMouseDown, this);
43721 canvas.on('pointermove', this._handleMouseMove, this);
43722 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43725 if ('ontouchstart' in window) {
43726 canvas.on('touchstart', this._handleTouchStart, this);
43727 canvas.on('touchmove', this._handleTouchMove, this);
43728 canvas.on('touchend', this._handleTouchEnd, this);
43731 Roo.EventManager.onWindowResize(this.resize, this, true);
43733 // file input event
43734 this.fileEl().on('change', this.uploadImage, this);
43741 resize: function(){
43743 var canvas = this.canvasEl().dom;
43744 var ctx = this.canvasElCtx();
43745 var img_data = false;
43747 if(canvas.width > 0) {
43748 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43750 // setting canvas width will clean img data
43753 var style = window.getComputedStyle ?
43754 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43756 var padding_left = parseInt(style.paddingLeft) || 0;
43757 var padding_right = parseInt(style.paddingRight) || 0;
43759 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43762 ctx.putImageData(img_data, 0, 0);
43766 _handleMouseDown: function(e)
43768 if (e.browserEvent.which === 1) {
43769 this.mouse_btn_down = true;
43770 this.strokeBegin(e);
43774 _handleMouseMove: function (e)
43776 if (this.mouse_btn_down) {
43777 this.strokeMoveUpdate(e);
43781 _handleMouseUp: function (e)
43783 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43784 this.mouse_btn_down = false;
43789 _handleTouchStart: function (e) {
43791 e.preventDefault();
43792 if (e.browserEvent.targetTouches.length === 1) {
43793 // var touch = e.browserEvent.changedTouches[0];
43794 // this.strokeBegin(touch);
43796 this.strokeBegin(e); // assume e catching the correct xy...
43800 _handleTouchMove: function (e) {
43801 e.preventDefault();
43802 // var touch = event.targetTouches[0];
43803 // _this._strokeMoveUpdate(touch);
43804 this.strokeMoveUpdate(e);
43807 _handleTouchEnd: function (e) {
43808 var wasCanvasTouched = e.target === this.canvasEl().dom;
43809 if (wasCanvasTouched) {
43810 e.preventDefault();
43811 // var touch = event.changedTouches[0];
43812 // _this._strokeEnd(touch);
43817 reset: function () {
43818 this._lastPoints = [];
43819 this._lastVelocity = 0;
43820 this._lastWidth = (this.min_width + this.max_width) / 2;
43821 this.canvasElCtx().fillStyle = this.dot_color;
43824 strokeMoveUpdate: function(e)
43826 this.strokeUpdate(e);
43828 if (this.throttle) {
43829 this.throttleStroke(this.strokeUpdate, this.throttle);
43832 this.strokeUpdate(e);
43836 strokeBegin: function(e)
43838 var newPointGroup = {
43839 color: this.dot_color,
43843 if (typeof this.onBegin === 'function') {
43847 this.curve_data.push(newPointGroup);
43849 this.strokeUpdate(e);
43852 strokeUpdate: function(e)
43854 var rect = this.canvasEl().dom.getBoundingClientRect();
43855 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43856 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43857 var lastPoints = lastPointGroup.points;
43858 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43859 var isLastPointTooClose = lastPoint
43860 ? point.distanceTo(lastPoint) <= this.min_distance
43862 var color = lastPointGroup.color;
43863 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43864 var curve = this.addPoint(point);
43866 this.drawDot({color: color, point: point});
43869 this.drawCurve({color: color, curve: curve});
43879 strokeEnd: function(e)
43881 this.strokeUpdate(e);
43882 if (typeof this.onEnd === 'function') {
43887 addPoint: function (point) {
43888 var _lastPoints = this._lastPoints;
43889 _lastPoints.push(point);
43890 if (_lastPoints.length > 2) {
43891 if (_lastPoints.length === 3) {
43892 _lastPoints.unshift(_lastPoints[0]);
43894 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43895 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43896 _lastPoints.shift();
43902 calculateCurveWidths: function (startPoint, endPoint) {
43903 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43904 (1 - this.velocity_filter_weight) * this._lastVelocity;
43906 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43909 start: this._lastWidth
43912 this._lastVelocity = velocity;
43913 this._lastWidth = newWidth;
43917 drawDot: function (_a) {
43918 var color = _a.color, point = _a.point;
43919 var ctx = this.canvasElCtx();
43920 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43922 this.drawCurveSegment(point.x, point.y, width);
43924 ctx.fillStyle = color;
43928 drawCurve: function (_a) {
43929 var color = _a.color, curve = _a.curve;
43930 var ctx = this.canvasElCtx();
43931 var widthDelta = curve.endWidth - curve.startWidth;
43932 var drawSteps = Math.floor(curve.length()) * 2;
43934 ctx.fillStyle = color;
43935 for (var i = 0; i < drawSteps; i += 1) {
43936 var t = i / drawSteps;
43942 var x = uuu * curve.startPoint.x;
43943 x += 3 * uu * t * curve.control1.x;
43944 x += 3 * u * tt * curve.control2.x;
43945 x += ttt * curve.endPoint.x;
43946 var y = uuu * curve.startPoint.y;
43947 y += 3 * uu * t * curve.control1.y;
43948 y += 3 * u * tt * curve.control2.y;
43949 y += ttt * curve.endPoint.y;
43950 var width = curve.startWidth + ttt * widthDelta;
43951 this.drawCurveSegment(x, y, width);
43957 drawCurveSegment: function (x, y, width) {
43958 var ctx = this.canvasElCtx();
43960 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43961 this.is_empty = false;
43966 var ctx = this.canvasElCtx();
43967 var canvas = this.canvasEl().dom;
43968 ctx.fillStyle = this.bg_color;
43969 ctx.clearRect(0, 0, canvas.width, canvas.height);
43970 ctx.fillRect(0, 0, canvas.width, canvas.height);
43971 this.curve_data = [];
43973 this.is_empty = true;
43978 return this.el.select('input',true).first();
43981 canvasEl: function()
43983 return this.el.select('canvas',true).first();
43986 canvasElCtx: function()
43988 return this.el.select('canvas',true).first().dom.getContext('2d');
43991 getImage: function(type)
43993 if(this.is_empty) {
43998 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44001 drawFromImage: function(img_src)
44003 var img = new Image();
44005 img.onload = function(){
44006 this.canvasElCtx().drawImage(img, 0, 0);
44011 this.is_empty = false;
44014 selectImage: function()
44016 this.fileEl().dom.click();
44019 uploadImage: function(e)
44021 var reader = new FileReader();
44023 reader.onload = function(e){
44024 var img = new Image();
44025 img.onload = function(){
44027 this.canvasElCtx().drawImage(img, 0, 0);
44029 img.src = e.target.result;
44032 reader.readAsDataURL(e.target.files[0]);
44035 // Bezier Point Constructor
44036 Point: (function () {
44037 function Point(x, y, time) {
44040 this.time = time || Date.now();
44042 Point.prototype.distanceTo = function (start) {
44043 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44045 Point.prototype.equals = function (other) {
44046 return this.x === other.x && this.y === other.y && this.time === other.time;
44048 Point.prototype.velocityFrom = function (start) {
44049 return this.time !== start.time
44050 ? this.distanceTo(start) / (this.time - start.time)
44057 // Bezier Constructor
44058 Bezier: (function () {
44059 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44060 this.startPoint = startPoint;
44061 this.control2 = control2;
44062 this.control1 = control1;
44063 this.endPoint = endPoint;
44064 this.startWidth = startWidth;
44065 this.endWidth = endWidth;
44067 Bezier.fromPoints = function (points, widths, scope) {
44068 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44069 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44070 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44072 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44073 var dx1 = s1.x - s2.x;
44074 var dy1 = s1.y - s2.y;
44075 var dx2 = s2.x - s3.x;
44076 var dy2 = s2.y - s3.y;
44077 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44078 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44079 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44080 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44081 var dxm = m1.x - m2.x;
44082 var dym = m1.y - m2.y;
44083 var k = l2 / (l1 + l2);
44084 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44085 var tx = s2.x - cm.x;
44086 var ty = s2.y - cm.y;
44088 c1: new scope.Point(m1.x + tx, m1.y + ty),
44089 c2: new scope.Point(m2.x + tx, m2.y + ty)
44092 Bezier.prototype.length = function () {
44097 for (var i = 0; i <= steps; i += 1) {
44099 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44100 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44102 var xdiff = cx - px;
44103 var ydiff = cy - py;
44104 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44111 Bezier.prototype.point = function (t, start, c1, c2, end) {
44112 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44113 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44114 + (3.0 * c2 * (1.0 - t) * t * t)
44115 + (end * t * t * t);
44120 throttleStroke: function(fn, wait) {
44121 if (wait === void 0) { wait = 250; }
44123 var timeout = null;
44127 var later = function () {
44128 previous = Date.now();
44130 result = fn.apply(storedContext, storedArgs);
44132 storedContext = null;
44136 return function wrapper() {
44138 for (var _i = 0; _i < arguments.length; _i++) {
44139 args[_i] = arguments[_i];
44141 var now = Date.now();
44142 var remaining = wait - (now - previous);
44143 storedContext = this;
44145 if (remaining <= 0 || remaining > wait) {
44147 clearTimeout(timeout);
44151 result = fn.apply(storedContext, storedArgs);
44153 storedContext = null;
44157 else if (!timeout) {
44158 timeout = window.setTimeout(later, remaining);