2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952 * @cfg {String} size (sm|lg|xl) default empty
3953 * @cfg {Number} max_width set the max width of modal
3954 * @cfg {Boolean} editableTitle can the title be edited
3959 * Create a new Modal Dialog
3960 * @param {Object} config The config object
3963 Roo.bootstrap.Modal = function(config){
3964 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969 * The raw btnclick event for the button
3970 * @param {Roo.EventObject} e
3975 * Fire when dialog resize
3976 * @param {Roo.bootstrap.Modal} this
3977 * @param {Roo.EventObject} e
3981 * @event titlechanged
3982 * Fire when the editable title has been changed
3983 * @param {Roo.bootstrap.Modal} this
3984 * @param {Roo.EventObject} value
3986 "titlechanged" : true
3989 this.buttons = this.buttons || [];
3992 this.tmpl = Roo.factory(this.tmpl);
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3999 title : 'test dialog',
4009 specificTitle: false,
4011 buttonPosition: 'right',
4033 editableTitle : false,
4035 onRender : function(ct, position)
4037 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4040 var cfg = Roo.apply({}, this.getAutoCreate());
4043 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045 //if (!cfg.name.length) {
4049 cfg.cls += ' ' + this.cls;
4052 cfg.style = this.style;
4054 this.el = Roo.get(document.body).createChild(cfg, position);
4056 //var type = this.el.dom.type;
4059 if(this.tabIndex !== undefined){
4060 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4063 this.dialogEl = this.el.select('.modal-dialog',true).first();
4064 this.bodyEl = this.el.select('.modal-body',true).first();
4065 this.closeEl = this.el.select('.modal-header .close', true).first();
4066 this.headerEl = this.el.select('.modal-header',true).first();
4067 this.titleEl = this.el.select('.modal-title',true).first();
4068 this.footerEl = this.el.select('.modal-footer',true).first();
4070 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072 //this.el.addClass("x-dlg-modal");
4074 if (this.buttons.length) {
4075 Roo.each(this.buttons, function(bb) {
4076 var b = Roo.apply({}, bb);
4077 b.xns = b.xns || Roo.bootstrap;
4078 b.xtype = b.xtype || 'Button';
4079 if (typeof(b.listeners) == 'undefined') {
4080 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4083 var btn = Roo.factory(b);
4085 btn.render(this.getButtonContainer());
4089 // render the children.
4092 if(typeof(this.items) != 'undefined'){
4093 var items = this.items;
4096 for(var i =0;i < items.length;i++) {
4097 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4101 this.items = nitems;
4103 // where are these used - they used to be body/close/footer
4107 //this.el.addClass([this.fieldClass, this.cls]);
4111 getAutoCreate : function()
4113 // we will default to modal-body-overflow - might need to remove or make optional later.
4115 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''),
4116 html : this.html || ''
4121 cls : 'modal-title',
4125 if(this.specificTitle){ // WTF is this?
4130 if (this.allow_close && Roo.bootstrap.version == 3) {
4140 if (this.editableTitle) {
4142 cls: 'form-control roo-editable-title d-none',
4148 if (this.allow_close && Roo.bootstrap.version == 4) {
4158 if(this.size.length){
4159 size = 'modal-' + this.size;
4162 var footer = Roo.bootstrap.version == 3 ?
4164 cls : 'modal-footer',
4168 cls: 'btn-' + this.buttonPosition
4173 { // BS4 uses mr-auto on left buttons....
4174 cls : 'modal-footer'
4185 cls: "modal-dialog " + size,
4188 cls : "modal-content",
4191 cls : 'modal-header',
4206 modal.cls += ' fade';
4212 getChildContainer : function() {
4217 getButtonContainer : function() {
4219 return Roo.bootstrap.version == 4 ?
4220 this.el.select('.modal-footer',true).first()
4221 : this.el.select('.modal-footer div',true).first();
4224 initEvents : function()
4226 if (this.allow_close) {
4227 this.closeEl.on('click', this.hide, this);
4229 Roo.EventManager.onWindowResize(this.resize, this, true);
4230 if (this.editableTitle) {
4231 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4232 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233 this.headerEditEl.on('keyup', function(e) {
4234 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235 this.toggleHeaderInput(false)
4238 this.headerEditEl.on('blur', function(e) {
4239 this.toggleHeaderInput(false)
4248 this.maskEl.setSize(
4249 Roo.lib.Dom.getViewWidth(true),
4250 Roo.lib.Dom.getViewHeight(true)
4253 if (this.fitwindow) {
4257 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263 if(this.max_width !== 0) {
4265 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4268 this.setSize(w, this.height);
4272 if(this.max_height) {
4273 this.setSize(w,Math.min(
4275 Roo.lib.Dom.getViewportHeight(true) - 60
4281 if(!this.fit_content) {
4282 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4286 this.setSize(w, Math.min(
4288 this.headerEl.getHeight() +
4289 this.footerEl.getHeight() +
4290 this.getChildHeight(this.bodyEl.dom.childNodes),
4291 Roo.lib.Dom.getViewportHeight(true) - 60)
4297 setSize : function(w,h)
4308 if (!this.rendered) {
4311 this.toggleHeaderInput(false);
4312 //this.el.setStyle('display', 'block');
4313 this.el.removeClass('hideing');
4314 this.el.dom.style.display='block';
4316 Roo.get(document.body).addClass('modal-open');
4318 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4321 this.el.addClass('show');
4322 this.el.addClass('in');
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 // not sure how we can show data in here..
4331 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4334 Roo.get(document.body).addClass("x-body-masked");
4336 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4337 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338 this.maskEl.dom.style.display = 'block';
4339 this.maskEl.addClass('show');
4344 this.fireEvent('show', this);
4346 // set zindex here - otherwise it appears to be ignored...
4347 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4350 this.items.forEach( function(e) {
4351 e.layout ? e.layout() : false;
4359 if(this.fireEvent("beforehide", this) !== false){
4361 this.maskEl.removeClass('show');
4363 this.maskEl.dom.style.display = '';
4364 Roo.get(document.body).removeClass("x-body-masked");
4365 this.el.removeClass('in');
4366 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368 if(this.animate){ // why
4369 this.el.addClass('hideing');
4370 this.el.removeClass('show');
4372 if (!this.el.hasClass('hideing')) {
4373 return; // it's been shown again...
4376 this.el.dom.style.display='';
4378 Roo.get(document.body).removeClass('modal-open');
4379 this.el.removeClass('hideing');
4383 this.el.removeClass('show');
4384 this.el.dom.style.display='';
4385 Roo.get(document.body).removeClass('modal-open');
4388 this.fireEvent('hide', this);
4391 isVisible : function()
4394 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4398 addButton : function(str, cb)
4402 var b = Roo.apply({}, { html : str } );
4403 b.xns = b.xns || Roo.bootstrap;
4404 b.xtype = b.xtype || 'Button';
4405 if (typeof(b.listeners) == 'undefined') {
4406 b.listeners = { click : cb.createDelegate(this) };
4409 var btn = Roo.factory(b);
4411 btn.render(this.getButtonContainer());
4417 setDefaultButton : function(btn)
4419 //this.el.select('.modal-footer').()
4422 resizeTo: function(w,h)
4424 this.dialogEl.setWidth(w);
4426 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4428 this.bodyEl.setHeight(h - diff);
4430 this.fireEvent('resize', this);
4433 setContentSize : function(w, h)
4437 onButtonClick: function(btn,e)
4440 this.fireEvent('btnclick', btn.name, e);
4443 * Set the title of the Dialog
4444 * @param {String} str new Title
4446 setTitle: function(str) {
4447 this.titleEl.dom.innerHTML = str;
4451 * Set the body of the Dialog
4452 * @param {String} str new Title
4454 setBody: function(str) {
4455 this.bodyEl.dom.innerHTML = str;
4458 * Set the body of the Dialog using the template
4459 * @param {Obj} data - apply this data to the template and replace the body contents.
4461 applyBody: function(obj)
4464 Roo.log("Error - using apply Body without a template");
4467 this.tmpl.overwrite(this.bodyEl, obj);
4470 getChildHeight : function(child_nodes)
4474 child_nodes.length == 0
4479 var child_height = 0;
4481 for(var i = 0; i < child_nodes.length; i++) {
4484 * for modal with tabs...
4485 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487 var layout_childs = child_nodes[i].childNodes;
4489 for(var j = 0; j < layout_childs.length; j++) {
4491 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493 var layout_body_childs = layout_childs[j].childNodes;
4495 for(var k = 0; k < layout_body_childs.length; k++) {
4497 if(layout_body_childs[k].classList.contains('navbar')) {
4498 child_height += layout_body_childs[k].offsetHeight;
4502 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4524 child_height += child_nodes[i].offsetHeight;
4525 // Roo.log(child_nodes[i].offsetHeight);
4528 return child_height;
4530 toggleHeaderInput : function(is_edit)
4532 if (!this.editableTitle) {
4533 return; // not editable.
4535 if (is_edit && this.is_header_editing) {
4536 return; // already editing..
4540 this.headerEditEl.dom.value = this.title;
4541 this.headerEditEl.removeClass('d-none');
4542 this.headerEditEl.dom.focus();
4543 this.titleEl.addClass('d-none');
4545 this.is_header_editing = true;
4548 // flip back to not editing.
4549 this.title = this.headerEditEl.dom.value;
4550 this.headerEditEl.addClass('d-none');
4551 this.titleEl.removeClass('d-none');
4552 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553 this.is_header_editing = false;
4554 this.fireEvent('titlechanged', this, this.title);
4563 Roo.apply(Roo.bootstrap.Modal, {
4565 * Button config that displays a single OK button
4574 * Button config that displays Yes and No buttons
4590 * Button config that displays OK and Cancel buttons
4605 * Button config that displays Yes, No and Cancel buttons
4630 * messagebox - can be used as a replace
4634 * @class Roo.MessageBox
4635 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644 // process text value...
4648 // Show a dialog using config options:
4650 title:'Save Changes?',
4651 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652 buttons: Roo.Msg.YESNOCANCEL,
4659 Roo.bootstrap.MessageBox = function(){
4660 var dlg, opt, mask, waitTimer;
4661 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662 var buttons, activeTextEl, bwidth;
4666 var handleButton = function(button){
4668 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4672 var handleHide = function(){
4674 dlg.el.removeClass(opt.cls);
4677 // Roo.TaskMgr.stop(waitTimer);
4678 // waitTimer = null;
4683 var updateButtons = function(b){
4686 buttons["ok"].hide();
4687 buttons["cancel"].hide();
4688 buttons["yes"].hide();
4689 buttons["no"].hide();
4690 dlg.footerEl.hide();
4694 dlg.footerEl.show();
4695 for(var k in buttons){
4696 if(typeof buttons[k] != "function"){
4699 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700 width += buttons[k].el.getWidth()+15;
4710 var handleEsc = function(d, k, e){
4711 if(opt && opt.closable !== false){
4721 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722 * @return {Roo.BasicDialog} The BasicDialog element
4724 getDialog : function(){
4726 dlg = new Roo.bootstrap.Modal( {
4729 //constraintoviewport:false,
4731 //collapsible : false,
4736 //buttonAlign:"center",
4737 closeClick : function(){
4738 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4741 handleButton("cancel");
4746 dlg.on("hide", handleHide);
4748 //dlg.addKeyListener(27, handleEsc);
4750 this.buttons = buttons;
4751 var bt = this.buttonText;
4752 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757 bodyEl = dlg.bodyEl.createChild({
4759 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760 '<textarea class="roo-mb-textarea"></textarea>' +
4761 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4763 msgEl = bodyEl.dom.firstChild;
4764 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765 textboxEl.enableDisplayMode();
4766 textboxEl.addKeyListener([10,13], function(){
4767 if(dlg.isVisible() && opt && opt.buttons){
4770 }else if(opt.buttons.yes){
4771 handleButton("yes");
4775 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776 textareaEl.enableDisplayMode();
4777 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778 progressEl.enableDisplayMode();
4780 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781 var pf = progressEl.dom.firstChild;
4783 pp = Roo.get(pf.firstChild);
4784 pp.setHeight(pf.offsetHeight);
4792 * Updates the message box body text
4793 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794 * the XHTML-compliant non-breaking space character '&#160;')
4795 * @return {Roo.MessageBox} This message box
4797 updateText : function(text)
4799 if(!dlg.isVisible() && !opt.width){
4800 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803 msgEl.innerHTML = text || ' ';
4805 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808 Math.min(opt.width || cw , this.maxWidth),
4809 Math.max(opt.minWidth || this.minWidth, bwidth)
4812 activeTextEl.setWidth(w);
4814 if(dlg.isVisible()){
4815 dlg.fixedcenter = false;
4817 // to big, make it scroll. = But as usual stupid IE does not support
4820 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824 bodyEl.dom.style.height = '';
4825 bodyEl.dom.style.overflowY = '';
4828 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830 bodyEl.dom.style.overflowX = '';
4833 dlg.setContentSize(w, bodyEl.getHeight());
4834 if(dlg.isVisible()){
4835 dlg.fixedcenter = true;
4841 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4842 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845 * @return {Roo.MessageBox} This message box
4847 updateProgress : function(value, text){
4849 this.updateText(text);
4852 if (pp) { // weird bug on my firefox - for some reason this is not defined
4853 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4860 * Returns true if the message box is currently displayed
4861 * @return {Boolean} True if the message box is visible, else false
4863 isVisible : function(){
4864 return dlg && dlg.isVisible();
4868 * Hides the message box if it is displayed
4871 if(this.isVisible()){
4877 * Displays a new message box, or reinitializes an existing message box, based on the config options
4878 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879 * The following config object properties are supported:
4881 Property Type Description
4882 ---------- --------------- ------------------------------------------------------------------------------------
4883 animEl String/Element An id or Element from which the message box should animate as it opens and
4884 closes (defaults to undefined)
4885 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable Boolean False to hide the top-right close button (defaults to true). Note that
4888 progress and wait dialogs will ignore this property and always hide the
4889 close button as they can only be closed programmatically.
4890 cls String A custom CSS class to apply to the message box element
4891 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4892 displayed (defaults to 75)
4893 fn Function A callback function to execute after closing the dialog. The arguments to the
4894 function will be btn (the name of the button that was clicked, if applicable,
4895 e.g. "ok"), and text (the value of the active text field, if applicable).
4896 Progress and wait dialogs will ignore this option since they do not respond to
4897 user actions and can only be closed programmatically, so any required function
4898 should be called by the same code after it closes the dialog.
4899 icon String A CSS class that provides a background image to be used as an icon for
4900 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4902 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4903 modal Boolean False to allow user interaction with the page while the message box is
4904 displayed (defaults to true)
4905 msg String A string that will replace the existing message box body text (defaults
4906 to the XHTML-compliant non-breaking space character ' ')
4907 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4908 progress Boolean True to display a progress bar (defaults to false)
4909 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4912 title String The title text
4913 value String The string value to set into the active textbox element if displayed
4914 wait Boolean True to display a progress bar (defaults to false)
4915 width Number The width of the dialog in pixels
4922 msg: 'Please enter your address:',
4924 buttons: Roo.MessageBox.OKCANCEL,
4927 animEl: 'addAddressBtn'
4930 * @param {Object} config Configuration options
4931 * @return {Roo.MessageBox} This message box
4933 show : function(options)
4936 // this causes nightmares if you show one dialog after another
4937 // especially on callbacks..
4939 if(this.isVisible()){
4942 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4944 Roo.log("New Dialog Message:" + options.msg )
4945 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4949 var d = this.getDialog();
4951 d.setTitle(opt.title || " ");
4952 d.closeEl.setDisplayed(opt.closable !== false);
4953 activeTextEl = textboxEl;
4954 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959 textareaEl.setHeight(typeof opt.multiline == "number" ?
4960 opt.multiline : this.defaultTextHeight);
4961 activeTextEl = textareaEl;
4970 progressEl.setDisplayed(opt.progress === true);
4972 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974 this.updateProgress(0);
4975 activeTextEl.dom.value = opt.value || "";
4977 dlg.setDefaultButton(activeTextEl);
4979 var bs = opt.buttons;
4983 }else if(bs && bs.yes){
4984 db = buttons["yes"];
4986 dlg.setDefaultButton(db);
4988 bwidth = updateButtons(opt.buttons);
4989 this.updateText(opt.msg);
4991 d.el.addClass(opt.cls);
4993 d.proxyDrag = opt.proxyDrag === true;
4994 d.modal = opt.modal !== false;
4995 d.mask = opt.modal !== false ? mask : false;
4997 // force it to the end of the z-index stack so it gets a cursor in FF
4998 document.body.appendChild(dlg.el.dom);
4999 d.animateTarget = null;
5000 d.show(options.animEl);
5006 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5007 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008 * and closing the message box when the process is complete.
5009 * @param {String} title The title bar text
5010 * @param {String} msg The message box body text
5011 * @return {Roo.MessageBox} This message box
5013 progress : function(title, msg){
5020 minWidth: this.minProgressWidth,
5027 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028 * If a callback function is passed it will be called after the user clicks the button, and the
5029 * id of the button that was clicked will be passed as the only parameter to the callback
5030 * (could also be the top-right close button).
5031 * @param {String} title The title bar text
5032 * @param {String} msg The message box body text
5033 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034 * @param {Object} scope (optional) The scope of the callback function
5035 * @return {Roo.MessageBox} This message box
5037 alert : function(title, msg, fn, scope)
5052 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5053 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054 * You are responsible for closing the message box when the process is complete.
5055 * @param {String} msg The message box body text
5056 * @param {String} title (optional) The title bar text
5057 * @return {Roo.MessageBox} This message box
5059 wait : function(msg, title){
5070 waitTimer = Roo.TaskMgr.start({
5072 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5080 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083 * @param {String} title The title bar text
5084 * @param {String} msg The message box body text
5085 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086 * @param {Object} scope (optional) The scope of the callback function
5087 * @return {Roo.MessageBox} This message box
5089 confirm : function(title, msg, fn, scope){
5093 buttons: this.YESNO,
5102 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5104 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105 * (could also be the top-right close button) and the text that was entered will be passed as the two
5106 * parameters to the callback.
5107 * @param {String} title The title bar text
5108 * @param {String} msg The message box body text
5109 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110 * @param {Object} scope (optional) The scope of the callback function
5111 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113 * @return {Roo.MessageBox} This message box
5115 prompt : function(title, msg, fn, scope, multiline){
5119 buttons: this.OKCANCEL,
5124 multiline: multiline,
5131 * Button config that displays a single OK button
5136 * Button config that displays Yes and No buttons
5139 YESNO : {yes:true, no:true},
5141 * Button config that displays OK and Cancel buttons
5144 OKCANCEL : {ok:true, cancel:true},
5146 * Button config that displays Yes, No and Cancel buttons
5149 YESNOCANCEL : {yes:true, no:true, cancel:true},
5152 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5155 defaultTextHeight : 75,
5157 * The maximum width in pixels of the message box (defaults to 600)
5162 * The minimum width in pixels of the message box (defaults to 100)
5167 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5168 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5171 minProgressWidth : 250,
5173 * An object containing the default button text strings that can be overriden for localized language support.
5174 * Supported properties are: ok, cancel, yes and no.
5175 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5188 * Shorthand for {@link Roo.MessageBox}
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5200 * @class Roo.bootstrap.Navbar
5201 * @extends Roo.bootstrap.Component
5202 * Bootstrap Navbar class
5205 * Create a new Navbar
5206 * @param {Object} config The config object
5210 Roo.bootstrap.Navbar = function(config){
5211 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5215 * @event beforetoggle
5216 * Fire before toggle the menu
5217 * @param {Roo.EventObject} e
5219 "beforetoggle" : true
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5232 getAutoCreate : function(){
5235 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5239 initEvents :function ()
5241 //Roo.log(this.el.select('.navbar-toggle',true));
5242 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5249 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251 var size = this.el.getSize();
5252 this.maskEl.setSize(size.width, size.height);
5253 this.maskEl.enableDisplayMode("block");
5262 getChildContainer : function()
5264 if (this.el && this.el.select('.collapse').getCount()) {
5265 return this.el.select('.collapse',true).first();
5280 onToggle : function()
5283 if(this.fireEvent('beforetoggle', this) === false){
5286 var ce = this.el.select('.navbar-collapse',true).first();
5288 if (!ce.hasClass('show')) {
5298 * Expand the navbar pulldown
5300 expand : function ()
5303 var ce = this.el.select('.navbar-collapse',true).first();
5304 if (ce.hasClass('collapsing')) {
5307 ce.dom.style.height = '';
5309 ce.addClass('in'); // old...
5310 ce.removeClass('collapse');
5311 ce.addClass('show');
5312 var h = ce.getHeight();
5314 ce.removeClass('show');
5315 // at this point we should be able to see it..
5316 ce.addClass('collapsing');
5318 ce.setHeight(0); // resize it ...
5319 ce.on('transitionend', function() {
5320 //Roo.log('done transition');
5321 ce.removeClass('collapsing');
5322 ce.addClass('show');
5323 ce.removeClass('collapse');
5325 ce.dom.style.height = '';
5326 }, this, { single: true} );
5328 ce.dom.scrollTop = 0;
5331 * Collapse the navbar pulldown
5333 collapse : function()
5335 var ce = this.el.select('.navbar-collapse',true).first();
5337 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338 // it's collapsed or collapsing..
5341 ce.removeClass('in'); // old...
5342 ce.setHeight(ce.getHeight());
5343 ce.removeClass('show');
5344 ce.addClass('collapsing');
5346 ce.on('transitionend', function() {
5347 ce.dom.style.height = '';
5348 ce.removeClass('collapsing');
5349 ce.addClass('collapse');
5350 }, this, { single: true} );
5370 * @class Roo.bootstrap.NavSimplebar
5371 * @extends Roo.bootstrap.Navbar
5372 * Bootstrap Sidebar class
5374 * @cfg {Boolean} inverse is inverted color
5376 * @cfg {String} type (nav | pills | tabs)
5377 * @cfg {Boolean} arrangement stacked | justified
5378 * @cfg {String} align (left | right) alignment
5380 * @cfg {Boolean} main (true|false) main nav bar? default false
5381 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383 * @cfg {String} tag (header|footer|nav|div) default is nav
5385 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5389 * Create a new Sidebar
5390 * @param {Object} config The config object
5394 Roo.bootstrap.NavSimplebar = function(config){
5395 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5414 getAutoCreate : function(){
5418 tag : this.tag || 'div',
5419 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421 if (['light','white'].indexOf(this.weight) > -1) {
5422 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424 cfg.cls += ' bg-' + this.weight;
5427 cfg.cls += ' navbar-inverse';
5431 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5442 cls: 'nav nav-' + this.xtype,
5448 this.type = this.type || 'nav';
5449 if (['tabs','pills'].indexOf(this.type) != -1) {
5450 cfg.cn[0].cls += ' nav-' + this.type
5454 if (this.type!=='nav') {
5455 Roo.log('nav type must be nav/tabs/pills')
5457 cfg.cn[0].cls += ' navbar-nav'
5463 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464 cfg.cn[0].cls += ' nav-' + this.arrangement;
5468 if (this.align === 'right') {
5469 cfg.cn[0].cls += ' navbar-right';
5494 * navbar-expand-md fixed-top
5498 * @class Roo.bootstrap.NavHeaderbar
5499 * @extends Roo.bootstrap.NavSimplebar
5500 * Bootstrap Sidebar class
5502 * @cfg {String} brand what is brand
5503 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504 * @cfg {String} brand_href href of the brand
5505 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5506 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5511 * Create a new Sidebar
5512 * @param {Object} config The config object
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5528 desktopCenter : false,
5531 getAutoCreate : function(){
5534 tag: this.nav || 'nav',
5535 cls: 'navbar navbar-expand-md',
5541 if (this.desktopCenter) {
5542 cn.push({cls : 'container', cn : []});
5550 cls: 'navbar-toggle navbar-toggler',
5551 'data-toggle': 'collapse',
5556 html: 'Toggle navigation'
5560 cls: 'icon-bar navbar-toggler-icon'
5573 cn.push( Roo.bootstrap.version == 4 ? btn : {
5575 cls: 'navbar-header',
5584 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5588 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590 if (['light','white'].indexOf(this.weight) > -1) {
5591 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593 cfg.cls += ' bg-' + this.weight;
5596 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599 // tag can override this..
5601 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5604 if (this.brand !== '') {
5605 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608 href: this.brand_href ? this.brand_href : '#',
5609 cls: 'navbar-brand',
5617 cfg.cls += ' main-nav';
5625 getHeaderChildContainer : function()
5627 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628 return this.el.select('.navbar-header',true).first();
5631 return this.getChildContainer();
5634 getChildContainer : function()
5637 return this.el.select('.roo-navbar-collapse',true).first();
5642 initEvents : function()
5644 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646 if (this.autohide) {
5651 Roo.get(document).on('scroll',function(e) {
5652 var ns = Roo.get(document).getScroll().top;
5653 var os = prevScroll;
5657 ft.removeClass('slideDown');
5658 ft.addClass('slideUp');
5661 ft.removeClass('slideUp');
5662 ft.addClass('slideDown');
5683 * @class Roo.bootstrap.NavSidebar
5684 * @extends Roo.bootstrap.Navbar
5685 * Bootstrap Sidebar class
5688 * Create a new Sidebar
5689 * @param {Object} config The config object
5693 Roo.bootstrap.NavSidebar = function(config){
5694 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5699 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701 getAutoCreate : function(){
5706 cls: 'sidebar sidebar-nav'
5728 * @class Roo.bootstrap.NavGroup
5729 * @extends Roo.bootstrap.Component
5730 * Bootstrap NavGroup class
5731 * @cfg {String} align (left|right)
5732 * @cfg {Boolean} inverse
5733 * @cfg {String} type (nav|pills|tab) default nav
5734 * @cfg {String} navId - reference Id for navbar.
5738 * Create a new nav group
5739 * @param {Object} config The config object
5742 Roo.bootstrap.NavGroup = function(config){
5743 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5746 Roo.bootstrap.NavGroup.register(this);
5750 * Fires when the active item changes
5751 * @param {Roo.bootstrap.NavGroup} this
5752 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5771 getAutoCreate : function()
5773 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779 if (Roo.bootstrap.version == 4) {
5780 if (['tabs','pills'].indexOf(this.type) != -1) {
5781 cfg.cls += ' nav-' + this.type;
5783 // trying to remove so header bar can right align top?
5784 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785 // do not use on header bar...
5786 cfg.cls += ' navbar-nav';
5791 if (['tabs','pills'].indexOf(this.type) != -1) {
5792 cfg.cls += ' nav-' + this.type
5794 if (this.type !== 'nav') {
5795 Roo.log('nav type must be nav/tabs/pills')
5797 cfg.cls += ' navbar-nav'
5801 if (this.parent() && this.parent().sidebar) {
5804 cls: 'dashboard-menu sidebar-menu'
5810 if (this.form === true) {
5813 cls: 'navbar-form form-inline'
5815 //nav navbar-right ml-md-auto
5816 if (this.align === 'right') {
5817 cfg.cls += ' navbar-right ml-md-auto';
5819 cfg.cls += ' navbar-left';
5823 if (this.align === 'right') {
5824 cfg.cls += ' navbar-right ml-md-auto';
5826 cfg.cls += ' mr-auto';
5830 cfg.cls += ' navbar-inverse';
5838 * sets the active Navigation item
5839 * @param {Roo.bootstrap.NavItem} the new current navitem
5841 setActiveItem : function(item)
5844 Roo.each(this.navItems, function(v){
5849 v.setActive(false, true);
5856 item.setActive(true, true);
5857 this.fireEvent('changed', this, item, prev);
5862 * gets the active Navigation item
5863 * @return {Roo.bootstrap.NavItem} the current navitem
5865 getActive : function()
5869 Roo.each(this.navItems, function(v){
5880 indexOfNav : function()
5884 Roo.each(this.navItems, function(v,i){
5895 * adds a Navigation item
5896 * @param {Roo.bootstrap.NavItem} the navitem to add
5898 addItem : function(cfg)
5900 if (this.form && Roo.bootstrap.version == 4) {
5903 var cn = new Roo.bootstrap.NavItem(cfg);
5905 cn.parentId = this.id;
5906 cn.onRender(this.el, null);
5910 * register a Navigation item
5911 * @param {Roo.bootstrap.NavItem} the navitem to add
5913 register : function(item)
5915 this.navItems.push( item);
5916 item.navId = this.navId;
5921 * clear all the Navigation item
5924 clearAll : function()
5927 this.el.dom.innerHTML = '';
5930 getNavItem: function(tabId)
5933 Roo.each(this.navItems, function(e) {
5934 if (e.tabId == tabId) {
5944 setActiveNext : function()
5946 var i = this.indexOfNav(this.getActive());
5947 if (i > this.navItems.length) {
5950 this.setActiveItem(this.navItems[i+1]);
5952 setActivePrev : function()
5954 var i = this.indexOfNav(this.getActive());
5958 this.setActiveItem(this.navItems[i-1]);
5960 clearWasActive : function(except) {
5961 Roo.each(this.navItems, function(e) {
5962 if (e.tabId != except.tabId && e.was_active) {
5963 e.was_active = false;
5970 getWasActive : function ()
5973 Roo.each(this.navItems, function(e) {
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5992 * register a Navigation Group
5993 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5995 register : function(navgrp)
5997 this.groups[navgrp.navId] = navgrp;
6001 * fetch a Navigation Group based on the navigation ID
6002 * @param {string} the navgroup to add
6003 * @returns {Roo.bootstrap.NavGroup} the navgroup
6005 get: function(navId) {
6006 if (typeof(this.groups[navId]) == 'undefined') {
6008 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6010 return this.groups[navId] ;
6025 * @class Roo.bootstrap.NavItem
6026 * @extends Roo.bootstrap.Component
6027 * Bootstrap Navbar.NavItem class
6028 * @cfg {String} href link to
6029 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6031 * @cfg {String} html content of button
6032 * @cfg {String} badge text inside badge
6033 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034 * @cfg {String} glyphicon DEPRICATED - use fa
6035 * @cfg {String} icon DEPRICATED - use fa
6036 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037 * @cfg {Boolean} active Is item active
6038 * @cfg {Boolean} disabled Is item disabled
6040 * @cfg {Boolean} preventDefault (true | false) default false
6041 * @cfg {String} tabId the tab that this item activates.
6042 * @cfg {String} tagtype (a|span) render as a href or span?
6043 * @cfg {Boolean} animateRef (true|false) link to element default false
6046 * Create a new Navbar Item
6047 * @param {Object} config The config object
6049 Roo.bootstrap.NavItem = function(config){
6050 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6055 * The raw click event for the entire grid.
6056 * @param {Roo.EventObject} e
6061 * Fires when the active item active state changes
6062 * @param {Roo.bootstrap.NavItem} this
6063 * @param {boolean} state the new state
6069 * Fires when scroll to element
6070 * @param {Roo.bootstrap.NavItem} this
6071 * @param {Object} options
6072 * @param {Roo.EventObject} e
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6089 preventDefault : false,
6097 button_outline : false,
6101 getAutoCreate : function(){
6108 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6111 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' + (this.active ? ' active' : '');
6151 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6156 if(this.glyphicon) {
6157 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6162 cfg.cn[0].html += " <span class='caret'></span>";
6166 if (this.badge !== '') {
6168 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6176 onRender : function(ct, position)
6178 // Roo.log("Call onRender: " + this.xtype);
6179 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6183 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6184 this.navLink = this.el.select('.nav-link',true).first();
6189 initEvents: function()
6191 if (typeof (this.menu) != 'undefined') {
6192 this.menu.parentType = this.xtype;
6193 this.menu.triggerEl = this.el;
6194 this.menu = this.addxtype(Roo.apply({}, this.menu));
6197 this.el.select('a',true).on('click', this.onClick, this);
6199 if(this.tagtype == 'span'){
6200 this.el.select('span',true).on('click', this.onClick, this);
6203 // at this point parent should be available..
6204 this.parent().register(this);
6207 onClick : function(e)
6209 if (e.getTarget('.dropdown-menu-item')) {
6210 // did you click on a menu itemm.... - then don't trigger onclick..
6215 this.preventDefault ||
6218 Roo.log("NavItem - prevent Default?");
6222 if (this.disabled) {
6226 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6227 if (tg && tg.transition) {
6228 Roo.log("waiting for the transitionend");
6234 //Roo.log("fire event clicked");
6235 if(this.fireEvent('click', this, e) === false){
6239 if(this.tagtype == 'span'){
6243 //Roo.log(this.href);
6244 var ael = this.el.select('a',true).first();
6247 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6248 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6249 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6250 return; // ignore... - it's a 'hash' to another page.
6252 Roo.log("NavItem - prevent Default?");
6254 this.scrollToElement(e);
6258 var p = this.parent();
6260 if (['tabs','pills'].indexOf(p.type)!==-1) {
6261 if (typeof(p.setActiveItem) !== 'undefined') {
6262 p.setActiveItem(this);
6266 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6267 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6268 // remove the collapsed menu expand...
6269 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6273 isActive: function () {
6276 setActive : function(state, fire, is_was_active)
6278 if (this.active && !state && this.navId) {
6279 this.was_active = true;
6280 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6282 nv.clearWasActive(this);
6286 this.active = state;
6289 this.el.removeClass('active');
6290 this.navLink ? this.navLink.removeClass('active') : false;
6291 } else if (!this.el.hasClass('active')) {
6293 this.el.addClass('active');
6294 if (Roo.bootstrap.version == 4 && this.navLink ) {
6295 this.navLink.addClass('active');
6300 this.fireEvent('changed', this, state);
6303 // show a panel if it's registered and related..
6305 if (!this.navId || !this.tabId || !state || is_was_active) {
6309 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6313 var pan = tg.getPanelByName(this.tabId);
6317 // if we can not flip to new panel - go back to old nav highlight..
6318 if (false == tg.showPanel(pan)) {
6319 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6321 var onav = nv.getWasActive();
6323 onav.setActive(true, false, true);
6332 // this should not be here...
6333 setDisabled : function(state)
6335 this.disabled = state;
6337 this.el.removeClass('disabled');
6338 } else if (!this.el.hasClass('disabled')) {
6339 this.el.addClass('disabled');
6345 * Fetch the element to display the tooltip on.
6346 * @return {Roo.Element} defaults to this.el
6348 tooltipEl : function()
6350 return this.el.select('' + this.tagtype + '', true).first();
6353 scrollToElement : function(e)
6355 var c = document.body;
6358 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6360 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6361 c = document.documentElement;
6364 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370 var o = target.calcOffsetsTo(c);
6377 this.fireEvent('scrollto', this, options, e);
6379 Roo.get(c).scrollTo('top', options.value, true);
6392 * <span> icon </span>
6393 * <span> text </span>
6394 * <span>badge </span>
6398 * @class Roo.bootstrap.NavSidebarItem
6399 * @extends Roo.bootstrap.NavItem
6400 * Bootstrap Navbar.NavSidebarItem class
6401 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6402 * {Boolean} open is the menu open
6403 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6404 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6405 * {String} buttonSize (sm|md|lg)the extra classes for the button
6406 * {Boolean} showArrow show arrow next to the text (default true)
6408 * Create a new Navbar Button
6409 * @param {Object} config The config object
6411 Roo.bootstrap.NavSidebarItem = function(config){
6412 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6417 * The raw click event for the entire grid.
6418 * @param {Roo.EventObject} e
6423 * Fires when the active item active state changes
6424 * @param {Roo.bootstrap.NavSidebarItem} this
6425 * @param {boolean} state the new state
6433 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6435 badgeWeight : 'default',
6441 buttonWeight : 'default',
6447 getAutoCreate : function(){
6452 href : this.href || '#',
6458 if(this.buttonView){
6461 href : this.href || '#',
6462 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6475 cfg.cls += ' active';
6478 if (this.disabled) {
6479 cfg.cls += ' disabled';
6482 cfg.cls += ' open x-open';
6485 if (this.glyphicon || this.icon) {
6486 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6487 a.cn.push({ tag : 'i', cls : c }) ;
6490 if(!this.buttonView){
6493 html : this.html || ''
6500 if (this.badge !== '') {
6501 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6507 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510 a.cls += ' dropdown-toggle treeview' ;
6516 initEvents : function()
6518 if (typeof (this.menu) != 'undefined') {
6519 this.menu.parentType = this.xtype;
6520 this.menu.triggerEl = this.el;
6521 this.menu = this.addxtype(Roo.apply({}, this.menu));
6524 this.el.on('click', this.onClick, this);
6526 if(this.badge !== ''){
6527 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6532 onClick : function(e)
6539 if(this.preventDefault){
6543 this.fireEvent('click', this, e);
6546 disable : function()
6548 this.setDisabled(true);
6553 this.setDisabled(false);
6556 setDisabled : function(state)
6558 if(this.disabled == state){
6562 this.disabled = state;
6565 this.el.addClass('disabled');
6569 this.el.removeClass('disabled');
6574 setActive : function(state)
6576 if(this.active == state){
6580 this.active = state;
6583 this.el.addClass('active');
6587 this.el.removeClass('active');
6592 isActive: function ()
6597 setBadge : function(str)
6603 this.badgeEl.dom.innerHTML = str;
6618 Roo.namespace('Roo.bootstrap.breadcrumb');
6622 * @class Roo.bootstrap.breadcrumb.Nav
6623 * @extends Roo.bootstrap.Component
6624 * Bootstrap Breadcrumb Nav Class
6626 * @children Roo.bootstrap.breadcrumb.Item
6629 * Create a new breadcrumb.Nav
6630 * @param {Object} config The config object
6634 Roo.bootstrap.breadcrumb.Nav = function(config){
6635 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6640 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6642 getAutoCreate : function()
6659 initEvents: function()
6661 this.olEl = this.el.select('ol',true).first();
6663 getChildContainer : function()
6679 * @class Roo.bootstrap.breadcrumb.Nav
6680 * @extends Roo.bootstrap.Component
6681 * Bootstrap Breadcrumb Nav Class
6683 * @children Roo.bootstrap.breadcrumb.Component
6684 * @cfg {String} html the content of the link.
6685 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6686 * @cfg {Boolean} active is it active
6690 * Create a new breadcrumb.Nav
6691 * @param {Object} config The config object
6694 Roo.bootstrap.breadcrumb.Item = function(config){
6695 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6700 * The img click event for the img.
6701 * @param {Roo.EventObject} e
6708 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6713 getAutoCreate : function()
6718 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6720 if (this.href !== false) {
6727 cfg.html = this.html;
6733 initEvents: function()
6736 this.el.select('a', true).first().on('click',this.onClick, this)
6740 onClick : function(e)
6743 this.fireEvent('click',this, e);
6756 * @class Roo.bootstrap.Row
6757 * @extends Roo.bootstrap.Component
6758 * Bootstrap Row class (contains columns...)
6762 * @param {Object} config The config object
6765 Roo.bootstrap.Row = function(config){
6766 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6771 getAutoCreate : function(){
6790 * @class Roo.bootstrap.Pagination
6791 * @extends Roo.bootstrap.Component
6792 * Bootstrap Pagination class
6793 * @cfg {String} size xs | sm | md | lg
6794 * @cfg {Boolean} inverse false | true
6797 * Create a new Pagination
6798 * @param {Object} config The config object
6801 Roo.bootstrap.Pagination = function(config){
6802 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6811 getAutoCreate : function(){
6817 cfg.cls += ' inverse';
6823 cfg.cls += " " + this.cls;
6841 * @class Roo.bootstrap.PaginationItem
6842 * @extends Roo.bootstrap.Component
6843 * Bootstrap PaginationItem class
6844 * @cfg {String} html text
6845 * @cfg {String} href the link
6846 * @cfg {Boolean} preventDefault (true | false) default true
6847 * @cfg {Boolean} active (true | false) default false
6848 * @cfg {Boolean} disabled default false
6852 * Create a new PaginationItem
6853 * @param {Object} config The config object
6857 Roo.bootstrap.PaginationItem = function(config){
6858 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6863 * The raw click event for the entire grid.
6864 * @param {Roo.EventObject} e
6870 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6874 preventDefault: true,
6879 getAutoCreate : function(){
6885 href : this.href ? this.href : '#',
6886 html : this.html ? this.html : ''
6896 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6900 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906 initEvents: function() {
6908 this.el.on('click', this.onClick, this);
6911 onClick : function(e)
6913 Roo.log('PaginationItem on click ');
6914 if(this.preventDefault){
6922 this.fireEvent('click', this, e);
6938 * @class Roo.bootstrap.Slider
6939 * @extends Roo.bootstrap.Component
6940 * Bootstrap Slider class
6943 * Create a new Slider
6944 * @param {Object} config The config object
6947 Roo.bootstrap.Slider = function(config){
6948 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6953 getAutoCreate : function(){
6957 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6961 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6973 * Ext JS Library 1.1.1
6974 * Copyright(c) 2006-2007, Ext JS, LLC.
6976 * Originally Released Under LGPL - original licence link has changed is not relivant.
6979 * <script type="text/javascript">
6984 * @class Roo.grid.ColumnModel
6985 * @extends Roo.util.Observable
6986 * This is the default implementation of a ColumnModel used by the Grid. It defines
6987 * the columns in the grid.
6990 var colModel = new Roo.grid.ColumnModel([
6991 {header: "Ticker", width: 60, sortable: true, locked: true},
6992 {header: "Company Name", width: 150, sortable: true},
6993 {header: "Market Cap.", width: 100, sortable: true},
6994 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6995 {header: "Employees", width: 100, sortable: true, resizable: false}
7000 * The config options listed for this class are options which may appear in each
7001 * individual column definition.
7002 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7004 * @param {Object} config An Array of column config objects. See this class's
7005 * config objects for details.
7007 Roo.grid.ColumnModel = function(config){
7009 * The config passed into the constructor
7011 this.config = config;
7014 // if no id, create one
7015 // if the column does not have a dataIndex mapping,
7016 // map it to the order it is in the config
7017 for(var i = 0, len = config.length; i < len; i++){
7019 if(typeof c.dataIndex == "undefined"){
7022 if(typeof c.renderer == "string"){
7023 c.renderer = Roo.util.Format[c.renderer];
7025 if(typeof c.id == "undefined"){
7028 if(c.editor && c.editor.xtype){
7029 c.editor = Roo.factory(c.editor, Roo.grid);
7031 if(c.editor && c.editor.isFormField){
7032 c.editor = new Roo.grid.GridEditor(c.editor);
7034 this.lookup[c.id] = c;
7038 * The width of columns which have no width specified (defaults to 100)
7041 this.defaultWidth = 100;
7044 * Default sortable of columns which have no sortable specified (defaults to false)
7047 this.defaultSortable = false;
7051 * @event widthchange
7052 * Fires when the width of a column changes.
7053 * @param {ColumnModel} this
7054 * @param {Number} columnIndex The column index
7055 * @param {Number} newWidth The new width
7057 "widthchange": true,
7059 * @event headerchange
7060 * Fires when the text of a header changes.
7061 * @param {ColumnModel} this
7062 * @param {Number} columnIndex The column index
7063 * @param {Number} newText The new header text
7065 "headerchange": true,
7067 * @event hiddenchange
7068 * Fires when a column is hidden or "unhidden".
7069 * @param {ColumnModel} this
7070 * @param {Number} columnIndex The column index
7071 * @param {Boolean} hidden true if hidden, false otherwise
7073 "hiddenchange": true,
7075 * @event columnmoved
7076 * Fires when a column is moved.
7077 * @param {ColumnModel} this
7078 * @param {Number} oldIndex
7079 * @param {Number} newIndex
7081 "columnmoved" : true,
7083 * @event columlockchange
7084 * Fires when a column's locked state is changed
7085 * @param {ColumnModel} this
7086 * @param {Number} colIndex
7087 * @param {Boolean} locked true if locked
7089 "columnlockchange" : true
7091 Roo.grid.ColumnModel.superclass.constructor.call(this);
7093 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7095 * @cfg {String} header The header text to display in the Grid view.
7098 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7099 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7100 * specified, the column's index is used as an index into the Record's data Array.
7103 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7104 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7108 * Defaults to the value of the {@link #defaultSortable} property.
7109 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7115 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7118 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7125 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7126 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7127 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7133 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7136 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7139 * @cfg {String} cursor (Optional)
7142 * @cfg {String} tooltip (Optional)
7145 * @cfg {Number} xs (Optional)
7148 * @cfg {Number} sm (Optional)
7151 * @cfg {Number} md (Optional)
7154 * @cfg {Number} lg (Optional)
7157 * Returns the id of the column at the specified index.
7158 * @param {Number} index The column index
7159 * @return {String} the id
7161 getColumnId : function(index){
7162 return this.config[index].id;
7166 * Returns the column for a specified id.
7167 * @param {String} id The column id
7168 * @return {Object} the column
7170 getColumnById : function(id){
7171 return this.lookup[id];
7176 * Returns the column for a specified dataIndex.
7177 * @param {String} dataIndex The column dataIndex
7178 * @return {Object|Boolean} the column or false if not found
7180 getColumnByDataIndex: function(dataIndex){
7181 var index = this.findColumnIndex(dataIndex);
7182 return index > -1 ? this.config[index] : false;
7186 * Returns the index for a specified column id.
7187 * @param {String} id The column id
7188 * @return {Number} the index, or -1 if not found
7190 getIndexById : function(id){
7191 for(var i = 0, len = this.config.length; i < len; i++){
7192 if(this.config[i].id == id){
7200 * Returns the index for a specified column dataIndex.
7201 * @param {String} dataIndex The column dataIndex
7202 * @return {Number} the index, or -1 if not found
7205 findColumnIndex : function(dataIndex){
7206 for(var i = 0, len = this.config.length; i < len; i++){
7207 if(this.config[i].dataIndex == dataIndex){
7215 moveColumn : function(oldIndex, newIndex){
7216 var c = this.config[oldIndex];
7217 this.config.splice(oldIndex, 1);
7218 this.config.splice(newIndex, 0, c);
7219 this.dataMap = null;
7220 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223 isLocked : function(colIndex){
7224 return this.config[colIndex].locked === true;
7227 setLocked : function(colIndex, value, suppressEvent){
7228 if(this.isLocked(colIndex) == value){
7231 this.config[colIndex].locked = value;
7233 this.fireEvent("columnlockchange", this, colIndex, value);
7237 getTotalLockedWidth : function(){
7239 for(var i = 0; i < this.config.length; i++){
7240 if(this.isLocked(i) && !this.isHidden(i)){
7241 this.totalWidth += this.getColumnWidth(i);
7247 getLockedCount : function(){
7248 for(var i = 0, len = this.config.length; i < len; i++){
7249 if(!this.isLocked(i)){
7254 return this.config.length;
7258 * Returns the number of columns.
7261 getColumnCount : function(visibleOnly){
7262 if(visibleOnly === true){
7264 for(var i = 0, len = this.config.length; i < len; i++){
7265 if(!this.isHidden(i)){
7271 return this.config.length;
7275 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7276 * @param {Function} fn
7277 * @param {Object} scope (optional)
7278 * @return {Array} result
7280 getColumnsBy : function(fn, scope){
7282 for(var i = 0, len = this.config.length; i < len; i++){
7283 var c = this.config[i];
7284 if(fn.call(scope||this, c, i) === true){
7292 * Returns true if the specified column is sortable.
7293 * @param {Number} col The column index
7296 isSortable : function(col){
7297 if(typeof this.config[col].sortable == "undefined"){
7298 return this.defaultSortable;
7300 return this.config[col].sortable;
7304 * Returns the rendering (formatting) function defined for the column.
7305 * @param {Number} col The column index.
7306 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7308 getRenderer : function(col){
7309 if(!this.config[col].renderer){
7310 return Roo.grid.ColumnModel.defaultRenderer;
7312 return this.config[col].renderer;
7316 * Sets the rendering (formatting) function for a column.
7317 * @param {Number} col The column index
7318 * @param {Function} fn The function to use to process the cell's raw data
7319 * to return HTML markup for the grid view. The render function is called with
7320 * the following parameters:<ul>
7321 * <li>Data value.</li>
7322 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7323 * <li>css A CSS style string to apply to the table cell.</li>
7324 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7325 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7326 * <li>Row index</li>
7327 * <li>Column index</li>
7328 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7330 setRenderer : function(col, fn){
7331 this.config[col].renderer = fn;
7335 * Returns the width for the specified column.
7336 * @param {Number} col The column index
7339 getColumnWidth : function(col){
7340 return this.config[col].width * 1 || this.defaultWidth;
7344 * Sets the width for a column.
7345 * @param {Number} col The column index
7346 * @param {Number} width The new width
7348 setColumnWidth : function(col, width, suppressEvent){
7349 this.config[col].width = width;
7350 this.totalWidth = null;
7352 this.fireEvent("widthchange", this, col, width);
7357 * Returns the total width of all columns.
7358 * @param {Boolean} includeHidden True to include hidden column widths
7361 getTotalWidth : function(includeHidden){
7362 if(!this.totalWidth){
7363 this.totalWidth = 0;
7364 for(var i = 0, len = this.config.length; i < len; i++){
7365 if(includeHidden || !this.isHidden(i)){
7366 this.totalWidth += this.getColumnWidth(i);
7370 return this.totalWidth;
7374 * Returns the header for the specified column.
7375 * @param {Number} col The column index
7378 getColumnHeader : function(col){
7379 return this.config[col].header;
7383 * Sets the header for a column.
7384 * @param {Number} col The column index
7385 * @param {String} header The new header
7387 setColumnHeader : function(col, header){
7388 this.config[col].header = header;
7389 this.fireEvent("headerchange", this, col, header);
7393 * Returns the tooltip for the specified column.
7394 * @param {Number} col The column index
7397 getColumnTooltip : function(col){
7398 return this.config[col].tooltip;
7401 * Sets the tooltip for a column.
7402 * @param {Number} col The column index
7403 * @param {String} tooltip The new tooltip
7405 setColumnTooltip : function(col, tooltip){
7406 this.config[col].tooltip = tooltip;
7410 * Returns the dataIndex for the specified column.
7411 * @param {Number} col The column index
7414 getDataIndex : function(col){
7415 return this.config[col].dataIndex;
7419 * Sets the dataIndex for a column.
7420 * @param {Number} col The column index
7421 * @param {Number} dataIndex The new dataIndex
7423 setDataIndex : function(col, dataIndex){
7424 this.config[col].dataIndex = dataIndex;
7430 * Returns true if the cell is editable.
7431 * @param {Number} colIndex The column index
7432 * @param {Number} rowIndex The row index - this is nto actually used..?
7435 isCellEditable : function(colIndex, rowIndex){
7436 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7440 * Returns the editor defined for the cell/column.
7441 * return false or null to disable editing.
7442 * @param {Number} colIndex The column index
7443 * @param {Number} rowIndex The row index
7446 getCellEditor : function(colIndex, rowIndex){
7447 return this.config[colIndex].editor;
7451 * Sets if a column is editable.
7452 * @param {Number} col The column index
7453 * @param {Boolean} editable True if the column is editable
7455 setEditable : function(col, editable){
7456 this.config[col].editable = editable;
7461 * Returns true if the column is hidden.
7462 * @param {Number} colIndex The column index
7465 isHidden : function(colIndex){
7466 return this.config[colIndex].hidden;
7471 * Returns true if the column width cannot be changed
7473 isFixed : function(colIndex){
7474 return this.config[colIndex].fixed;
7478 * Returns true if the column can be resized
7481 isResizable : function(colIndex){
7482 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485 * Sets if a column is hidden.
7486 * @param {Number} colIndex The column index
7487 * @param {Boolean} hidden True if the column is hidden
7489 setHidden : function(colIndex, hidden){
7490 this.config[colIndex].hidden = hidden;
7491 this.totalWidth = null;
7492 this.fireEvent("hiddenchange", this, colIndex, hidden);
7496 * Sets the editor for a column.
7497 * @param {Number} col The column index
7498 * @param {Object} editor The editor object
7500 setEditor : function(col, editor){
7501 this.config[col].editor = editor;
7505 Roo.grid.ColumnModel.defaultRenderer = function(value)
7507 if(typeof value == "object") {
7510 if(typeof value == "string" && value.length < 1){
7514 return String.format("{0}", value);
7517 // Alias for backwards compatibility
7518 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 * Ext JS Library 1.1.1
7522 * Copyright(c) 2006-2007, Ext JS, LLC.
7524 * Originally Released Under LGPL - original licence link has changed is not relivant.
7527 * <script type="text/javascript">
7531 * @class Roo.LoadMask
7532 * A simple utility class for generically masking elements while loading data. If the element being masked has
7533 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7534 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7535 * element's UpdateManager load indicator and will be destroyed after the initial load.
7537 * Create a new LoadMask
7538 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7539 * @param {Object} config The config object
7541 Roo.LoadMask = function(el, config){
7542 this.el = Roo.get(el);
7543 Roo.apply(this, config);
7545 this.store.on('beforeload', this.onBeforeLoad, this);
7546 this.store.on('load', this.onLoad, this);
7547 this.store.on('loadexception', this.onLoadException, this);
7548 this.removeMask = false;
7550 var um = this.el.getUpdateManager();
7551 um.showLoadIndicator = false; // disable the default indicator
7552 um.on('beforeupdate', this.onBeforeLoad, this);
7553 um.on('update', this.onLoad, this);
7554 um.on('failure', this.onLoad, this);
7555 this.removeMask = true;
7559 Roo.LoadMask.prototype = {
7561 * @cfg {Boolean} removeMask
7562 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7563 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7567 * The text to display in a centered loading message box (defaults to 'Loading...')
7571 * @cfg {String} msgCls
7572 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7574 msgCls : 'x-mask-loading',
7577 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583 * Disables the mask to prevent it from being displayed
7585 disable : function(){
7586 this.disabled = true;
7590 * Enables the mask so that it can be displayed
7592 enable : function(){
7593 this.disabled = false;
7596 onLoadException : function()
7600 if (typeof(arguments[3]) != 'undefined') {
7601 Roo.MessageBox.alert("Error loading",arguments[3]);
7605 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7606 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7613 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7622 onBeforeLoad : function(){
7624 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7629 destroy : function(){
7631 this.store.un('beforeload', this.onBeforeLoad, this);
7632 this.store.un('load', this.onLoad, this);
7633 this.store.un('loadexception', this.onLoadException, this);
7635 var um = this.el.getUpdateManager();
7636 um.un('beforeupdate', this.onBeforeLoad, this);
7637 um.un('update', this.onLoad, this);
7638 um.un('failure', this.onLoad, this);
7649 * @class Roo.bootstrap.Table
7650 * @extends Roo.bootstrap.Component
7651 * Bootstrap Table class
7652 * @cfg {String} cls table class
7653 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7654 * @cfg {String} bgcolor Specifies the background color for a table
7655 * @cfg {Number} border Specifies whether the table cells should have borders or not
7656 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7657 * @cfg {Number} cellspacing Specifies the space between cells
7658 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7659 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7660 * @cfg {String} sortable Specifies that the table should be sortable
7661 * @cfg {String} summary Specifies a summary of the content of a table
7662 * @cfg {Number} width Specifies the width of a table
7663 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7665 * @cfg {boolean} striped Should the rows be alternative striped
7666 * @cfg {boolean} bordered Add borders to the table
7667 * @cfg {boolean} hover Add hover highlighting
7668 * @cfg {boolean} condensed Format condensed
7669 * @cfg {boolean} responsive Format condensed
7670 * @cfg {Boolean} loadMask (true|false) default false
7671 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7672 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7673 * @cfg {Boolean} rowSelection (true|false) default false
7674 * @cfg {Boolean} cellSelection (true|false) default false
7675 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7676 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7677 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7678 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7682 * Create a new Table
7683 * @param {Object} config The config object
7686 Roo.bootstrap.Table = function(config){
7687 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7692 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7693 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7694 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7695 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7697 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7699 this.sm.grid = this;
7700 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7701 this.sm = this.selModel;
7702 this.sm.xmodule = this.xmodule || false;
7705 if (this.cm && typeof(this.cm.config) == 'undefined') {
7706 this.colModel = new Roo.grid.ColumnModel(this.cm);
7707 this.cm = this.colModel;
7708 this.cm.xmodule = this.xmodule || false;
7711 this.store= Roo.factory(this.store, Roo.data);
7712 this.ds = this.store;
7713 this.ds.xmodule = this.xmodule || false;
7716 if (this.footer && this.store) {
7717 this.footer.dataSource = this.ds;
7718 this.footer = Roo.factory(this.footer);
7725 * Fires when a cell is clicked
7726 * @param {Roo.bootstrap.Table} this
7727 * @param {Roo.Element} el
7728 * @param {Number} rowIndex
7729 * @param {Number} columnIndex
7730 * @param {Roo.EventObject} e
7734 * @event celldblclick
7735 * Fires when a cell is double clicked
7736 * @param {Roo.bootstrap.Table} this
7737 * @param {Roo.Element} el
7738 * @param {Number} rowIndex
7739 * @param {Number} columnIndex
7740 * @param {Roo.EventObject} e
7742 "celldblclick" : true,
7745 * Fires when a row is clicked
7746 * @param {Roo.bootstrap.Table} this
7747 * @param {Roo.Element} el
7748 * @param {Number} rowIndex
7749 * @param {Roo.EventObject} e
7753 * @event rowdblclick
7754 * Fires when a row is double clicked
7755 * @param {Roo.bootstrap.Table} this
7756 * @param {Roo.Element} el
7757 * @param {Number} rowIndex
7758 * @param {Roo.EventObject} e
7760 "rowdblclick" : true,
7763 * Fires when a mouseover occur
7764 * @param {Roo.bootstrap.Table} this
7765 * @param {Roo.Element} el
7766 * @param {Number} rowIndex
7767 * @param {Number} columnIndex
7768 * @param {Roo.EventObject} e
7773 * Fires when a mouseout occur
7774 * @param {Roo.bootstrap.Table} this
7775 * @param {Roo.Element} el
7776 * @param {Number} rowIndex
7777 * @param {Number} columnIndex
7778 * @param {Roo.EventObject} e
7783 * Fires when a row is rendered, so you can change add a style to it.
7784 * @param {Roo.bootstrap.Table} this
7785 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7789 * @event rowsrendered
7790 * Fires when all the rows have been rendered
7791 * @param {Roo.bootstrap.Table} this
7793 'rowsrendered' : true,
7795 * @event contextmenu
7796 * The raw contextmenu event for the entire grid.
7797 * @param {Roo.EventObject} e
7799 "contextmenu" : true,
7801 * @event rowcontextmenu
7802 * Fires when a row is right clicked
7803 * @param {Roo.bootstrap.Table} this
7804 * @param {Number} rowIndex
7805 * @param {Roo.EventObject} e
7807 "rowcontextmenu" : true,
7809 * @event cellcontextmenu
7810 * Fires when a cell is right clicked
7811 * @param {Roo.bootstrap.Table} this
7812 * @param {Number} rowIndex
7813 * @param {Number} cellIndex
7814 * @param {Roo.EventObject} e
7816 "cellcontextmenu" : true,
7818 * @event headercontextmenu
7819 * Fires when a header is right clicked
7820 * @param {Roo.bootstrap.Table} this
7821 * @param {Number} columnIndex
7822 * @param {Roo.EventObject} e
7824 "headercontextmenu" : true
7828 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7854 rowSelection : false,
7855 cellSelection : false,
7858 // Roo.Element - the tbody
7860 // Roo.Element - thead element
7863 container: false, // used by gridpanel...
7869 auto_hide_footer : false,
7871 getAutoCreate : function()
7873 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7880 if (this.scrollBody) {
7881 cfg.cls += ' table-body-fixed';
7884 cfg.cls += ' table-striped';
7888 cfg.cls += ' table-hover';
7890 if (this.bordered) {
7891 cfg.cls += ' table-bordered';
7893 if (this.condensed) {
7894 cfg.cls += ' table-condensed';
7896 if (this.responsive) {
7897 cfg.cls += ' table-responsive';
7901 cfg.cls+= ' ' +this.cls;
7904 // this lot should be simplifed...
7917 ].forEach(function(k) {
7925 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928 if(this.store || this.cm){
7929 if(this.headerShow){
7930 cfg.cn.push(this.renderHeader());
7933 cfg.cn.push(this.renderBody());
7935 if(this.footerShow){
7936 cfg.cn.push(this.renderFooter());
7938 // where does this come from?
7939 //cfg.cls+= ' TableGrid';
7942 return { cn : [ cfg ] };
7945 initEvents : function()
7947 if(!this.store || !this.cm){
7950 if (this.selModel) {
7951 this.selModel.initEvents();
7955 //Roo.log('initEvents with ds!!!!');
7957 this.mainBody = this.el.select('tbody', true).first();
7958 this.mainHead = this.el.select('thead', true).first();
7959 this.mainFoot = this.el.select('tfoot', true).first();
7965 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7966 e.on('click', _this.sort, _this);
7969 this.mainBody.on("click", this.onClick, this);
7970 this.mainBody.on("dblclick", this.onDblClick, this);
7972 // why is this done????? = it breaks dialogs??
7973 //this.parent().el.setStyle('position', 'relative');
7977 this.footer.parentId = this.id;
7978 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981 this.el.select('tfoot tr td').first().addClass('hide');
7986 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989 this.store.on('load', this.onLoad, this);
7990 this.store.on('beforeload', this.onBeforeLoad, this);
7991 this.store.on('update', this.onUpdate, this);
7992 this.store.on('add', this.onAdd, this);
7993 this.store.on("clear", this.clear, this);
7995 this.el.on("contextmenu", this.onContextMenu, this);
7997 this.mainBody.on('scroll', this.onBodyScroll, this);
7999 this.cm.on("headerchange", this.onHeaderChange, this);
8001 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8005 onContextMenu : function(e, t)
8007 this.processEvent("contextmenu", e);
8010 processEvent : function(name, e)
8012 if (name != 'touchstart' ) {
8013 this.fireEvent(name, e);
8016 var t = e.getTarget();
8018 var cell = Roo.get(t);
8024 if(cell.findParent('tfoot', false, true)){
8028 if(cell.findParent('thead', false, true)){
8030 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8031 cell = Roo.get(t).findParent('th', false, true);
8033 Roo.log("failed to find th in thead?");
8034 Roo.log(e.getTarget());
8039 var cellIndex = cell.dom.cellIndex;
8041 var ename = name == 'touchstart' ? 'click' : name;
8042 this.fireEvent("header" + ename, this, cellIndex, e);
8047 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8048 cell = Roo.get(t).findParent('td', false, true);
8050 Roo.log("failed to find th in tbody?");
8051 Roo.log(e.getTarget());
8056 var row = cell.findParent('tr', false, true);
8057 var cellIndex = cell.dom.cellIndex;
8058 var rowIndex = row.dom.rowIndex - 1;
8062 this.fireEvent("row" + name, this, rowIndex, e);
8066 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072 onMouseover : function(e, el)
8074 var cell = Roo.get(el);
8080 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8081 cell = cell.findParent('td', false, true);
8084 var row = cell.findParent('tr', false, true);
8085 var cellIndex = cell.dom.cellIndex;
8086 var rowIndex = row.dom.rowIndex - 1; // start from 0
8088 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8092 onMouseout : function(e, el)
8094 var cell = Roo.get(el);
8100 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8101 cell = cell.findParent('td', false, true);
8104 var row = cell.findParent('tr', false, true);
8105 var cellIndex = cell.dom.cellIndex;
8106 var rowIndex = row.dom.rowIndex - 1; // start from 0
8108 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8112 onClick : function(e, el)
8114 var cell = Roo.get(el);
8116 if(!cell || (!this.cellSelection && !this.rowSelection)){
8120 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8121 cell = cell.findParent('td', false, true);
8124 if(!cell || typeof(cell) == 'undefined'){
8128 var row = cell.findParent('tr', false, true);
8130 if(!row || typeof(row) == 'undefined'){
8134 var cellIndex = cell.dom.cellIndex;
8135 var rowIndex = this.getRowIndex(row);
8137 // why??? - should these not be based on SelectionModel?
8138 if(this.cellSelection){
8139 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142 if(this.rowSelection){
8143 this.fireEvent('rowclick', this, row, rowIndex, e);
8149 onDblClick : function(e,el)
8151 var cell = Roo.get(el);
8153 if(!cell || (!this.cellSelection && !this.rowSelection)){
8157 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8158 cell = cell.findParent('td', false, true);
8161 if(!cell || typeof(cell) == 'undefined'){
8165 var row = cell.findParent('tr', false, true);
8167 if(!row || typeof(row) == 'undefined'){
8171 var cellIndex = cell.dom.cellIndex;
8172 var rowIndex = this.getRowIndex(row);
8174 if(this.cellSelection){
8175 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178 if(this.rowSelection){
8179 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8183 sort : function(e,el)
8185 var col = Roo.get(el);
8187 if(!col.hasClass('sortable')){
8191 var sort = col.attr('sort');
8194 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8198 this.store.sortInfo = {field : sort, direction : dir};
8201 Roo.log("calling footer first");
8202 this.footer.onClick('first');
8205 this.store.load({ params : { start : 0 } });
8209 renderHeader : function()
8217 this.totalWidth = 0;
8219 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8221 var config = cm.config[i];
8225 cls : 'x-hcol-' + i,
8227 html: cm.getColumnHeader(i)
8232 if(typeof(config.sortable) != 'undefined' && config.sortable){
8234 c.html = '<i class="glyphicon"></i>' + c.html;
8237 // could use BS4 hidden-..-down
8239 if(typeof(config.lgHeader) != 'undefined'){
8240 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243 if(typeof(config.mdHeader) != 'undefined'){
8244 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247 if(typeof(config.smHeader) != 'undefined'){
8248 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251 if(typeof(config.xsHeader) != 'undefined'){
8252 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8259 if(typeof(config.tooltip) != 'undefined'){
8260 c.tooltip = config.tooltip;
8263 if(typeof(config.colspan) != 'undefined'){
8264 c.colspan = config.colspan;
8267 if(typeof(config.hidden) != 'undefined' && config.hidden){
8268 c.style += ' display:none;';
8271 if(typeof(config.dataIndex) != 'undefined'){
8272 c.sort = config.dataIndex;
8277 if(typeof(config.align) != 'undefined' && config.align.length){
8278 c.style += ' text-align:' + config.align + ';';
8281 if(typeof(config.width) != 'undefined'){
8282 c.style += ' width:' + config.width + 'px;';
8283 this.totalWidth += config.width;
8285 this.totalWidth += 100; // assume minimum of 100 per column?
8288 if(typeof(config.cls) != 'undefined'){
8289 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292 ['xs','sm','md','lg'].map(function(size){
8294 if(typeof(config[size]) == 'undefined'){
8298 if (!config[size]) { // 0 = hidden
8299 // BS 4 '0' is treated as hide that column and below.
8300 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8304 c.cls += ' col-' + size + '-' + config[size] + (
8305 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8317 renderBody : function()
8327 colspan : this.cm.getColumnCount()
8337 renderFooter : function()
8347 colspan : this.cm.getColumnCount()
8361 // Roo.log('ds onload');
8366 var ds = this.store;
8368 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8369 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8370 if (_this.store.sortInfo) {
8372 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8373 e.select('i', true).addClass(['glyphicon-arrow-up']);
8376 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8377 e.select('i', true).addClass(['glyphicon-arrow-down']);
8382 var tbody = this.mainBody;
8384 if(ds.getCount() > 0){
8385 ds.data.each(function(d,rowIndex){
8386 var row = this.renderRow(cm, ds, rowIndex);
8388 tbody.createChild(row);
8392 if(row.cellObjects.length){
8393 Roo.each(row.cellObjects, function(r){
8394 _this.renderCellObject(r);
8401 var tfoot = this.el.select('tfoot', true).first();
8403 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8405 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8407 var total = this.ds.getTotalCount();
8409 if(this.footer.pageSize < total){
8410 this.mainFoot.show();
8414 Roo.each(this.el.select('tbody td', true).elements, function(e){
8415 e.on('mouseover', _this.onMouseover, _this);
8418 Roo.each(this.el.select('tbody td', true).elements, function(e){
8419 e.on('mouseout', _this.onMouseout, _this);
8421 this.fireEvent('rowsrendered', this);
8427 onUpdate : function(ds,record)
8429 this.refreshRow(record);
8433 onRemove : function(ds, record, index, isUpdate){
8434 if(isUpdate !== true){
8435 this.fireEvent("beforerowremoved", this, index, record);
8437 var bt = this.mainBody.dom;
8439 var rows = this.el.select('tbody > tr', true).elements;
8441 if(typeof(rows[index]) != 'undefined'){
8442 bt.removeChild(rows[index].dom);
8445 // if(bt.rows[index]){
8446 // bt.removeChild(bt.rows[index]);
8449 if(isUpdate !== true){
8450 //this.stripeRows(index);
8451 //this.syncRowHeights(index, index);
8453 this.fireEvent("rowremoved", this, index, record);
8457 onAdd : function(ds, records, rowIndex)
8459 //Roo.log('on Add called');
8460 // - note this does not handle multiple adding very well..
8461 var bt = this.mainBody.dom;
8462 for (var i =0 ; i < records.length;i++) {
8463 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8464 //Roo.log(records[i]);
8465 //Roo.log(this.store.getAt(rowIndex+i));
8466 this.insertRow(this.store, rowIndex + i, false);
8473 refreshRow : function(record){
8474 var ds = this.store, index;
8475 if(typeof record == 'number'){
8477 record = ds.getAt(index);
8479 index = ds.indexOf(record);
8481 return; // should not happen - but seems to
8484 this.insertRow(ds, index, true);
8486 this.onRemove(ds, record, index+1, true);
8488 //this.syncRowHeights(index, index);
8490 this.fireEvent("rowupdated", this, index, record);
8493 insertRow : function(dm, rowIndex, isUpdate){
8496 this.fireEvent("beforerowsinserted", this, rowIndex);
8498 //var s = this.getScrollState();
8499 var row = this.renderRow(this.cm, this.store, rowIndex);
8500 // insert before rowIndex..
8501 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8505 if(row.cellObjects.length){
8506 Roo.each(row.cellObjects, function(r){
8507 _this.renderCellObject(r);
8512 this.fireEvent("rowsinserted", this, rowIndex);
8513 //this.syncRowHeights(firstRow, lastRow);
8514 //this.stripeRows(firstRow);
8521 getRowDom : function(rowIndex)
8523 var rows = this.el.select('tbody > tr', true).elements;
8525 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528 // returns the object tree for a tr..
8531 renderRow : function(cm, ds, rowIndex)
8533 var d = ds.getAt(rowIndex);
8537 cls : 'x-row-' + rowIndex,
8541 var cellObjects = [];
8543 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8544 var config = cm.config[i];
8546 var renderer = cm.getRenderer(i);
8550 if(typeof(renderer) !== 'undefined'){
8551 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8553 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8554 // and are rendered into the cells after the row is rendered - using the id for the element.
8556 if(typeof(value) === 'object'){
8566 rowIndex : rowIndex,
8571 this.fireEvent('rowclass', this, rowcfg);
8575 cls : rowcfg.rowClass + ' x-col-' + i,
8577 html: (typeof(value) === 'object') ? '' : value
8584 if(typeof(config.colspan) != 'undefined'){
8585 td.colspan = config.colspan;
8588 if(typeof(config.hidden) != 'undefined' && config.hidden){
8589 td.style += ' display:none;';
8592 if(typeof(config.align) != 'undefined' && config.align.length){
8593 td.style += ' text-align:' + config.align + ';';
8595 if(typeof(config.valign) != 'undefined' && config.valign.length){
8596 td.style += ' vertical-align:' + config.valign + ';';
8599 if(typeof(config.width) != 'undefined'){
8600 td.style += ' width:' + config.width + 'px;';
8603 if(typeof(config.cursor) != 'undefined'){
8604 td.style += ' cursor:' + config.cursor + ';';
8607 if(typeof(config.cls) != 'undefined'){
8608 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611 ['xs','sm','md','lg'].map(function(size){
8613 if(typeof(config[size]) == 'undefined'){
8619 if (!config[size]) { // 0 = hidden
8620 // BS 4 '0' is treated as hide that column and below.
8621 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8625 td.cls += ' col-' + size + '-' + config[size] + (
8626 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8636 row.cellObjects = cellObjects;
8644 onBeforeLoad : function()
8653 this.el.select('tbody', true).first().dom.innerHTML = '';
8656 * Show or hide a row.
8657 * @param {Number} rowIndex to show or hide
8658 * @param {Boolean} state hide
8660 setRowVisibility : function(rowIndex, state)
8662 var bt = this.mainBody.dom;
8664 var rows = this.el.select('tbody > tr', true).elements;
8666 if(typeof(rows[rowIndex]) == 'undefined'){
8669 rows[rowIndex].dom.style.display = state ? '' : 'none';
8673 getSelectionModel : function(){
8675 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8677 return this.selModel;
8680 * Render the Roo.bootstrap object from renderder
8682 renderCellObject : function(r)
8686 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8688 var t = r.cfg.render(r.container);
8691 Roo.each(r.cfg.cn, function(c){
8693 container: t.getChildContainer(),
8696 _this.renderCellObject(child);
8701 getRowIndex : function(row)
8705 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8716 * Returns the grid's underlying element = used by panel.Grid
8717 * @return {Element} The element
8719 getGridEl : function(){
8723 * Forces a resize - used by panel.Grid
8724 * @return {Element} The element
8726 autoSize : function()
8728 //var ctr = Roo.get(this.container.dom.parentElement);
8729 var ctr = Roo.get(this.el.dom);
8731 var thd = this.getGridEl().select('thead',true).first();
8732 var tbd = this.getGridEl().select('tbody', true).first();
8733 var tfd = this.getGridEl().select('tfoot', true).first();
8735 var cw = ctr.getWidth();
8736 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8740 tbd.setWidth(ctr.getWidth());
8741 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8742 // this needs fixing for various usage - currently only hydra job advers I think..
8744 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8746 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749 cw = Math.max(cw, this.totalWidth);
8750 this.getGridEl().select('tbody tr',true).setWidth(cw);
8752 // resize 'expandable coloumn?
8754 return; // we doe not have a view in this design..
8757 onBodyScroll: function()
8759 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8761 this.mainHead.setStyle({
8762 'position' : 'relative',
8763 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769 var scrollHeight = this.mainBody.dom.scrollHeight;
8771 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8773 var height = this.mainBody.getHeight();
8775 if(scrollHeight - height == scrollTop) {
8777 var total = this.ds.getTotalCount();
8779 if(this.footer.cursor + this.footer.pageSize < total){
8781 this.footer.ds.load({
8783 start : this.footer.cursor + this.footer.pageSize,
8784 limit : this.footer.pageSize
8794 onHeaderChange : function()
8796 var header = this.renderHeader();
8797 var table = this.el.select('table', true).first();
8799 this.mainHead.remove();
8800 this.mainHead = table.createChild(header, this.mainBody, false);
8803 onHiddenChange : function(colModel, colIndex, hidden)
8805 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8806 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8808 this.CSS.updateRule(thSelector, "display", "");
8809 this.CSS.updateRule(tdSelector, "display", "");
8812 this.CSS.updateRule(thSelector, "display", "none");
8813 this.CSS.updateRule(tdSelector, "display", "none");
8816 this.onHeaderChange();
8820 setColumnWidth: function(col_index, width)
8822 // width = "md-2 xs-2..."
8823 if(!this.colModel.config[col_index]) {
8827 var w = width.split(" ");
8829 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8831 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834 for(var j = 0; j < w.length; j++) {
8840 var size_cls = w[j].split("-");
8842 if(!Number.isInteger(size_cls[1] * 1)) {
8846 if(!this.colModel.config[col_index][size_cls[0]]) {
8850 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8854 h_row[0].classList.replace(
8855 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8856 "col-"+size_cls[0]+"-"+size_cls[1]
8859 for(var i = 0; i < rows.length; i++) {
8861 var size_cls = w[j].split("-");
8863 if(!Number.isInteger(size_cls[1] * 1)) {
8867 if(!this.colModel.config[col_index][size_cls[0]]) {
8871 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8875 rows[i].classList.replace(
8876 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8877 "col-"+size_cls[0]+"-"+size_cls[1]
8881 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8896 * @class Roo.bootstrap.TableCell
8897 * @extends Roo.bootstrap.Component
8898 * Bootstrap TableCell class
8899 * @cfg {String} html cell contain text
8900 * @cfg {String} cls cell class
8901 * @cfg {String} tag cell tag (td|th) default td
8902 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8903 * @cfg {String} align Aligns the content in a cell
8904 * @cfg {String} axis Categorizes cells
8905 * @cfg {String} bgcolor Specifies the background color of a cell
8906 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8907 * @cfg {Number} colspan Specifies the number of columns a cell should span
8908 * @cfg {String} headers Specifies one or more header cells a cell is related to
8909 * @cfg {Number} height Sets the height of a cell
8910 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8911 * @cfg {Number} rowspan Sets the number of rows a cell should span
8912 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8913 * @cfg {String} valign Vertical aligns the content in a cell
8914 * @cfg {Number} width Specifies the width of a cell
8917 * Create a new TableCell
8918 * @param {Object} config The config object
8921 Roo.bootstrap.TableCell = function(config){
8922 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8945 getAutoCreate : function(){
8946 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8966 cfg.align=this.align
8972 cfg.bgcolor=this.bgcolor
8975 cfg.charoff=this.charoff
8978 cfg.colspan=this.colspan
8981 cfg.headers=this.headers
8984 cfg.height=this.height
8987 cfg.nowrap=this.nowrap
8990 cfg.rowspan=this.rowspan
8993 cfg.scope=this.scope
8996 cfg.valign=this.valign
8999 cfg.width=this.width
9018 * @class Roo.bootstrap.TableRow
9019 * @extends Roo.bootstrap.Component
9020 * Bootstrap TableRow class
9021 * @cfg {String} cls row class
9022 * @cfg {String} align Aligns the content in a table row
9023 * @cfg {String} bgcolor Specifies a background color for a table row
9024 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9025 * @cfg {String} valign Vertical aligns the content in a table row
9028 * Create a new TableRow
9029 * @param {Object} config The config object
9032 Roo.bootstrap.TableRow = function(config){
9033 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9044 getAutoCreate : function(){
9045 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9055 cfg.align = this.align;
9058 cfg.bgcolor = this.bgcolor;
9061 cfg.charoff = this.charoff;
9064 cfg.valign = this.valign;
9082 * @class Roo.bootstrap.TableBody
9083 * @extends Roo.bootstrap.Component
9084 * Bootstrap TableBody class
9085 * @cfg {String} cls element class
9086 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9087 * @cfg {String} align Aligns the content inside the element
9088 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9089 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092 * Create a new TableBody
9093 * @param {Object} config The config object
9096 Roo.bootstrap.TableBody = function(config){
9097 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9108 getAutoCreate : function(){
9109 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9123 cfg.align = this.align;
9126 cfg.charoff = this.charoff;
9129 cfg.valign = this.valign;
9136 // initEvents : function()
9143 // this.store = Roo.factory(this.store, Roo.data);
9144 // this.store.on('load', this.onLoad, this);
9146 // this.store.load();
9150 // onLoad: function ()
9152 // this.fireEvent('load', this);
9162 * Ext JS Library 1.1.1
9163 * Copyright(c) 2006-2007, Ext JS, LLC.
9165 * Originally Released Under LGPL - original licence link has changed is not relivant.
9168 * <script type="text/javascript">
9171 // as we use this in bootstrap.
9172 Roo.namespace('Roo.form');
9174 * @class Roo.form.Action
9175 * Internal Class used to handle form actions
9177 * @param {Roo.form.BasicForm} el The form element or its id
9178 * @param {Object} config Configuration options
9183 // define the action interface
9184 Roo.form.Action = function(form, options){
9186 this.options = options || {};
9189 * Client Validation Failed
9192 Roo.form.Action.CLIENT_INVALID = 'client';
9194 * Server Validation Failed
9197 Roo.form.Action.SERVER_INVALID = 'server';
9199 * Connect to Server Failed
9202 Roo.form.Action.CONNECT_FAILURE = 'connect';
9204 * Reading Data from Server Failed
9207 Roo.form.Action.LOAD_FAILURE = 'load';
9209 Roo.form.Action.prototype = {
9211 failureType : undefined,
9212 response : undefined,
9216 run : function(options){
9221 success : function(response){
9226 handleResponse : function(response){
9230 // default connection failure
9231 failure : function(response){
9233 this.response = response;
9234 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9235 this.form.afterAction(this, false);
9238 processResponse : function(response){
9239 this.response = response;
9240 if(!response.responseText){
9243 this.result = this.handleResponse(response);
9247 // utility functions used internally
9248 getUrl : function(appendParams){
9249 var url = this.options.url || this.form.url || this.form.el.dom.action;
9251 var p = this.getParams();
9253 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259 getMethod : function(){
9260 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263 getParams : function(){
9264 var bp = this.form.baseParams;
9265 var p = this.options.params;
9267 if(typeof p == "object"){
9268 p = Roo.urlEncode(Roo.applyIf(p, bp));
9269 }else if(typeof p == 'string' && bp){
9270 p += '&' + Roo.urlEncode(bp);
9273 p = Roo.urlEncode(bp);
9278 createCallback : function(){
9280 success: this.success,
9281 failure: this.failure,
9283 timeout: (this.form.timeout*1000),
9284 upload: this.form.fileUpload ? this.success : undefined
9289 Roo.form.Action.Submit = function(form, options){
9290 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296 haveProgress : false,
9297 uploadComplete : false,
9299 // uploadProgress indicator.
9300 uploadProgress : function()
9302 if (!this.form.progressUrl) {
9306 if (!this.haveProgress) {
9307 Roo.MessageBox.progress("Uploading", "Uploading");
9309 if (this.uploadComplete) {
9310 Roo.MessageBox.hide();
9314 this.haveProgress = true;
9316 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9318 var c = new Roo.data.Connection();
9320 url : this.form.progressUrl,
9325 success : function(req){
9326 //console.log(data);
9330 rdata = Roo.decode(req.responseText)
9332 Roo.log("Invalid data from server..");
9336 if (!rdata || !rdata.success) {
9338 Roo.MessageBox.alert(Roo.encode(rdata));
9341 var data = rdata.data;
9343 if (this.uploadComplete) {
9344 Roo.MessageBox.hide();
9349 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9350 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353 this.uploadProgress.defer(2000,this);
9356 failure: function(data) {
9357 Roo.log('progress url failed ');
9368 // run get Values on the form, so it syncs any secondary forms.
9369 this.form.getValues();
9371 var o = this.options;
9372 var method = this.getMethod();
9373 var isPost = method == 'POST';
9374 if(o.clientValidation === false || this.form.isValid()){
9376 if (this.form.progressUrl) {
9377 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9378 (new Date() * 1) + '' + Math.random());
9383 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9384 form:this.form.el.dom,
9385 url:this.getUrl(!isPost),
9387 params:isPost ? this.getParams() : null,
9388 isUpload: this.form.fileUpload,
9389 formData : this.form.formData
9392 this.uploadProgress();
9394 }else if (o.clientValidation !== false){ // client validation failed
9395 this.failureType = Roo.form.Action.CLIENT_INVALID;
9396 this.form.afterAction(this, false);
9400 success : function(response)
9402 this.uploadComplete= true;
9403 if (this.haveProgress) {
9404 Roo.MessageBox.hide();
9408 var result = this.processResponse(response);
9409 if(result === true || result.success){
9410 this.form.afterAction(this, true);
9414 this.form.markInvalid(result.errors);
9415 this.failureType = Roo.form.Action.SERVER_INVALID;
9417 this.form.afterAction(this, false);
9419 failure : function(response)
9421 this.uploadComplete= true;
9422 if (this.haveProgress) {
9423 Roo.MessageBox.hide();
9426 this.response = response;
9427 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9428 this.form.afterAction(this, false);
9431 handleResponse : function(response){
9432 if(this.form.errorReader){
9433 var rs = this.form.errorReader.read(response);
9436 for(var i = 0, len = rs.records.length; i < len; i++) {
9437 var r = rs.records[i];
9441 if(errors.length < 1){
9445 success : rs.success,
9451 ret = Roo.decode(response.responseText);
9455 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9465 Roo.form.Action.Load = function(form, options){
9466 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9467 this.reader = this.form.reader;
9470 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9475 Roo.Ajax.request(Roo.apply(
9476 this.createCallback(), {
9477 method:this.getMethod(),
9478 url:this.getUrl(false),
9479 params:this.getParams()
9483 success : function(response){
9485 var result = this.processResponse(response);
9486 if(result === true || !result.success || !result.data){
9487 this.failureType = Roo.form.Action.LOAD_FAILURE;
9488 this.form.afterAction(this, false);
9491 this.form.clearInvalid();
9492 this.form.setValues(result.data);
9493 this.form.afterAction(this, true);
9496 handleResponse : function(response){
9497 if(this.form.reader){
9498 var rs = this.form.reader.read(response);
9499 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9501 success : rs.success,
9505 return Roo.decode(response.responseText);
9509 Roo.form.Action.ACTION_TYPES = {
9510 'load' : Roo.form.Action.Load,
9511 'submit' : Roo.form.Action.Submit
9520 * @class Roo.bootstrap.Form
9521 * @extends Roo.bootstrap.Component
9522 * Bootstrap Form class
9523 * @cfg {String} method GET | POST (default POST)
9524 * @cfg {String} labelAlign top | left (default top)
9525 * @cfg {String} align left | right - for navbars
9526 * @cfg {Boolean} loadMask load mask when submit (default true)
9531 * @param {Object} config The config object
9535 Roo.bootstrap.Form = function(config){
9537 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9539 Roo.bootstrap.Form.popover.apply();
9543 * @event clientvalidation
9544 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9545 * @param {Form} this
9546 * @param {Boolean} valid true if the form has passed client-side validation
9548 clientvalidation: true,
9550 * @event beforeaction
9551 * Fires before any action is performed. Return false to cancel the action.
9552 * @param {Form} this
9553 * @param {Action} action The action to be performed
9557 * @event actionfailed
9558 * Fires when an action fails.
9559 * @param {Form} this
9560 * @param {Action} action The action that failed
9562 actionfailed : true,
9564 * @event actioncomplete
9565 * Fires when an action is completed.
9566 * @param {Form} this
9567 * @param {Action} action The action that completed
9569 actioncomplete : true
9573 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9576 * @cfg {String} method
9577 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9582 * The URL to use for form actions if one isn't supplied in the action options.
9585 * @cfg {Boolean} fileUpload
9586 * Set to true if this form is a file upload.
9590 * @cfg {Object} baseParams
9591 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9595 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9599 * @cfg {Sting} align (left|right) for navbar forms
9604 activeAction : null,
9607 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9608 * element by passing it or its id or mask the form itself by passing in true.
9611 waitMsgTarget : false,
9616 * @cfg {Boolean} errorMask (true|false) default false
9621 * @cfg {Number} maskOffset Default 100
9626 * @cfg {Boolean} maskBody
9630 getAutoCreate : function(){
9634 method : this.method || 'POST',
9635 id : this.id || Roo.id(),
9638 if (this.parent().xtype.match(/^Nav/)) {
9639 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9643 if (this.labelAlign == 'left' ) {
9644 cfg.cls += ' form-horizontal';
9650 initEvents : function()
9652 this.el.on('submit', this.onSubmit, this);
9653 // this was added as random key presses on the form where triggering form submit.
9654 this.el.on('keypress', function(e) {
9655 if (e.getCharCode() != 13) {
9658 // we might need to allow it for textareas.. and some other items.
9659 // check e.getTarget().
9661 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9665 Roo.log("keypress blocked");
9673 onSubmit : function(e){
9678 * Returns true if client-side validation on the form is successful.
9681 isValid : function(){
9682 var items = this.getItems();
9686 items.each(function(f){
9692 Roo.log('invalid field: ' + f.name);
9696 if(!target && f.el.isVisible(true)){
9702 if(this.errorMask && !valid){
9703 Roo.bootstrap.Form.popover.mask(this, target);
9710 * Returns true if any fields in this form have changed since their original load.
9713 isDirty : function(){
9715 var items = this.getItems();
9716 items.each(function(f){
9726 * Performs a predefined action (submit or load) or custom actions you define on this form.
9727 * @param {String} actionName The name of the action type
9728 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9729 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9730 * accept other config options):
9732 Property Type Description
9733 ---------------- --------------- ----------------------------------------------------------------------------------
9734 url String The url for the action (defaults to the form's url)
9735 method String The form method to use (defaults to the form's method, or POST if not defined)
9736 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9737 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9738 validate the form on the client (defaults to false)
9740 * @return {BasicForm} this
9742 doAction : function(action, options){
9743 if(typeof action == 'string'){
9744 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9746 if(this.fireEvent('beforeaction', this, action) !== false){
9747 this.beforeAction(action);
9748 action.run.defer(100, action);
9754 beforeAction : function(action){
9755 var o = action.options;
9760 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9762 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765 // not really supported yet.. ??
9767 //if(this.waitMsgTarget === true){
9768 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769 //}else if(this.waitMsgTarget){
9770 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9771 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9773 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779 afterAction : function(action, success){
9780 this.activeAction = null;
9781 var o = action.options;
9786 Roo.get(document.body).unmask();
9792 //if(this.waitMsgTarget === true){
9793 // this.el.unmask();
9794 //}else if(this.waitMsgTarget){
9795 // this.waitMsgTarget.unmask();
9797 // Roo.MessageBox.updateProgress(1);
9798 // Roo.MessageBox.hide();
9805 Roo.callback(o.success, o.scope, [this, action]);
9806 this.fireEvent('actioncomplete', this, action);
9810 // failure condition..
9811 // we have a scenario where updates need confirming.
9812 // eg. if a locking scenario exists..
9813 // we look for { errors : { needs_confirm : true }} in the response.
9815 (typeof(action.result) != 'undefined') &&
9816 (typeof(action.result.errors) != 'undefined') &&
9817 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820 Roo.log("not supported yet");
9823 Roo.MessageBox.confirm(
9824 "Change requires confirmation",
9825 action.result.errorMsg,
9830 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9840 Roo.callback(o.failure, o.scope, [this, action]);
9841 // show an error message if no failed handler is set..
9842 if (!this.hasListener('actionfailed')) {
9843 Roo.log("need to add dialog support");
9845 Roo.MessageBox.alert("Error",
9846 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9847 action.result.errorMsg :
9848 "Saving Failed, please check your entries or try again"
9853 this.fireEvent('actionfailed', this, action);
9858 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9859 * @param {String} id The value to search for
9862 findField : function(id){
9863 var items = this.getItems();
9864 var field = items.get(id);
9866 items.each(function(f){
9867 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9874 return field || null;
9877 * Mark fields in this form invalid in bulk.
9878 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9879 * @return {BasicForm} this
9881 markInvalid : function(errors){
9882 if(errors instanceof Array){
9883 for(var i = 0, len = errors.length; i < len; i++){
9884 var fieldError = errors[i];
9885 var f = this.findField(fieldError.id);
9887 f.markInvalid(fieldError.msg);
9893 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9894 field.markInvalid(errors[id]);
9898 //Roo.each(this.childForms || [], function (f) {
9899 // f.markInvalid(errors);
9906 * Set values for fields in this form in bulk.
9907 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9908 * @return {BasicForm} this
9910 setValues : function(values){
9911 if(values instanceof Array){ // array of objects
9912 for(var i = 0, len = values.length; i < len; i++){
9914 var f = this.findField(v.id);
9916 f.setValue(v.value);
9917 if(this.trackResetOnLoad){
9918 f.originalValue = f.getValue();
9922 }else{ // object hash
9925 if(typeof values[id] != 'function' && (field = this.findField(id))){
9927 if (field.setFromData &&
9929 field.displayField &&
9930 // combos' with local stores can
9931 // be queried via setValue()
9932 // to set their value..
9933 (field.store && !field.store.isLocal)
9937 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9938 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9939 field.setFromData(sd);
9941 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9943 field.setFromData(values);
9946 field.setValue(values[id]);
9950 if(this.trackResetOnLoad){
9951 field.originalValue = field.getValue();
9957 //Roo.each(this.childForms || [], function (f) {
9958 // f.setValues(values);
9965 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9966 * they are returned as an array.
9967 * @param {Boolean} asString
9970 getValues : function(asString){
9971 //if (this.childForms) {
9972 // copy values from the child forms
9973 // Roo.each(this.childForms, function (f) {
9974 // this.setValues(f.getValues());
9980 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9981 if(asString === true){
9984 return Roo.urlDecode(fs);
9988 * Returns the fields in this form as an object with key/value pairs.
9989 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992 getFieldValues : function(with_hidden)
9994 var items = this.getItems();
9996 items.each(function(f){
10002 var v = f.getValue();
10004 if (f.inputType =='radio') {
10005 if (typeof(ret[f.getName()]) == 'undefined') {
10006 ret[f.getName()] = ''; // empty..
10009 if (!f.el.dom.checked) {
10013 v = f.el.dom.value;
10017 if(f.xtype == 'MoneyField'){
10018 ret[f.currencyName] = f.getCurrency();
10021 // not sure if this supported any more..
10022 if ((typeof(v) == 'object') && f.getRawValue) {
10023 v = f.getRawValue() ; // dates..
10025 // combo boxes where name != hiddenName...
10026 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10027 ret[f.name] = f.getRawValue();
10029 ret[f.getName()] = v;
10036 * Clears all invalid messages in this form.
10037 * @return {BasicForm} this
10039 clearInvalid : function(){
10040 var items = this.getItems();
10042 items.each(function(f){
10050 * Resets this form.
10051 * @return {BasicForm} this
10053 reset : function(){
10054 var items = this.getItems();
10055 items.each(function(f){
10059 Roo.each(this.childForms || [], function (f) {
10067 getItems : function()
10069 var r=new Roo.util.MixedCollection(false, function(o){
10070 return o.id || (o.id = Roo.id());
10072 var iter = function(el) {
10079 Roo.each(el.items,function(e) {
10088 hideFields : function(items)
10090 Roo.each(items, function(i){
10092 var f = this.findField(i);
10103 showFields : function(items)
10105 Roo.each(items, function(i){
10107 var f = this.findField(i);
10120 Roo.apply(Roo.bootstrap.Form, {
10136 intervalID : false,
10142 if(this.isApplied){
10147 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10148 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10149 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10150 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153 this.maskEl.top.enableDisplayMode("block");
10154 this.maskEl.left.enableDisplayMode("block");
10155 this.maskEl.bottom.enableDisplayMode("block");
10156 this.maskEl.right.enableDisplayMode("block");
10158 this.toolTip = new Roo.bootstrap.Tooltip({
10159 cls : 'roo-form-error-popover',
10161 'left' : ['r-l', [-2,0], 'right'],
10162 'right' : ['l-r', [2,0], 'left'],
10163 'bottom' : ['tl-bl', [0,2], 'top'],
10164 'top' : [ 'bl-tl', [0,-2], 'bottom']
10168 this.toolTip.render(Roo.get(document.body));
10170 this.toolTip.el.enableDisplayMode("block");
10172 Roo.get(document.body).on('click', function(){
10176 Roo.get(document.body).on('touchstart', function(){
10180 this.isApplied = true
10183 mask : function(form, target)
10187 this.target = target;
10189 if(!this.form.errorMask || !target.el){
10193 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10195 Roo.log(scrollable);
10197 var ot = this.target.el.calcOffsetsTo(scrollable);
10199 var scrollTo = ot[1] - this.form.maskOffset;
10201 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10203 scrollable.scrollTo('top', scrollTo);
10205 var box = this.target.el.getBox();
10207 var zIndex = Roo.bootstrap.Modal.zIndex++;
10210 this.maskEl.top.setStyle('position', 'absolute');
10211 this.maskEl.top.setStyle('z-index', zIndex);
10212 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10213 this.maskEl.top.setLeft(0);
10214 this.maskEl.top.setTop(0);
10215 this.maskEl.top.show();
10217 this.maskEl.left.setStyle('position', 'absolute');
10218 this.maskEl.left.setStyle('z-index', zIndex);
10219 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10220 this.maskEl.left.setLeft(0);
10221 this.maskEl.left.setTop(box.y - this.padding);
10222 this.maskEl.left.show();
10224 this.maskEl.bottom.setStyle('position', 'absolute');
10225 this.maskEl.bottom.setStyle('z-index', zIndex);
10226 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10227 this.maskEl.bottom.setLeft(0);
10228 this.maskEl.bottom.setTop(box.bottom + this.padding);
10229 this.maskEl.bottom.show();
10231 this.maskEl.right.setStyle('position', 'absolute');
10232 this.maskEl.right.setStyle('z-index', zIndex);
10233 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10234 this.maskEl.right.setLeft(box.right + this.padding);
10235 this.maskEl.right.setTop(box.y - this.padding);
10236 this.maskEl.right.show();
10238 this.toolTip.bindEl = this.target.el;
10240 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10242 var tip = this.target.blankText;
10244 if(this.target.getValue() !== '' ) {
10246 if (this.target.invalidText.length) {
10247 tip = this.target.invalidText;
10248 } else if (this.target.regexText.length){
10249 tip = this.target.regexText;
10253 this.toolTip.show(tip);
10255 this.intervalID = window.setInterval(function() {
10256 Roo.bootstrap.Form.popover.unmask();
10259 window.onwheel = function(){ return false;};
10261 (function(){ this.isMasked = true; }).defer(500, this);
10265 unmask : function()
10267 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10271 this.maskEl.top.setStyle('position', 'absolute');
10272 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10273 this.maskEl.top.hide();
10275 this.maskEl.left.setStyle('position', 'absolute');
10276 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10277 this.maskEl.left.hide();
10279 this.maskEl.bottom.setStyle('position', 'absolute');
10280 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10281 this.maskEl.bottom.hide();
10283 this.maskEl.right.setStyle('position', 'absolute');
10284 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10285 this.maskEl.right.hide();
10287 this.toolTip.hide();
10289 this.toolTip.el.hide();
10291 window.onwheel = function(){ return true;};
10293 if(this.intervalID){
10294 window.clearInterval(this.intervalID);
10295 this.intervalID = false;
10298 this.isMasked = false;
10308 * Ext JS Library 1.1.1
10309 * Copyright(c) 2006-2007, Ext JS, LLC.
10311 * Originally Released Under LGPL - original licence link has changed is not relivant.
10314 * <script type="text/javascript">
10317 * @class Roo.form.VTypes
10318 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321 Roo.form.VTypes = function(){
10322 // closure these in so they are only created once.
10323 var alpha = /^[a-zA-Z_]+$/;
10324 var alphanum = /^[a-zA-Z0-9_]+$/;
10325 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10326 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10328 // All these messages and functions are configurable
10331 * The function used to validate email addresses
10332 * @param {String} value The email address
10334 'email' : function(v){
10335 return email.test(v);
10338 * The error text to display when the email validation function returns false
10341 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10343 * The keystroke filter mask to be applied on email input
10346 'emailMask' : /[a-z0-9_\.\-@]/i,
10349 * The function used to validate URLs
10350 * @param {String} value The URL
10352 'url' : function(v){
10353 return url.test(v);
10356 * The error text to display when the url validation function returns false
10359 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362 * The function used to validate alpha values
10363 * @param {String} value The value
10365 'alpha' : function(v){
10366 return alpha.test(v);
10369 * The error text to display when the alpha validation function returns false
10372 'alphaText' : 'This field should only contain letters and _',
10374 * The keystroke filter mask to be applied on alpha input
10377 'alphaMask' : /[a-z_]/i,
10380 * The function used to validate alphanumeric values
10381 * @param {String} value The value
10383 'alphanum' : function(v){
10384 return alphanum.test(v);
10387 * The error text to display when the alphanumeric validation function returns false
10390 'alphanumText' : 'This field should only contain letters, numbers and _',
10392 * The keystroke filter mask to be applied on alphanumeric input
10395 'alphanumMask' : /[a-z0-9_]/i
10405 * @class Roo.bootstrap.Input
10406 * @extends Roo.bootstrap.Component
10407 * Bootstrap Input class
10408 * @cfg {Boolean} disabled is it disabled
10409 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10410 * @cfg {String} name name of the input
10411 * @cfg {string} fieldLabel - the label associated
10412 * @cfg {string} placeholder - placeholder to put in text.
10413 * @cfg {string} before - input group add on before
10414 * @cfg {string} after - input group add on after
10415 * @cfg {string} size - (lg|sm) or leave empty..
10416 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10417 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10418 * @cfg {Number} md colspan out of 12 for computer-sized screens
10419 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10420 * @cfg {string} value default value of the input
10421 * @cfg {Number} labelWidth set the width of label
10422 * @cfg {Number} labellg set the width of label (1-12)
10423 * @cfg {Number} labelmd set the width of label (1-12)
10424 * @cfg {Number} labelsm set the width of label (1-12)
10425 * @cfg {Number} labelxs set the width of label (1-12)
10426 * @cfg {String} labelAlign (top|left)
10427 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10428 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10429 * @cfg {String} indicatorpos (left|right) default left
10430 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10431 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10432 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10434 * @cfg {String} align (left|center|right) Default left
10435 * @cfg {Boolean} forceFeedback (true|false) Default false
10438 * Create a new Input
10439 * @param {Object} config The config object
10442 Roo.bootstrap.Input = function(config){
10444 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10449 * Fires when this field receives input focus.
10450 * @param {Roo.form.Field} this
10455 * Fires when this field loses input focus.
10456 * @param {Roo.form.Field} this
10460 * @event specialkey
10461 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10462 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10463 * @param {Roo.form.Field} this
10464 * @param {Roo.EventObject} e The event object
10469 * Fires just before the field blurs if the field value has changed.
10470 * @param {Roo.form.Field} this
10471 * @param {Mixed} newValue The new value
10472 * @param {Mixed} oldValue The original value
10477 * Fires after the field has been marked as invalid.
10478 * @param {Roo.form.Field} this
10479 * @param {String} msg The validation message
10484 * Fires after the field has been validated with no errors.
10485 * @param {Roo.form.Field} this
10490 * Fires after the key up
10491 * @param {Roo.form.Field} this
10492 * @param {Roo.EventObject} e The event Object
10498 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10500 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10501 automatic validation (defaults to "keyup").
10503 validationEvent : "keyup",
10505 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10507 validateOnBlur : true,
10509 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10511 validationDelay : 250,
10513 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10515 focusClass : "x-form-focus", // not needed???
10519 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10521 invalidClass : "has-warning",
10524 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10526 validClass : "has-success",
10529 * @cfg {Boolean} hasFeedback (true|false) default true
10531 hasFeedback : true,
10534 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10536 invalidFeedbackClass : "glyphicon-warning-sign",
10539 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10541 validFeedbackClass : "glyphicon-ok",
10544 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10546 selectOnFocus : false,
10549 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10553 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10558 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10560 disableKeyFilter : false,
10563 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10567 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10571 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10573 blankText : "Please complete this mandatory field",
10576 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10580 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10582 maxLength : Number.MAX_VALUE,
10584 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10586 minLengthText : "The minimum length for this field is {0}",
10588 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10590 maxLengthText : "The maximum length for this field is {0}",
10594 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10595 * If available, this function will be called only after the basic validators all return true, and will be passed the
10596 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10600 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10601 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10602 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10606 * @cfg {String} regexText -- Depricated - use Invalid Text
10611 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10617 autocomplete: false,
10621 inputType : 'text',
10624 placeholder: false,
10629 preventMark: false,
10630 isFormField : true,
10633 labelAlign : false,
10636 formatedValue : false,
10637 forceFeedback : false,
10639 indicatorpos : 'left',
10649 parentLabelAlign : function()
10652 while (parent.parent()) {
10653 parent = parent.parent();
10654 if (typeof(parent.labelAlign) !='undefined') {
10655 return parent.labelAlign;
10662 getAutoCreate : function()
10664 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10670 if(this.inputType != 'hidden'){
10671 cfg.cls = 'form-group' //input-group
10677 type : this.inputType,
10678 value : this.value,
10679 cls : 'form-control',
10680 placeholder : this.placeholder || '',
10681 autocomplete : this.autocomplete || 'new-password'
10683 if (this.inputType == 'file') {
10684 input.style = 'overflow:hidden'; // why not in CSS?
10687 if(this.capture.length){
10688 input.capture = this.capture;
10691 if(this.accept.length){
10692 input.accept = this.accept + "/*";
10696 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10700 input.maxLength = this.maxLength;
10703 if (this.disabled) {
10704 input.disabled=true;
10707 if (this.readOnly) {
10708 input.readonly=true;
10712 input.name = this.name;
10716 input.cls += ' input-' + this.size;
10720 ['xs','sm','md','lg'].map(function(size){
10721 if (settings[size]) {
10722 cfg.cls += ' col-' + size + '-' + settings[size];
10726 var inputblock = input;
10730 cls: 'glyphicon form-control-feedback'
10733 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736 cls : 'has-feedback',
10744 if (this.before || this.after) {
10747 cls : 'input-group',
10751 if (this.before && typeof(this.before) == 'string') {
10753 inputblock.cn.push({
10755 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10759 if (this.before && typeof(this.before) == 'object') {
10760 this.before = Roo.factory(this.before);
10762 inputblock.cn.push({
10764 cls : 'roo-input-before input-group-prepend input-group-' +
10765 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10769 inputblock.cn.push(input);
10771 if (this.after && typeof(this.after) == 'string') {
10772 inputblock.cn.push({
10774 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10778 if (this.after && typeof(this.after) == 'object') {
10779 this.after = Roo.factory(this.after);
10781 inputblock.cn.push({
10783 cls : 'roo-input-after input-group-append input-group-' +
10784 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10788 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10789 inputblock.cls += ' has-feedback';
10790 inputblock.cn.push(feedback);
10795 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10796 tooltip : 'This field is required'
10798 if (this.allowBlank ) {
10799 indicator.style = this.allowBlank ? ' display:none' : '';
10801 if (align ==='left' && this.fieldLabel.length) {
10803 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10810 cls : 'control-label col-form-label',
10811 html : this.fieldLabel
10822 var labelCfg = cfg.cn[1];
10823 var contentCfg = cfg.cn[2];
10825 if(this.indicatorpos == 'right'){
10830 cls : 'control-label col-form-label',
10834 html : this.fieldLabel
10848 labelCfg = cfg.cn[0];
10849 contentCfg = cfg.cn[1];
10853 if(this.labelWidth > 12){
10854 labelCfg.style = "width: " + this.labelWidth + 'px';
10857 if(this.labelWidth < 13 && this.labelmd == 0){
10858 this.labelmd = this.labelWidth;
10861 if(this.labellg > 0){
10862 labelCfg.cls += ' col-lg-' + this.labellg;
10863 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866 if(this.labelmd > 0){
10867 labelCfg.cls += ' col-md-' + this.labelmd;
10868 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871 if(this.labelsm > 0){
10872 labelCfg.cls += ' col-sm-' + this.labelsm;
10873 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876 if(this.labelxs > 0){
10877 labelCfg.cls += ' col-xs-' + this.labelxs;
10878 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10882 } else if ( this.fieldLabel.length) {
10889 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10890 tooltip : 'This field is required',
10891 style : this.allowBlank ? ' display:none' : ''
10895 //cls : 'input-group-addon',
10896 html : this.fieldLabel
10904 if(this.indicatorpos == 'right'){
10909 //cls : 'input-group-addon',
10910 html : this.fieldLabel
10915 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10916 tooltip : 'This field is required',
10917 style : this.allowBlank ? ' display:none' : ''
10937 if (this.parentType === 'Navbar' && this.parent().bar) {
10938 cfg.cls += ' navbar-form';
10941 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10942 // on BS4 we do this only if not form
10943 cfg.cls += ' navbar-form';
10951 * return the real input element.
10953 inputEl: function ()
10955 return this.el.select('input.form-control',true).first();
10958 tooltipEl : function()
10960 return this.inputEl();
10963 indicatorEl : function()
10965 if (Roo.bootstrap.version == 4) {
10966 return false; // not enabled in v4 yet.
10969 var indicator = this.el.select('i.roo-required-indicator',true).first();
10979 setDisabled : function(v)
10981 var i = this.inputEl().dom;
10983 i.removeAttribute('disabled');
10987 i.setAttribute('disabled','true');
10989 initEvents : function()
10992 this.inputEl().on("keydown" , this.fireKey, this);
10993 this.inputEl().on("focus", this.onFocus, this);
10994 this.inputEl().on("blur", this.onBlur, this);
10996 this.inputEl().relayEvent('keyup', this);
10998 this.indicator = this.indicatorEl();
11000 if(this.indicator){
11001 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11004 // reference to original value for reset
11005 this.originalValue = this.getValue();
11006 //Roo.form.TextField.superclass.initEvents.call(this);
11007 if(this.validationEvent == 'keyup'){
11008 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11009 this.inputEl().on('keyup', this.filterValidation, this);
11011 else if(this.validationEvent !== false){
11012 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015 if(this.selectOnFocus){
11016 this.on("focus", this.preFocus, this);
11019 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11020 this.inputEl().on("keypress", this.filterKeys, this);
11022 this.inputEl().relayEvent('keypress', this);
11025 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11026 this.el.on("click", this.autoSize, this);
11029 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11030 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033 if (typeof(this.before) == 'object') {
11034 this.before.render(this.el.select('.roo-input-before',true).first());
11036 if (typeof(this.after) == 'object') {
11037 this.after.render(this.el.select('.roo-input-after',true).first());
11040 this.inputEl().on('change', this.onChange, this);
11043 filterValidation : function(e){
11044 if(!e.isNavKeyPress()){
11045 this.validationTask.delay(this.validationDelay);
11049 * Validates the field value
11050 * @return {Boolean} True if the value is valid, else false
11052 validate : function(){
11053 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11054 if(this.disabled || this.validateValue(this.getRawValue())){
11059 this.markInvalid();
11065 * Validates a value according to the field's validation rules and marks the field as invalid
11066 * if the validation fails
11067 * @param {Mixed} value The value to validate
11068 * @return {Boolean} True if the value is valid, else false
11070 validateValue : function(value)
11072 if(this.getVisibilityEl().hasClass('hidden')){
11076 if(value.length < 1) { // if it's blank
11077 if(this.allowBlank){
11083 if(value.length < this.minLength){
11086 if(value.length > this.maxLength){
11090 var vt = Roo.form.VTypes;
11091 if(!vt[this.vtype](value, this)){
11095 if(typeof this.validator == "function"){
11096 var msg = this.validator(value);
11100 if (typeof(msg) == 'string') {
11101 this.invalidText = msg;
11105 if(this.regex && !this.regex.test(value)){
11113 fireKey : function(e){
11114 //Roo.log('field ' + e.getKey());
11115 if(e.isNavKeyPress()){
11116 this.fireEvent("specialkey", this, e);
11119 focus : function (selectText){
11121 this.inputEl().focus();
11122 if(selectText === true){
11123 this.inputEl().dom.select();
11129 onFocus : function(){
11130 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11131 // this.el.addClass(this.focusClass);
11133 if(!this.hasFocus){
11134 this.hasFocus = true;
11135 this.startValue = this.getValue();
11136 this.fireEvent("focus", this);
11140 beforeBlur : Roo.emptyFn,
11144 onBlur : function(){
11146 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11147 //this.el.removeClass(this.focusClass);
11149 this.hasFocus = false;
11150 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153 var v = this.getValue();
11154 if(String(v) !== String(this.startValue)){
11155 this.fireEvent('change', this, v, this.startValue);
11157 this.fireEvent("blur", this);
11160 onChange : function(e)
11162 var v = this.getValue();
11163 if(String(v) !== String(this.startValue)){
11164 this.fireEvent('change', this, v, this.startValue);
11170 * Resets the current field value to the originally loaded value and clears any validation messages
11172 reset : function(){
11173 this.setValue(this.originalValue);
11177 * Returns the name of the field
11178 * @return {Mixed} name The name field
11180 getName: function(){
11184 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11185 * @return {Mixed} value The field value
11187 getValue : function(){
11189 var v = this.inputEl().getValue();
11194 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11195 * @return {Mixed} value The field value
11197 getRawValue : function(){
11198 var v = this.inputEl().getValue();
11204 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11205 * @param {Mixed} value The value to set
11207 setRawValue : function(v){
11208 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211 selectText : function(start, end){
11212 var v = this.getRawValue();
11214 start = start === undefined ? 0 : start;
11215 end = end === undefined ? v.length : end;
11216 var d = this.inputEl().dom;
11217 if(d.setSelectionRange){
11218 d.setSelectionRange(start, end);
11219 }else if(d.createTextRange){
11220 var range = d.createTextRange();
11221 range.moveStart("character", start);
11222 range.moveEnd("character", v.length-end);
11229 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11230 * @param {Mixed} value The value to set
11232 setValue : function(v){
11235 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241 processValue : function(value){
11242 if(this.stripCharsRe){
11243 var newValue = value.replace(this.stripCharsRe, '');
11244 if(newValue !== value){
11245 this.setRawValue(newValue);
11252 preFocus : function(){
11254 if(this.selectOnFocus){
11255 this.inputEl().dom.select();
11258 filterKeys : function(e){
11259 var k = e.getKey();
11260 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263 var c = e.getCharCode(), cc = String.fromCharCode(c);
11264 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267 if(!this.maskRe.test(cc)){
11272 * Clear any invalid styles/messages for this field
11274 clearInvalid : function(){
11276 if(!this.el || this.preventMark){ // not rendered
11281 this.el.removeClass([this.invalidClass, 'is-invalid']);
11283 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11285 var feedback = this.el.select('.form-control-feedback', true).first();
11288 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11293 if(this.indicator){
11294 this.indicator.removeClass('visible');
11295 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298 this.fireEvent('valid', this);
11302 * Mark this field as valid
11304 markValid : function()
11306 if(!this.el || this.preventMark){ // not rendered...
11310 this.el.removeClass([this.invalidClass, this.validClass]);
11311 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11313 var feedback = this.el.select('.form-control-feedback', true).first();
11316 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319 if(this.indicator){
11320 this.indicator.removeClass('visible');
11321 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11329 if(this.allowBlank && !this.getRawValue().length){
11332 if (Roo.bootstrap.version == 3) {
11333 this.el.addClass(this.validClass);
11335 this.inputEl().addClass('is-valid');
11338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11340 var feedback = this.el.select('.form-control-feedback', true).first();
11343 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11344 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11349 this.fireEvent('valid', this);
11353 * Mark this field as invalid
11354 * @param {String} msg The validation message
11356 markInvalid : function(msg)
11358 if(!this.el || this.preventMark){ // not rendered
11362 this.el.removeClass([this.invalidClass, this.validClass]);
11363 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11365 var feedback = this.el.select('.form-control-feedback', true).first();
11368 this.el.select('.form-control-feedback', true).first().removeClass(
11369 [this.invalidFeedbackClass, this.validFeedbackClass]);
11376 if(this.allowBlank && !this.getRawValue().length){
11380 if(this.indicator){
11381 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11382 this.indicator.addClass('visible');
11384 if (Roo.bootstrap.version == 3) {
11385 this.el.addClass(this.invalidClass);
11387 this.inputEl().addClass('is-invalid');
11392 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11394 var feedback = this.el.select('.form-control-feedback', true).first();
11397 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11399 if(this.getValue().length || this.forceFeedback){
11400 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11407 this.fireEvent('invalid', this, msg);
11410 SafariOnKeyDown : function(event)
11412 // this is a workaround for a password hang bug on chrome/ webkit.
11413 if (this.inputEl().dom.type != 'password') {
11417 var isSelectAll = false;
11419 if(this.inputEl().dom.selectionEnd > 0){
11420 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11422 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11423 event.preventDefault();
11428 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11430 event.preventDefault();
11431 // this is very hacky as keydown always get's upper case.
11433 var cc = String.fromCharCode(event.getCharCode());
11434 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11438 adjustWidth : function(tag, w){
11439 tag = tag.toLowerCase();
11440 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11441 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11442 if(tag == 'input'){
11445 if(tag == 'textarea'){
11448 }else if(Roo.isOpera){
11449 if(tag == 'input'){
11452 if(tag == 'textarea'){
11460 setFieldLabel : function(v)
11462 if(!this.rendered){
11466 if(this.indicatorEl()){
11467 var ar = this.el.select('label > span',true);
11469 if (ar.elements.length) {
11470 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11471 this.fieldLabel = v;
11475 var br = this.el.select('label',true);
11477 if(br.elements.length) {
11478 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11479 this.fieldLabel = v;
11483 Roo.log('Cannot Found any of label > span || label in input');
11487 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11488 this.fieldLabel = v;
11503 * @class Roo.bootstrap.TextArea
11504 * @extends Roo.bootstrap.Input
11505 * Bootstrap TextArea class
11506 * @cfg {Number} cols Specifies the visible width of a text area
11507 * @cfg {Number} rows Specifies the visible number of lines in a text area
11508 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11509 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11510 * @cfg {string} html text
11513 * Create a new TextArea
11514 * @param {Object} config The config object
11517 Roo.bootstrap.TextArea = function(config){
11518 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11522 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11532 getAutoCreate : function(){
11534 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11540 if(this.inputType != 'hidden'){
11541 cfg.cls = 'form-group' //input-group
11549 value : this.value || '',
11550 html: this.html || '',
11551 cls : 'form-control',
11552 placeholder : this.placeholder || ''
11556 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11557 input.maxLength = this.maxLength;
11561 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11565 input.cols = this.cols;
11568 if (this.readOnly) {
11569 input.readonly = true;
11573 input.name = this.name;
11577 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11581 ['xs','sm','md','lg'].map(function(size){
11582 if (settings[size]) {
11583 cfg.cls += ' col-' + size + '-' + settings[size];
11587 var inputblock = input;
11589 if(this.hasFeedback && !this.allowBlank){
11593 cls: 'glyphicon form-control-feedback'
11597 cls : 'has-feedback',
11606 if (this.before || this.after) {
11609 cls : 'input-group',
11613 inputblock.cn.push({
11615 cls : 'input-group-addon',
11620 inputblock.cn.push(input);
11622 if(this.hasFeedback && !this.allowBlank){
11623 inputblock.cls += ' has-feedback';
11624 inputblock.cn.push(feedback);
11628 inputblock.cn.push({
11630 cls : 'input-group-addon',
11637 if (align ==='left' && this.fieldLabel.length) {
11642 cls : 'control-label',
11643 html : this.fieldLabel
11654 if(this.labelWidth > 12){
11655 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658 if(this.labelWidth < 13 && this.labelmd == 0){
11659 this.labelmd = this.labelWidth;
11662 if(this.labellg > 0){
11663 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11664 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667 if(this.labelmd > 0){
11668 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11669 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672 if(this.labelsm > 0){
11673 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11674 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677 if(this.labelxs > 0){
11678 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11679 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682 } else if ( this.fieldLabel.length) {
11687 //cls : 'input-group-addon',
11688 html : this.fieldLabel
11706 if (this.disabled) {
11707 input.disabled=true;
11714 * return the real textarea element.
11716 inputEl: function ()
11718 return this.el.select('textarea.form-control',true).first();
11722 * Clear any invalid styles/messages for this field
11724 clearInvalid : function()
11727 if(!this.el || this.preventMark){ // not rendered
11731 var label = this.el.select('label', true).first();
11732 var icon = this.el.select('i.fa-star', true).first();
11737 this.el.removeClass( this.validClass);
11738 this.inputEl().removeClass('is-invalid');
11740 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11742 var feedback = this.el.select('.form-control-feedback', true).first();
11745 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11750 this.fireEvent('valid', this);
11754 * Mark this field as valid
11756 markValid : function()
11758 if(!this.el || this.preventMark){ // not rendered
11762 this.el.removeClass([this.invalidClass, this.validClass]);
11763 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11765 var feedback = this.el.select('.form-control-feedback', true).first();
11768 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771 if(this.disabled || this.allowBlank){
11775 var label = this.el.select('label', true).first();
11776 var icon = this.el.select('i.fa-star', true).first();
11781 if (Roo.bootstrap.version == 3) {
11782 this.el.addClass(this.validClass);
11784 this.inputEl().addClass('is-valid');
11788 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11790 var feedback = this.el.select('.form-control-feedback', true).first();
11793 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11794 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11799 this.fireEvent('valid', this);
11803 * Mark this field as invalid
11804 * @param {String} msg The validation message
11806 markInvalid : function(msg)
11808 if(!this.el || this.preventMark){ // not rendered
11812 this.el.removeClass([this.invalidClass, this.validClass]);
11813 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11815 var feedback = this.el.select('.form-control-feedback', true).first();
11818 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821 if(this.disabled || this.allowBlank){
11825 var label = this.el.select('label', true).first();
11826 var icon = this.el.select('i.fa-star', true).first();
11828 if(!this.getValue().length && label && !icon){
11829 this.el.createChild({
11831 cls : 'text-danger fa fa-lg fa-star',
11832 tooltip : 'This field is required',
11833 style : 'margin-right:5px;'
11837 if (Roo.bootstrap.version == 3) {
11838 this.el.addClass(this.invalidClass);
11840 this.inputEl().addClass('is-invalid');
11843 // fixme ... this may be depricated need to test..
11844 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11846 var feedback = this.el.select('.form-control-feedback', true).first();
11849 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11851 if(this.getValue().length || this.forceFeedback){
11852 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11859 this.fireEvent('invalid', this, msg);
11867 * trigger field - base class for combo..
11872 * @class Roo.bootstrap.TriggerField
11873 * @extends Roo.bootstrap.Input
11874 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11875 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11876 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11877 * for which you can provide a custom implementation. For example:
11879 var trigger = new Roo.bootstrap.TriggerField();
11880 trigger.onTriggerClick = myTriggerFn;
11881 trigger.applyTo('my-field');
11884 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11885 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11886 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11887 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11888 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891 * Create a new TriggerField.
11892 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11893 * to the base TextField)
11895 Roo.bootstrap.TriggerField = function(config){
11896 this.mimicing = false;
11897 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11902 * @cfg {String} triggerClass A CSS class to apply to the trigger
11905 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11910 * @cfg {Boolean} removable (true|false) special filter default false
11914 /** @cfg {Boolean} grow @hide */
11915 /** @cfg {Number} growMin @hide */
11916 /** @cfg {Number} growMax @hide */
11922 autoSize: Roo.emptyFn,
11926 deferHeight : true,
11929 actionMode : 'wrap',
11934 getAutoCreate : function(){
11936 var align = this.labelAlign || this.parentLabelAlign();
11941 cls: 'form-group' //input-group
11948 type : this.inputType,
11949 cls : 'form-control',
11950 autocomplete: 'new-password',
11951 placeholder : this.placeholder || ''
11955 input.name = this.name;
11958 input.cls += ' input-' + this.size;
11961 if (this.disabled) {
11962 input.disabled=true;
11965 var inputblock = input;
11967 if(this.hasFeedback && !this.allowBlank){
11971 cls: 'glyphicon form-control-feedback'
11974 if(this.removable && !this.editable ){
11976 cls : 'has-feedback',
11982 cls : 'roo-combo-removable-btn close'
11989 cls : 'has-feedback',
11998 if(this.removable && !this.editable ){
12000 cls : 'roo-removable',
12006 cls : 'roo-combo-removable-btn close'
12013 if (this.before || this.after) {
12016 cls : 'input-group',
12020 inputblock.cn.push({
12022 cls : 'input-group-addon input-group-prepend input-group-text',
12027 inputblock.cn.push(input);
12029 if(this.hasFeedback && !this.allowBlank){
12030 inputblock.cls += ' has-feedback';
12031 inputblock.cn.push(feedback);
12035 inputblock.cn.push({
12037 cls : 'input-group-addon input-group-append input-group-text',
12046 var ibwrap = inputblock;
12051 cls: 'roo-select2-choices',
12055 cls: 'roo-select2-search-field',
12067 cls: 'roo-select2-container input-group',
12072 cls: 'form-hidden-field'
12078 if(!this.multiple && this.showToggleBtn){
12084 if (this.caret != false) {
12087 cls: 'fa fa-' + this.caret
12094 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12096 Roo.bootstrap.version == 3 ? caret : '',
12099 cls: 'combobox-clear',
12113 combobox.cls += ' roo-select2-container-multi';
12117 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12118 tooltip : 'This field is required'
12120 if (Roo.bootstrap.version == 4) {
12123 style : 'display:none'
12128 if (align ==='left' && this.fieldLabel.length) {
12130 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12137 cls : 'control-label',
12138 html : this.fieldLabel
12150 var labelCfg = cfg.cn[1];
12151 var contentCfg = cfg.cn[2];
12153 if(this.indicatorpos == 'right'){
12158 cls : 'control-label',
12162 html : this.fieldLabel
12176 labelCfg = cfg.cn[0];
12177 contentCfg = cfg.cn[1];
12180 if(this.labelWidth > 12){
12181 labelCfg.style = "width: " + this.labelWidth + 'px';
12184 if(this.labelWidth < 13 && this.labelmd == 0){
12185 this.labelmd = this.labelWidth;
12188 if(this.labellg > 0){
12189 labelCfg.cls += ' col-lg-' + this.labellg;
12190 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193 if(this.labelmd > 0){
12194 labelCfg.cls += ' col-md-' + this.labelmd;
12195 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198 if(this.labelsm > 0){
12199 labelCfg.cls += ' col-sm-' + this.labelsm;
12200 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203 if(this.labelxs > 0){
12204 labelCfg.cls += ' col-xs-' + this.labelxs;
12205 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208 } else if ( this.fieldLabel.length) {
12209 // Roo.log(" label");
12214 //cls : 'input-group-addon',
12215 html : this.fieldLabel
12223 if(this.indicatorpos == 'right'){
12231 html : this.fieldLabel
12245 // Roo.log(" no label && no align");
12252 ['xs','sm','md','lg'].map(function(size){
12253 if (settings[size]) {
12254 cfg.cls += ' col-' + size + '-' + settings[size];
12265 onResize : function(w, h){
12266 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12267 // if(typeof w == 'number'){
12268 // var x = w - this.trigger.getWidth();
12269 // this.inputEl().setWidth(this.adjustWidth('input', x));
12270 // this.trigger.setStyle('left', x+'px');
12275 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278 getResizeEl : function(){
12279 return this.inputEl();
12283 getPositionEl : function(){
12284 return this.inputEl();
12288 alignErrorIcon : function(){
12289 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12293 initEvents : function(){
12297 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12298 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12299 if(!this.multiple && this.showToggleBtn){
12300 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12301 if(this.hideTrigger){
12302 this.trigger.setDisplayed(false);
12304 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12308 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311 if(this.removable && !this.editable && !this.tickable){
12312 var close = this.closeTriggerEl();
12315 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12316 close.on('click', this.removeBtnClick, this, close);
12320 //this.trigger.addClassOnOver('x-form-trigger-over');
12321 //this.trigger.addClassOnClick('x-form-trigger-click');
12324 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12328 closeTriggerEl : function()
12330 var close = this.el.select('.roo-combo-removable-btn', true).first();
12331 return close ? close : false;
12334 removeBtnClick : function(e, h, el)
12336 e.preventDefault();
12338 if(this.fireEvent("remove", this) !== false){
12340 this.fireEvent("afterremove", this)
12344 createList : function()
12346 this.list = Roo.get(document.body).createChild({
12347 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12348 cls: 'typeahead typeahead-long dropdown-menu shadow',
12349 style: 'display:none'
12352 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12357 initTrigger : function(){
12362 onDestroy : function(){
12364 this.trigger.removeAllListeners();
12365 // this.trigger.remove();
12368 // this.wrap.remove();
12370 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12374 onFocus : function(){
12375 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12377 if(!this.mimicing){
12378 this.wrap.addClass('x-trigger-wrap-focus');
12379 this.mimicing = true;
12380 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12381 if(this.monitorTab){
12382 this.el.on("keydown", this.checkTab, this);
12389 checkTab : function(e){
12390 if(e.getKey() == e.TAB){
12391 this.triggerBlur();
12396 onBlur : function(){
12401 mimicBlur : function(e, t){
12403 if(!this.wrap.contains(t) && this.validateBlur()){
12404 this.triggerBlur();
12410 triggerBlur : function(){
12411 this.mimicing = false;
12412 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12413 if(this.monitorTab){
12414 this.el.un("keydown", this.checkTab, this);
12416 //this.wrap.removeClass('x-trigger-wrap-focus');
12417 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12421 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12422 validateBlur : function(e, t){
12427 onDisable : function(){
12428 this.inputEl().dom.disabled = true;
12429 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12431 // this.wrap.addClass('x-item-disabled');
12436 onEnable : function(){
12437 this.inputEl().dom.disabled = false;
12438 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12440 // this.el.removeClass('x-item-disabled');
12445 onShow : function(){
12446 var ae = this.getActionEl();
12449 ae.dom.style.display = '';
12450 ae.dom.style.visibility = 'visible';
12456 onHide : function(){
12457 var ae = this.getActionEl();
12458 ae.dom.style.display = 'none';
12462 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12463 * by an implementing function.
12465 * @param {EventObject} e
12467 onTriggerClick : Roo.emptyFn
12475 * @class Roo.bootstrap.CardUploader
12476 * @extends Roo.bootstrap.Button
12477 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12478 * @cfg {Number} errorTimeout default 3000
12479 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12480 * @cfg {Array} html The button text.
12484 * Create a new CardUploader
12485 * @param {Object} config The config object
12488 Roo.bootstrap.CardUploader = function(config){
12492 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12502 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12505 errorTimeout : 3000,
12509 fileCollection : false,
12512 getAutoCreate : function()
12516 cls :'form-group' ,
12521 //cls : 'input-group-addon',
12522 html : this.fieldLabel
12529 value : this.value,
12530 cls : 'd-none form-control'
12535 multiple : 'multiple',
12537 cls : 'd-none roo-card-upload-selector'
12541 cls : 'roo-card-uploader-button-container w-100 mb-2'
12544 cls : 'card-columns roo-card-uploader-container'
12554 getChildContainer : function() /// what children are added to.
12556 return this.containerEl;
12559 getButtonContainer : function() /// what children are added to.
12561 return this.el.select(".roo-card-uploader-button-container").first();
12564 initEvents : function()
12567 Roo.bootstrap.Input.prototype.initEvents.call(this);
12571 xns: Roo.bootstrap,
12574 container_method : 'getButtonContainer' ,
12575 html : this.html, // fix changable?
12578 'click' : function(btn, e) {
12587 this.urlAPI = (window.createObjectURL && window) ||
12588 (window.URL && URL.revokeObjectURL && URL) ||
12589 (window.webkitURL && webkitURL);
12594 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12596 this.selectorEl.on('change', this.onFileSelected, this);
12599 this.images.forEach(function(img) {
12602 this.images = false;
12604 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12610 onClick : function(e)
12612 e.preventDefault();
12614 this.selectorEl.dom.click();
12618 onFileSelected : function(e)
12620 e.preventDefault();
12622 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626 Roo.each(this.selectorEl.dom.files, function(file){
12627 this.addFile(file);
12636 addFile : function(file)
12639 if(typeof(file) === 'string'){
12640 throw "Add file by name?"; // should not happen
12644 if(!file || !this.urlAPI){
12654 var url = _this.urlAPI.createObjectURL( file);
12657 id : Roo.bootstrap.CardUploader.ID--,
12658 is_uploaded : false,
12661 mimetype : file.type,
12668 addCard : function (data)
12670 // hidden input element?
12671 // if the file is not an image...
12672 //then we need to use something other that and header_image
12677 xns : Roo.bootstrap,
12678 xtype : 'CardFooter',
12681 xns : Roo.bootstrap,
12687 xns : Roo.bootstrap,
12689 html : String.format("<small>{0}</small>", data.title),
12690 cls : 'col-11 text-left',
12695 click : function() {
12696 this.downloadCard(data.id)
12702 xns : Roo.bootstrap,
12710 click : function() {
12711 t.removeCard(data.id)
12723 var cn = this.addxtype(
12726 xns : Roo.bootstrap,
12729 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12730 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12731 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12736 initEvents : function() {
12737 Roo.bootstrap.Card.prototype.initEvents.call(this);
12738 this.imgEl = this.el.select('.card-img-top').first();
12740 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12741 this.imgEl.set({ 'pointer' : 'cursor' });
12750 // dont' really need ot update items.
12751 // this.items.push(cn);
12752 this.fileCollection.add(cn);
12753 this.updateInput();
12756 removeCard : function(id)
12759 var card = this.fileCollection.get(id);
12760 card.data.is_deleted = 1;
12761 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12762 this.fileCollection.remove(card);
12763 //this.items = this.items.filter(function(e) { return e != card });
12764 // dont' really need ot update items.
12765 card.el.dom.parentNode.removeChild(card.el.dom);
12770 this.fileCollection.each(function(card) {
12771 card.el.dom.parentNode.removeChild(card.el.dom);
12773 this.fileCollection.clear();
12774 this.updateInput();
12777 updateInput : function()
12780 this.fileCollection.each(function(e) {
12784 this.inputEl().dom.value = JSON.stringify(data);
12791 Roo.bootstrap.CardUploader.ID = -1;/*
12793 * Ext JS Library 1.1.1
12794 * Copyright(c) 2006-2007, Ext JS, LLC.
12796 * Originally Released Under LGPL - original licence link has changed is not relivant.
12799 * <script type="text/javascript">
12804 * @class Roo.data.SortTypes
12806 * Defines the default sorting (casting?) comparison functions used when sorting data.
12808 Roo.data.SortTypes = {
12810 * Default sort that does nothing
12811 * @param {Mixed} s The value being converted
12812 * @return {Mixed} The comparison value
12814 none : function(s){
12819 * The regular expression used to strip tags
12823 stripTagsRE : /<\/?[^>]+>/gi,
12826 * Strips all HTML tags to sort on text only
12827 * @param {Mixed} s The value being converted
12828 * @return {String} The comparison value
12830 asText : function(s){
12831 return String(s).replace(this.stripTagsRE, "");
12835 * Strips all HTML tags to sort on text only - Case insensitive
12836 * @param {Mixed} s The value being converted
12837 * @return {String} The comparison value
12839 asUCText : function(s){
12840 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12844 * Case insensitive string
12845 * @param {Mixed} s The value being converted
12846 * @return {String} The comparison value
12848 asUCString : function(s) {
12849 return String(s).toUpperCase();
12854 * @param {Mixed} s The value being converted
12855 * @return {Number} The comparison value
12857 asDate : function(s) {
12861 if(s instanceof Date){
12862 return s.getTime();
12864 return Date.parse(String(s));
12869 * @param {Mixed} s The value being converted
12870 * @return {Float} The comparison value
12872 asFloat : function(s) {
12873 var val = parseFloat(String(s).replace(/,/g, ""));
12882 * @param {Mixed} s The value being converted
12883 * @return {Number} The comparison value
12885 asInt : function(s) {
12886 var val = parseInt(String(s).replace(/,/g, ""));
12894 * Ext JS Library 1.1.1
12895 * Copyright(c) 2006-2007, Ext JS, LLC.
12897 * Originally Released Under LGPL - original licence link has changed is not relivant.
12900 * <script type="text/javascript">
12904 * @class Roo.data.Record
12905 * Instances of this class encapsulate both record <em>definition</em> information, and record
12906 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12907 * to access Records cached in an {@link Roo.data.Store} object.<br>
12909 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12910 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12915 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12916 * {@link #create}. The parameters are the same.
12917 * @param {Array} data An associative Array of data values keyed by the field name.
12918 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12919 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12920 * not specified an integer id is generated.
12922 Roo.data.Record = function(data, id){
12923 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12928 * Generate a constructor for a specific record layout.
12929 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12930 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12931 * Each field definition object may contain the following properties: <ul>
12932 * <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,
12933 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12934 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12935 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12936 * is being used, then this is a string containing the javascript expression to reference the data relative to
12937 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12938 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12939 * this may be omitted.</p></li>
12940 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12941 * <ul><li>auto (Default, implies no conversion)</li>
12946 * <li>date</li></ul></p></li>
12947 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12948 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12949 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12950 * by the Reader into an object that will be stored in the Record. It is passed the
12951 * following parameters:<ul>
12952 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12954 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12956 * <br>usage:<br><pre><code>
12957 var TopicRecord = Roo.data.Record.create(
12958 {name: 'title', mapping: 'topic_title'},
12959 {name: 'author', mapping: 'username'},
12960 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12961 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12962 {name: 'lastPoster', mapping: 'user2'},
12963 {name: 'excerpt', mapping: 'post_text'}
12966 var myNewRecord = new TopicRecord({
12967 title: 'Do my job please',
12970 lastPost: new Date(),
12971 lastPoster: 'Animal',
12972 excerpt: 'No way dude!'
12974 myStore.add(myNewRecord);
12979 Roo.data.Record.create = function(o){
12980 var f = function(){
12981 f.superclass.constructor.apply(this, arguments);
12983 Roo.extend(f, Roo.data.Record);
12984 var p = f.prototype;
12985 p.fields = new Roo.util.MixedCollection(false, function(field){
12988 for(var i = 0, len = o.length; i < len; i++){
12989 p.fields.add(new Roo.data.Field(o[i]));
12991 f.getField = function(name){
12992 return p.fields.get(name);
12997 Roo.data.Record.AUTO_ID = 1000;
12998 Roo.data.Record.EDIT = 'edit';
12999 Roo.data.Record.REJECT = 'reject';
13000 Roo.data.Record.COMMIT = 'commit';
13002 Roo.data.Record.prototype = {
13004 * Readonly flag - true if this record has been modified.
13013 join : function(store){
13014 this.store = store;
13018 * Set the named field to the specified value.
13019 * @param {String} name The name of the field to set.
13020 * @param {Object} value The value to set the field to.
13022 set : function(name, value){
13023 if(this.data[name] == value){
13027 if(!this.modified){
13028 this.modified = {};
13030 if(typeof this.modified[name] == 'undefined'){
13031 this.modified[name] = this.data[name];
13033 this.data[name] = value;
13034 if(!this.editing && this.store){
13035 this.store.afterEdit(this);
13040 * Get the value of the named field.
13041 * @param {String} name The name of the field to get the value of.
13042 * @return {Object} The value of the field.
13044 get : function(name){
13045 return this.data[name];
13049 beginEdit : function(){
13050 this.editing = true;
13051 this.modified = {};
13055 cancelEdit : function(){
13056 this.editing = false;
13057 delete this.modified;
13061 endEdit : function(){
13062 this.editing = false;
13063 if(this.dirty && this.store){
13064 this.store.afterEdit(this);
13069 * Usually called by the {@link Roo.data.Store} which owns the Record.
13070 * Rejects all changes made to the Record since either creation, or the last commit operation.
13071 * Modified fields are reverted to their original values.
13073 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13074 * of reject operations.
13076 reject : function(){
13077 var m = this.modified;
13079 if(typeof m[n] != "function"){
13080 this.data[n] = m[n];
13083 this.dirty = false;
13084 delete this.modified;
13085 this.editing = false;
13087 this.store.afterReject(this);
13092 * Usually called by the {@link Roo.data.Store} which owns the Record.
13093 * Commits all changes made to the Record since either creation, or the last commit operation.
13095 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13096 * of commit operations.
13098 commit : function(){
13099 this.dirty = false;
13100 delete this.modified;
13101 this.editing = false;
13103 this.store.afterCommit(this);
13108 hasError : function(){
13109 return this.error != null;
13113 clearError : function(){
13118 * Creates a copy of this record.
13119 * @param {String} id (optional) A new record id if you don't want to use this record's id
13122 copy : function(newId) {
13123 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13127 * Ext JS Library 1.1.1
13128 * Copyright(c) 2006-2007, Ext JS, LLC.
13130 * Originally Released Under LGPL - original licence link has changed is not relivant.
13133 * <script type="text/javascript">
13139 * @class Roo.data.Store
13140 * @extends Roo.util.Observable
13141 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13142 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13144 * 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
13145 * has no knowledge of the format of the data returned by the Proxy.<br>
13147 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13148 * instances from the data object. These records are cached and made available through accessor functions.
13150 * Creates a new Store.
13151 * @param {Object} config A config object containing the objects needed for the Store to access data,
13152 * and read the data into Records.
13154 Roo.data.Store = function(config){
13155 this.data = new Roo.util.MixedCollection(false);
13156 this.data.getKey = function(o){
13159 this.baseParams = {};
13161 this.paramNames = {
13166 "multisort" : "_multisort"
13169 if(config && config.data){
13170 this.inlineData = config.data;
13171 delete config.data;
13174 Roo.apply(this, config);
13176 if(this.reader){ // reader passed
13177 this.reader = Roo.factory(this.reader, Roo.data);
13178 this.reader.xmodule = this.xmodule || false;
13179 if(!this.recordType){
13180 this.recordType = this.reader.recordType;
13182 if(this.reader.onMetaChange){
13183 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13187 if(this.recordType){
13188 this.fields = this.recordType.prototype.fields;
13190 this.modified = [];
13194 * @event datachanged
13195 * Fires when the data cache has changed, and a widget which is using this Store
13196 * as a Record cache should refresh its view.
13197 * @param {Store} this
13199 datachanged : true,
13201 * @event metachange
13202 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13203 * @param {Store} this
13204 * @param {Object} meta The JSON metadata
13209 * Fires when Records have been added to the Store
13210 * @param {Store} this
13211 * @param {Roo.data.Record[]} records The array of Records added
13212 * @param {Number} index The index at which the record(s) were added
13217 * Fires when a Record has been removed from the Store
13218 * @param {Store} this
13219 * @param {Roo.data.Record} record The Record that was removed
13220 * @param {Number} index The index at which the record was removed
13225 * Fires when a Record has been updated
13226 * @param {Store} this
13227 * @param {Roo.data.Record} record The Record that was updated
13228 * @param {String} operation The update operation being performed. Value may be one of:
13230 Roo.data.Record.EDIT
13231 Roo.data.Record.REJECT
13232 Roo.data.Record.COMMIT
13238 * Fires when the data cache has been cleared.
13239 * @param {Store} this
13243 * @event beforeload
13244 * Fires before a request is made for a new data object. If the beforeload handler returns false
13245 * the load action will be canceled.
13246 * @param {Store} this
13247 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13251 * @event beforeloadadd
13252 * Fires after a new set of Records has been loaded.
13253 * @param {Store} this
13254 * @param {Roo.data.Record[]} records The Records that were loaded
13255 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13257 beforeloadadd : true,
13260 * Fires after a new set of Records has been loaded, before they are added to the store.
13261 * @param {Store} this
13262 * @param {Roo.data.Record[]} records The Records that were loaded
13263 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13264 * @params {Object} return from reader
13268 * @event loadexception
13269 * Fires if an exception occurs in the Proxy during loading.
13270 * Called with the signature of the Proxy's "loadexception" event.
13271 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13275 * @param {Object} load options
13276 * @param {Object} jsonData from your request (normally this contains the Exception)
13278 loadexception : true
13282 this.proxy = Roo.factory(this.proxy, Roo.data);
13283 this.proxy.xmodule = this.xmodule || false;
13284 this.relayEvents(this.proxy, ["loadexception"]);
13286 this.sortToggle = {};
13287 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13289 Roo.data.Store.superclass.constructor.call(this);
13291 if(this.inlineData){
13292 this.loadData(this.inlineData);
13293 delete this.inlineData;
13297 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13299 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13300 * without a remote query - used by combo/forms at present.
13304 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13311 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13315 * on any HTTP request
13318 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13325 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13326 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13328 remoteSort : false,
13331 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13332 * loaded or when a record is removed. (defaults to false).
13334 pruneModifiedRecords : false,
13337 lastOptions : null,
13340 * Add Records to the Store and fires the add event.
13341 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13343 add : function(records){
13344 records = [].concat(records);
13345 for(var i = 0, len = records.length; i < len; i++){
13346 records[i].join(this);
13348 var index = this.data.length;
13349 this.data.addAll(records);
13350 this.fireEvent("add", this, records, index);
13354 * Remove a Record from the Store and fires the remove event.
13355 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13357 remove : function(record){
13358 var index = this.data.indexOf(record);
13359 this.data.removeAt(index);
13361 if(this.pruneModifiedRecords){
13362 this.modified.remove(record);
13364 this.fireEvent("remove", this, record, index);
13368 * Remove all Records from the Store and fires the clear event.
13370 removeAll : function(){
13372 if(this.pruneModifiedRecords){
13373 this.modified = [];
13375 this.fireEvent("clear", this);
13379 * Inserts Records to the Store at the given index and fires the add event.
13380 * @param {Number} index The start index at which to insert the passed Records.
13381 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13383 insert : function(index, records){
13384 records = [].concat(records);
13385 for(var i = 0, len = records.length; i < len; i++){
13386 this.data.insert(index, records[i]);
13387 records[i].join(this);
13389 this.fireEvent("add", this, records, index);
13393 * Get the index within the cache of the passed Record.
13394 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13395 * @return {Number} The index of the passed Record. Returns -1 if not found.
13397 indexOf : function(record){
13398 return this.data.indexOf(record);
13402 * Get the index within the cache of the Record with the passed id.
13403 * @param {String} id The id of the Record to find.
13404 * @return {Number} The index of the Record. Returns -1 if not found.
13406 indexOfId : function(id){
13407 return this.data.indexOfKey(id);
13411 * Get the Record with the specified id.
13412 * @param {String} id The id of the Record to find.
13413 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13415 getById : function(id){
13416 return this.data.key(id);
13420 * Get the Record at the specified index.
13421 * @param {Number} index The index of the Record to find.
13422 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13424 getAt : function(index){
13425 return this.data.itemAt(index);
13429 * Returns a range of Records between specified indices.
13430 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13431 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13432 * @return {Roo.data.Record[]} An array of Records
13434 getRange : function(start, end){
13435 return this.data.getRange(start, end);
13439 storeOptions : function(o){
13440 o = Roo.apply({}, o);
13443 this.lastOptions = o;
13447 * Loads the Record cache from the configured Proxy using the configured Reader.
13449 * If using remote paging, then the first load call must specify the <em>start</em>
13450 * and <em>limit</em> properties in the options.params property to establish the initial
13451 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13453 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13454 * and this call will return before the new data has been loaded. Perform any post-processing
13455 * in a callback function, or in a "load" event handler.</strong>
13457 * @param {Object} options An object containing properties which control loading options:<ul>
13458 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13459 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13460 * passed the following arguments:<ul>
13461 * <li>r : Roo.data.Record[]</li>
13462 * <li>options: Options object from the load call</li>
13463 * <li>success: Boolean success indicator</li></ul></li>
13464 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13465 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468 load : function(options){
13469 options = options || {};
13470 if(this.fireEvent("beforeload", this, options) !== false){
13471 this.storeOptions(options);
13472 var p = Roo.apply(options.params || {}, this.baseParams);
13473 // if meta was not loaded from remote source.. try requesting it.
13474 if (!this.reader.metaFromRemote) {
13475 p._requestMeta = 1;
13477 if(this.sortInfo && this.remoteSort){
13478 var pn = this.paramNames;
13479 p[pn["sort"]] = this.sortInfo.field;
13480 p[pn["dir"]] = this.sortInfo.direction;
13482 if (this.multiSort) {
13483 var pn = this.paramNames;
13484 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13492 * Reloads the Record cache from the configured Proxy using the configured Reader and
13493 * the options from the last load operation performed.
13494 * @param {Object} options (optional) An object containing properties which may override the options
13495 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13496 * the most recently used options are reused).
13498 reload : function(options){
13499 this.load(Roo.applyIf(options||{}, this.lastOptions));
13503 // Called as a callback by the Reader during a load operation.
13504 loadRecords : function(o, options, success){
13505 if(!o || success === false){
13506 if(success !== false){
13507 this.fireEvent("load", this, [], options, o);
13509 if(options.callback){
13510 options.callback.call(options.scope || this, [], options, false);
13514 // if data returned failure - throw an exception.
13515 if (o.success === false) {
13516 // show a message if no listener is registered.
13517 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13518 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13520 // loadmask wil be hooked into this..
13521 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524 var r = o.records, t = o.totalRecords || r.length;
13526 this.fireEvent("beforeloadadd", this, r, options, o);
13528 if(!options || options.add !== true){
13529 if(this.pruneModifiedRecords){
13530 this.modified = [];
13532 for(var i = 0, len = r.length; i < len; i++){
13536 this.data = this.snapshot;
13537 delete this.snapshot;
13540 this.data.addAll(r);
13541 this.totalLength = t;
13543 this.fireEvent("datachanged", this);
13545 this.totalLength = Math.max(t, this.data.length+r.length);
13549 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13551 var e = new Roo.data.Record({});
13553 e.set(this.parent.displayField, this.parent.emptyTitle);
13554 e.set(this.parent.valueField, '');
13559 this.fireEvent("load", this, r, options, o);
13560 if(options.callback){
13561 options.callback.call(options.scope || this, r, options, true);
13567 * Loads data from a passed data block. A Reader which understands the format of the data
13568 * must have been configured in the constructor.
13569 * @param {Object} data The data block from which to read the Records. The format of the data expected
13570 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13571 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13573 loadData : function(o, append){
13574 var r = this.reader.readRecords(o);
13575 this.loadRecords(r, {add: append}, true);
13579 * using 'cn' the nested child reader read the child array into it's child stores.
13580 * @param {Object} rec The record with a 'children array
13582 loadDataFromChildren : function(rec)
13584 this.loadData(this.reader.toLoadData(rec));
13589 * Gets the number of cached records.
13591 * <em>If using paging, this may not be the total size of the dataset. If the data object
13592 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13593 * the data set size</em>
13595 getCount : function(){
13596 return this.data.length || 0;
13600 * Gets the total number of records in the dataset as returned by the server.
13602 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13603 * the dataset size</em>
13605 getTotalCount : function(){
13606 return this.totalLength || 0;
13610 * Returns the sort state of the Store as an object with two properties:
13612 field {String} The name of the field by which the Records are sorted
13613 direction {String} The sort order, "ASC" or "DESC"
13616 getSortState : function(){
13617 return this.sortInfo;
13621 applySort : function(){
13622 if(this.sortInfo && !this.remoteSort){
13623 var s = this.sortInfo, f = s.field;
13624 var st = this.fields.get(f).sortType;
13625 var fn = function(r1, r2){
13626 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13627 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13629 this.data.sort(s.direction, fn);
13630 if(this.snapshot && this.snapshot != this.data){
13631 this.snapshot.sort(s.direction, fn);
13637 * Sets the default sort column and order to be used by the next load operation.
13638 * @param {String} fieldName The name of the field to sort by.
13639 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13641 setDefaultSort : function(field, dir){
13642 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13646 * Sort the Records.
13647 * If remote sorting is used, the sort is performed on the server, and the cache is
13648 * reloaded. If local sorting is used, the cache is sorted internally.
13649 * @param {String} fieldName The name of the field to sort by.
13650 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13652 sort : function(fieldName, dir){
13653 var f = this.fields.get(fieldName);
13655 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13657 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13658 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13663 this.sortToggle[f.name] = dir;
13664 this.sortInfo = {field: f.name, direction: dir};
13665 if(!this.remoteSort){
13667 this.fireEvent("datachanged", this);
13669 this.load(this.lastOptions);
13674 * Calls the specified function for each of the Records in the cache.
13675 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13676 * Returning <em>false</em> aborts and exits the iteration.
13677 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13679 each : function(fn, scope){
13680 this.data.each(fn, scope);
13684 * Gets all records modified since the last commit. Modified records are persisted across load operations
13685 * (e.g., during paging).
13686 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13688 getModifiedRecords : function(){
13689 return this.modified;
13693 createFilterFn : function(property, value, anyMatch){
13694 if(!value.exec){ // not a regex
13695 value = String(value);
13696 if(value.length == 0){
13699 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13701 return function(r){
13702 return value.test(r.data[property]);
13707 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13708 * @param {String} property A field on your records
13709 * @param {Number} start The record index to start at (defaults to 0)
13710 * @param {Number} end The last record index to include (defaults to length - 1)
13711 * @return {Number} The sum
13713 sum : function(property, start, end){
13714 var rs = this.data.items, v = 0;
13715 start = start || 0;
13716 end = (end || end === 0) ? end : rs.length-1;
13718 for(var i = start; i <= end; i++){
13719 v += (rs[i].data[property] || 0);
13725 * Filter the records by a specified property.
13726 * @param {String} field A field on your records
13727 * @param {String/RegExp} value Either a string that the field
13728 * should start with or a RegExp to test against the field
13729 * @param {Boolean} anyMatch True to match any part not just the beginning
13731 filter : function(property, value, anyMatch){
13732 var fn = this.createFilterFn(property, value, anyMatch);
13733 return fn ? this.filterBy(fn) : this.clearFilter();
13737 * Filter by a function. The specified function will be called with each
13738 * record in this data source. If the function returns true the record is included,
13739 * otherwise it is filtered.
13740 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13741 * @param {Object} scope (optional) The scope of the function (defaults to this)
13743 filterBy : function(fn, scope){
13744 this.snapshot = this.snapshot || this.data;
13745 this.data = this.queryBy(fn, scope||this);
13746 this.fireEvent("datachanged", this);
13750 * Query the records by a specified property.
13751 * @param {String} field A field on your records
13752 * @param {String/RegExp} value Either a string that the field
13753 * should start with or a RegExp to test against the field
13754 * @param {Boolean} anyMatch True to match any part not just the beginning
13755 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13757 query : function(property, value, anyMatch){
13758 var fn = this.createFilterFn(property, value, anyMatch);
13759 return fn ? this.queryBy(fn) : this.data.clone();
13763 * Query by a function. The specified function will be called with each
13764 * record in this data source. If the function returns true the record is included
13766 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13767 * @param {Object} scope (optional) The scope of the function (defaults to this)
13768 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13770 queryBy : function(fn, scope){
13771 var data = this.snapshot || this.data;
13772 return data.filterBy(fn, scope||this);
13776 * Collects unique values for a particular dataIndex from this store.
13777 * @param {String} dataIndex The property to collect
13778 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13779 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13780 * @return {Array} An array of the unique values
13782 collect : function(dataIndex, allowNull, bypassFilter){
13783 var d = (bypassFilter === true && this.snapshot) ?
13784 this.snapshot.items : this.data.items;
13785 var v, sv, r = [], l = {};
13786 for(var i = 0, len = d.length; i < len; i++){
13787 v = d[i].data[dataIndex];
13789 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13798 * Revert to a view of the Record cache with no filtering applied.
13799 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13801 clearFilter : function(suppressEvent){
13802 if(this.snapshot && this.snapshot != this.data){
13803 this.data = this.snapshot;
13804 delete this.snapshot;
13805 if(suppressEvent !== true){
13806 this.fireEvent("datachanged", this);
13812 afterEdit : function(record){
13813 if(this.modified.indexOf(record) == -1){
13814 this.modified.push(record);
13816 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13820 afterReject : function(record){
13821 this.modified.remove(record);
13822 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13826 afterCommit : function(record){
13827 this.modified.remove(record);
13828 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13832 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13833 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13835 commitChanges : function(){
13836 var m = this.modified.slice(0);
13837 this.modified = [];
13838 for(var i = 0, len = m.length; i < len; i++){
13844 * Cancel outstanding changes on all changed records.
13846 rejectChanges : function(){
13847 var m = this.modified.slice(0);
13848 this.modified = [];
13849 for(var i = 0, len = m.length; i < len; i++){
13854 onMetaChange : function(meta, rtype, o){
13855 this.recordType = rtype;
13856 this.fields = rtype.prototype.fields;
13857 delete this.snapshot;
13858 this.sortInfo = meta.sortInfo || this.sortInfo;
13859 this.modified = [];
13860 this.fireEvent('metachange', this, this.reader.meta);
13863 moveIndex : function(data, type)
13865 var index = this.indexOf(data);
13867 var newIndex = index + type;
13871 this.insert(newIndex, data);
13876 * Ext JS Library 1.1.1
13877 * Copyright(c) 2006-2007, Ext JS, LLC.
13879 * Originally Released Under LGPL - original licence link has changed is not relivant.
13882 * <script type="text/javascript">
13886 * @class Roo.data.SimpleStore
13887 * @extends Roo.data.Store
13888 * Small helper class to make creating Stores from Array data easier.
13889 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13890 * @cfg {Array} fields An array of field definition objects, or field name strings.
13891 * @cfg {Object} an existing reader (eg. copied from another store)
13892 * @cfg {Array} data The multi-dimensional array of data
13894 * @param {Object} config
13896 Roo.data.SimpleStore = function(config)
13898 Roo.data.SimpleStore.superclass.constructor.call(this, {
13900 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903 Roo.data.Record.create(config.fields)
13905 proxy : new Roo.data.MemoryProxy(config.data)
13909 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13911 * Ext JS Library 1.1.1
13912 * Copyright(c) 2006-2007, Ext JS, LLC.
13914 * Originally Released Under LGPL - original licence link has changed is not relivant.
13917 * <script type="text/javascript">
13922 * @extends Roo.data.Store
13923 * @class Roo.data.JsonStore
13924 * Small helper class to make creating Stores for JSON data easier. <br/>
13926 var store = new Roo.data.JsonStore({
13927 url: 'get-images.php',
13929 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13933 * JsonReader and HttpProxy (unless inline data is provided).</b>
13934 * @cfg {Array} fields An array of field definition objects, or field name strings.
13936 * @param {Object} config
13938 Roo.data.JsonStore = function(c){
13939 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13940 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13941 reader: new Roo.data.JsonReader(c, c.fields)
13944 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13946 * Ext JS Library 1.1.1
13947 * Copyright(c) 2006-2007, Ext JS, LLC.
13949 * Originally Released Under LGPL - original licence link has changed is not relivant.
13952 * <script type="text/javascript">
13956 Roo.data.Field = function(config){
13957 if(typeof config == "string"){
13958 config = {name: config};
13960 Roo.apply(this, config);
13963 this.type = "auto";
13966 var st = Roo.data.SortTypes;
13967 // named sortTypes are supported, here we look them up
13968 if(typeof this.sortType == "string"){
13969 this.sortType = st[this.sortType];
13972 // set default sortType for strings and dates
13973 if(!this.sortType){
13976 this.sortType = st.asUCString;
13979 this.sortType = st.asDate;
13982 this.sortType = st.none;
13987 var stripRe = /[\$,%]/g;
13989 // prebuilt conversion function for this field, instead of
13990 // switching every time we're reading a value
13992 var cv, dateFormat = this.dateFormat;
13997 cv = function(v){ return v; };
14000 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14004 return v !== undefined && v !== null && v !== '' ?
14005 parseInt(String(v).replace(stripRe, ""), 10) : '';
14010 return v !== undefined && v !== null && v !== '' ?
14011 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14016 cv = function(v){ return v === true || v === "true" || v == 1; };
14023 if(v instanceof Date){
14027 if(dateFormat == "timestamp"){
14028 return new Date(v*1000);
14030 return Date.parseDate(v, dateFormat);
14032 var parsed = Date.parse(v);
14033 return parsed ? new Date(parsed) : null;
14042 Roo.data.Field.prototype = {
14050 * Ext JS Library 1.1.1
14051 * Copyright(c) 2006-2007, Ext JS, LLC.
14053 * Originally Released Under LGPL - original licence link has changed is not relivant.
14056 * <script type="text/javascript">
14059 // Base class for reading structured data from a data source. This class is intended to be
14060 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063 * @class Roo.data.DataReader
14064 * Base class for reading structured data from a data source. This class is intended to be
14065 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068 Roo.data.DataReader = function(meta, recordType){
14072 this.recordType = recordType instanceof Array ?
14073 Roo.data.Record.create(recordType) : recordType;
14076 Roo.data.DataReader.prototype = {
14079 readerType : 'Data',
14081 * Create an empty record
14082 * @param {Object} data (optional) - overlay some values
14083 * @return {Roo.data.Record} record created.
14085 newRow : function(d) {
14087 this.recordType.prototype.fields.each(function(c) {
14089 case 'int' : da[c.name] = 0; break;
14090 case 'date' : da[c.name] = new Date(); break;
14091 case 'float' : da[c.name] = 0.0; break;
14092 case 'boolean' : da[c.name] = false; break;
14093 default : da[c.name] = ""; break;
14097 return new this.recordType(Roo.apply(da, d));
14103 * Ext JS Library 1.1.1
14104 * Copyright(c) 2006-2007, Ext JS, LLC.
14106 * Originally Released Under LGPL - original licence link has changed is not relivant.
14109 * <script type="text/javascript">
14113 * @class Roo.data.DataProxy
14114 * @extends Roo.data.Observable
14115 * This class is an abstract base class for implementations which provide retrieval of
14116 * unformatted data objects.<br>
14118 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14119 * (of the appropriate type which knows how to parse the data object) to provide a block of
14120 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14122 * Custom implementations must implement the load method as described in
14123 * {@link Roo.data.HttpProxy#load}.
14125 Roo.data.DataProxy = function(){
14128 * @event beforeload
14129 * Fires before a network request is made to retrieve a data object.
14130 * @param {Object} This DataProxy object.
14131 * @param {Object} params The params parameter to the load function.
14136 * Fires before the load method's callback is called.
14137 * @param {Object} This DataProxy object.
14138 * @param {Object} o The data object.
14139 * @param {Object} arg The callback argument object passed to the load function.
14143 * @event loadexception
14144 * Fires if an Exception occurs during data retrieval.
14145 * @param {Object} This DataProxy object.
14146 * @param {Object} o The data object.
14147 * @param {Object} arg The callback argument object passed to the load function.
14148 * @param {Object} e The Exception.
14150 loadexception : true
14152 Roo.data.DataProxy.superclass.constructor.call(this);
14155 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14162 * Ext JS Library 1.1.1
14163 * Copyright(c) 2006-2007, Ext JS, LLC.
14165 * Originally Released Under LGPL - original licence link has changed is not relivant.
14168 * <script type="text/javascript">
14171 * @class Roo.data.MemoryProxy
14172 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14173 * to the Reader when its load method is called.
14175 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14177 Roo.data.MemoryProxy = function(data){
14181 Roo.data.MemoryProxy.superclass.constructor.call(this);
14185 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188 * Load data from the requested source (in this case an in-memory
14189 * data object passed to the constructor), read the data object into
14190 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14191 * process that block using the passed callback.
14192 * @param {Object} params This parameter is not used by the MemoryProxy class.
14193 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14194 * object into a block of Roo.data.Records.
14195 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14196 * The function must be passed <ul>
14197 * <li>The Record block object</li>
14198 * <li>The "arg" argument from the load function</li>
14199 * <li>A boolean success indicator</li>
14201 * @param {Object} scope The scope in which to call the callback
14202 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14204 load : function(params, reader, callback, scope, arg){
14205 params = params || {};
14208 result = reader.readRecords(params.data ? params.data :this.data);
14210 this.fireEvent("loadexception", this, arg, null, e);
14211 callback.call(scope, null, arg, false);
14214 callback.call(scope, result, arg, true);
14218 update : function(params, records){
14223 * Ext JS Library 1.1.1
14224 * Copyright(c) 2006-2007, Ext JS, LLC.
14226 * Originally Released Under LGPL - original licence link has changed is not relivant.
14229 * <script type="text/javascript">
14232 * @class Roo.data.HttpProxy
14233 * @extends Roo.data.DataProxy
14234 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14235 * configured to reference a certain URL.<br><br>
14237 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14238 * from which the running page was served.<br><br>
14240 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14242 * Be aware that to enable the browser to parse an XML document, the server must set
14243 * the Content-Type header in the HTTP response to "text/xml".
14245 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14246 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14247 * will be used to make the request.
14249 Roo.data.HttpProxy = function(conn){
14250 Roo.data.HttpProxy.superclass.constructor.call(this);
14251 // is conn a conn config or a real conn?
14253 this.useAjax = !conn || !conn.events;
14257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14258 // thse are take from connection...
14261 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14265 * extra parameters to each request made by this object. (defaults to undefined)
14268 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14269 * to each request made by this object. (defaults to undefined)
14272 * @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)
14275 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14284 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14288 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14289 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14290 * a finer-grained basis than the DataProxy events.
14292 getConnection : function(){
14293 return this.useAjax ? Roo.Ajax : this.conn;
14297 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14298 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14299 * process that block using the passed callback.
14300 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14301 * for the request to the remote server.
14302 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14303 * object into a block of Roo.data.Records.
14304 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14305 * The function must be passed <ul>
14306 * <li>The Record block object</li>
14307 * <li>The "arg" argument from the load function</li>
14308 * <li>A boolean success indicator</li>
14310 * @param {Object} scope The scope in which to call the callback
14311 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14313 load : function(params, reader, callback, scope, arg){
14314 if(this.fireEvent("beforeload", this, params) !== false){
14316 params : params || {},
14318 callback : callback,
14323 callback : this.loadResponse,
14327 Roo.applyIf(o, this.conn);
14328 if(this.activeRequest){
14329 Roo.Ajax.abort(this.activeRequest);
14331 this.activeRequest = Roo.Ajax.request(o);
14333 this.conn.request(o);
14336 callback.call(scope||this, null, arg, false);
14341 loadResponse : function(o, success, response){
14342 delete this.activeRequest;
14344 this.fireEvent("loadexception", this, o, response);
14345 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14350 result = o.reader.read(response);
14352 this.fireEvent("loadexception", this, o, response, e);
14353 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14357 this.fireEvent("load", this, o, o.request.arg);
14358 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14362 update : function(dataSet){
14367 updateResponse : function(dataSet){
14372 * Ext JS Library 1.1.1
14373 * Copyright(c) 2006-2007, Ext JS, LLC.
14375 * Originally Released Under LGPL - original licence link has changed is not relivant.
14378 * <script type="text/javascript">
14382 * @class Roo.data.ScriptTagProxy
14383 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14384 * other than the originating domain of the running page.<br><br>
14386 * <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
14387 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14389 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14390 * source code that is used as the source inside a <script> tag.<br><br>
14392 * In order for the browser to process the returned data, the server must wrap the data object
14393 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14394 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14395 * depending on whether the callback name was passed:
14398 boolean scriptTag = false;
14399 String cb = request.getParameter("callback");
14402 response.setContentType("text/javascript");
14404 response.setContentType("application/x-json");
14406 Writer out = response.getWriter();
14408 out.write(cb + "(");
14410 out.print(dataBlock.toJsonString());
14417 * @param {Object} config A configuration object.
14419 Roo.data.ScriptTagProxy = function(config){
14420 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14421 Roo.apply(this, config);
14422 this.head = document.getElementsByTagName("head")[0];
14425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14429 * @cfg {String} url The URL from which to request the data object.
14432 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14436 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14437 * the server the name of the callback function set up by the load call to process the returned data object.
14438 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14439 * javascript output which calls this named function passing the data object as its only parameter.
14441 callbackParam : "callback",
14443 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14444 * name to the request.
14449 * Load data from the configured URL, read the data object into
14450 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14451 * process that block using the passed callback.
14452 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14453 * for the request to the remote server.
14454 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14455 * object into a block of Roo.data.Records.
14456 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14457 * The function must be passed <ul>
14458 * <li>The Record block object</li>
14459 * <li>The "arg" argument from the load function</li>
14460 * <li>A boolean success indicator</li>
14462 * @param {Object} scope The scope in which to call the callback
14463 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14465 load : function(params, reader, callback, scope, arg){
14466 if(this.fireEvent("beforeload", this, params) !== false){
14468 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14470 var url = this.url;
14471 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14473 url += "&_dc=" + (new Date().getTime());
14475 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478 cb : "stcCallback"+transId,
14479 scriptId : "stcScript"+transId,
14483 callback : callback,
14489 window[trans.cb] = function(o){
14490 conn.handleResponse(o, trans);
14493 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14495 if(this.autoAbort !== false){
14499 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14501 var script = document.createElement("script");
14502 script.setAttribute("src", url);
14503 script.setAttribute("type", "text/javascript");
14504 script.setAttribute("id", trans.scriptId);
14505 this.head.appendChild(script);
14507 this.trans = trans;
14509 callback.call(scope||this, null, arg, false);
14514 isLoading : function(){
14515 return this.trans ? true : false;
14519 * Abort the current server request.
14521 abort : function(){
14522 if(this.isLoading()){
14523 this.destroyTrans(this.trans);
14528 destroyTrans : function(trans, isLoaded){
14529 this.head.removeChild(document.getElementById(trans.scriptId));
14530 clearTimeout(trans.timeoutId);
14532 window[trans.cb] = undefined;
14534 delete window[trans.cb];
14537 // if hasn't been loaded, wait for load to remove it to prevent script error
14538 window[trans.cb] = function(){
14539 window[trans.cb] = undefined;
14541 delete window[trans.cb];
14548 handleResponse : function(o, trans){
14549 this.trans = false;
14550 this.destroyTrans(trans, true);
14553 result = trans.reader.readRecords(o);
14555 this.fireEvent("loadexception", this, o, trans.arg, e);
14556 trans.callback.call(trans.scope||window, null, trans.arg, false);
14559 this.fireEvent("load", this, o, trans.arg);
14560 trans.callback.call(trans.scope||window, result, trans.arg, true);
14564 handleFailure : function(trans){
14565 this.trans = false;
14566 this.destroyTrans(trans, false);
14567 this.fireEvent("loadexception", this, null, trans.arg);
14568 trans.callback.call(trans.scope||window, null, trans.arg, false);
14572 * Ext JS Library 1.1.1
14573 * Copyright(c) 2006-2007, Ext JS, LLC.
14575 * Originally Released Under LGPL - original licence link has changed is not relivant.
14578 * <script type="text/javascript">
14582 * @class Roo.data.JsonReader
14583 * @extends Roo.data.DataReader
14584 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14585 * based on mappings in a provided Roo.data.Record constructor.
14587 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14588 * in the reply previously.
14593 var RecordDef = Roo.data.Record.create([
14594 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14595 {name: 'occupation'} // This field will use "occupation" as the mapping.
14597 var myReader = new Roo.data.JsonReader({
14598 totalProperty: "results", // The property which contains the total dataset size (optional)
14599 root: "rows", // The property which contains an Array of row objects
14600 id: "id" // The property within each row object that provides an ID for the record (optional)
14604 * This would consume a JSON file like this:
14606 { 'results': 2, 'rows': [
14607 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14608 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14612 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14613 * paged from the remote server.
14614 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14615 * @cfg {String} root name of the property which contains the Array of row objects.
14616 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14617 * @cfg {Array} fields Array of field definition objects
14619 * Create a new JsonReader
14620 * @param {Object} meta Metadata configuration options
14621 * @param {Object} recordType Either an Array of field definition objects,
14622 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14624 Roo.data.JsonReader = function(meta, recordType){
14627 // set some defaults:
14628 Roo.applyIf(meta, {
14629 totalProperty: 'total',
14630 successProperty : 'success',
14635 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14637 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14639 readerType : 'Json',
14642 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14643 * Used by Store query builder to append _requestMeta to params.
14646 metaFromRemote : false,
14648 * This method is only used by a DataProxy which has retrieved data from a remote server.
14649 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14650 * @return {Object} data A data block which is used by an Roo.data.Store object as
14651 * a cache of Roo.data.Records.
14653 read : function(response){
14654 var json = response.responseText;
14656 var o = /* eval:var:o */ eval("("+json+")");
14658 throw {message: "JsonReader.read: Json object not found"};
14664 this.metaFromRemote = true;
14665 this.meta = o.metaData;
14666 this.recordType = Roo.data.Record.create(o.metaData.fields);
14667 this.onMetaChange(this.meta, this.recordType, o);
14669 return this.readRecords(o);
14672 // private function a store will implement
14673 onMetaChange : function(meta, recordType, o){
14680 simpleAccess: function(obj, subsc) {
14687 getJsonAccessor: function(){
14689 return function(expr) {
14691 return(re.test(expr))
14692 ? new Function("obj", "return obj." + expr)
14697 return Roo.emptyFn;
14702 * Create a data block containing Roo.data.Records from an XML document.
14703 * @param {Object} o An object which contains an Array of row objects in the property specified
14704 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14705 * which contains the total size of the dataset.
14706 * @return {Object} data A data block which is used by an Roo.data.Store object as
14707 * a cache of Roo.data.Records.
14709 readRecords : function(o){
14711 * After any data loads, the raw JSON data is available for further custom processing.
14715 var s = this.meta, Record = this.recordType,
14716 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14718 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14720 if(s.totalProperty) {
14721 this.getTotal = this.getJsonAccessor(s.totalProperty);
14723 if(s.successProperty) {
14724 this.getSuccess = this.getJsonAccessor(s.successProperty);
14726 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14728 var g = this.getJsonAccessor(s.id);
14729 this.getId = function(rec) {
14731 return (r === undefined || r === "") ? null : r;
14734 this.getId = function(){return null;};
14737 for(var jj = 0; jj < fl; jj++){
14739 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14740 this.ef[jj] = this.getJsonAccessor(map);
14744 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14745 if(s.totalProperty){
14746 var vt = parseInt(this.getTotal(o), 10);
14751 if(s.successProperty){
14752 var vs = this.getSuccess(o);
14753 if(vs === false || vs === 'false'){
14758 for(var i = 0; i < c; i++){
14761 var id = this.getId(n);
14762 for(var j = 0; j < fl; j++){
14764 var v = this.ef[j](n);
14766 Roo.log('missing convert for ' + f.name);
14770 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14772 var record = new Record(values, id);
14774 records[i] = record;
14780 totalRecords : totalRecords
14783 // used when loading children.. @see loadDataFromChildren
14784 toLoadData: function(rec)
14786 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14787 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14788 return { data : data, total : data.length };
14793 * Ext JS Library 1.1.1
14794 * Copyright(c) 2006-2007, Ext JS, LLC.
14796 * Originally Released Under LGPL - original licence link has changed is not relivant.
14799 * <script type="text/javascript">
14803 * @class Roo.data.ArrayReader
14804 * @extends Roo.data.DataReader
14805 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14806 * Each element of that Array represents a row of data fields. The
14807 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14808 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14812 var RecordDef = Roo.data.Record.create([
14813 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14814 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14816 var myReader = new Roo.data.ArrayReader({
14817 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14821 * This would consume an Array like this:
14823 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14827 * Create a new JsonReader
14828 * @param {Object} meta Metadata configuration options.
14829 * @param {Object|Array} recordType Either an Array of field definition objects
14831 * @cfg {Array} fields Array of field definition objects
14832 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14833 * as specified to {@link Roo.data.Record#create},
14834 * or an {@link Roo.data.Record} object
14837 * created using {@link Roo.data.Record#create}.
14839 Roo.data.ArrayReader = function(meta, recordType)
14841 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847 * Create a data block containing Roo.data.Records from an XML document.
14848 * @param {Object} o An Array of row objects which represents the dataset.
14849 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14850 * a cache of Roo.data.Records.
14852 readRecords : function(o)
14854 var sid = this.meta ? this.meta.id : null;
14855 var recordType = this.recordType, fields = recordType.prototype.fields;
14858 for(var i = 0; i < root.length; i++){
14861 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14862 for(var j = 0, jlen = fields.length; j < jlen; j++){
14863 var f = fields.items[j];
14864 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14865 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14867 values[f.name] = v;
14869 var record = new recordType(values, id);
14871 records[records.length] = record;
14875 totalRecords : records.length
14878 // used when loading children.. @see loadDataFromChildren
14879 toLoadData: function(rec)
14881 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14882 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14893 * @class Roo.bootstrap.ComboBox
14894 * @extends Roo.bootstrap.TriggerField
14895 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14896 * @cfg {Boolean} append (true|false) default false
14897 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14898 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14899 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14900 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14901 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14902 * @cfg {Boolean} animate default true
14903 * @cfg {Boolean} emptyResultText only for touch device
14904 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14905 * @cfg {String} emptyTitle default ''
14906 * @cfg {Number} width fixed with? experimental
14908 * Create a new ComboBox.
14909 * @param {Object} config Configuration options
14911 Roo.bootstrap.ComboBox = function(config){
14912 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14916 * Fires when the dropdown list is expanded
14917 * @param {Roo.bootstrap.ComboBox} combo This combo box
14922 * Fires when the dropdown list is collapsed
14923 * @param {Roo.bootstrap.ComboBox} combo This combo box
14927 * @event beforeselect
14928 * Fires before a list item is selected. Return false to cancel the selection.
14929 * @param {Roo.bootstrap.ComboBox} combo This combo box
14930 * @param {Roo.data.Record} record The data record returned from the underlying store
14931 * @param {Number} index The index of the selected item in the dropdown list
14933 'beforeselect' : true,
14936 * Fires when a list item is selected
14937 * @param {Roo.bootstrap.ComboBox} combo This combo box
14938 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14939 * @param {Number} index The index of the selected item in the dropdown list
14943 * @event beforequery
14944 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14945 * The event object passed has these properties:
14946 * @param {Roo.bootstrap.ComboBox} combo This combo box
14947 * @param {String} query The query
14948 * @param {Boolean} forceAll true to force "all" query
14949 * @param {Boolean} cancel true to cancel the query
14950 * @param {Object} e The query event object
14952 'beforequery': true,
14955 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14956 * @param {Roo.bootstrap.ComboBox} combo This combo box
14961 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14962 * @param {Roo.bootstrap.ComboBox} combo This combo box
14963 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14968 * Fires when the remove value from the combobox array
14969 * @param {Roo.bootstrap.ComboBox} combo This combo box
14973 * @event afterremove
14974 * Fires when the remove value from the combobox array
14975 * @param {Roo.bootstrap.ComboBox} combo This combo box
14977 'afterremove' : true,
14979 * @event specialfilter
14980 * Fires when specialfilter
14981 * @param {Roo.bootstrap.ComboBox} combo This combo box
14983 'specialfilter' : true,
14986 * Fires when tick the element
14987 * @param {Roo.bootstrap.ComboBox} combo This combo box
14991 * @event touchviewdisplay
14992 * Fires when touch view require special display (default is using displayField)
14993 * @param {Roo.bootstrap.ComboBox} combo This combo box
14994 * @param {Object} cfg set html .
14996 'touchviewdisplay' : true
15001 this.tickItems = [];
15003 this.selectedIndex = -1;
15004 if(this.mode == 'local'){
15005 if(config.queryDelay === undefined){
15006 this.queryDelay = 10;
15008 if(config.minChars === undefined){
15014 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15018 * rendering into an Roo.Editor, defaults to false)
15021 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15022 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15029 * the dropdown list (defaults to undefined, with no header element)
15033 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15037 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15039 listWidth: undefined,
15041 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15042 * mode = 'remote' or 'text' if mode = 'local')
15044 displayField: undefined,
15047 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15048 * mode = 'remote' or 'value' if mode = 'local').
15049 * Note: use of a valueField requires the user make a selection
15050 * in order for a value to be mapped.
15052 valueField: undefined,
15054 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15059 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15060 * field's data value (defaults to the underlying DOM element's name)
15062 hiddenName: undefined,
15064 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15068 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15070 selectedClass: 'active',
15073 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15077 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15078 * anchor positions (defaults to 'tl-bl')
15080 listAlign: 'tl-bl?',
15082 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15086 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15087 * query specified by the allQuery config option (defaults to 'query')
15089 triggerAction: 'query',
15091 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15092 * (defaults to 4, does not apply if editable = false)
15096 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15097 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15101 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15102 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15106 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15107 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15111 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15112 * when editable = true (defaults to false)
15114 selectOnFocus:false,
15116 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15118 queryParam: 'query',
15120 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15121 * when mode = 'remote' (defaults to 'Loading...')
15123 loadingText: 'Loading...',
15125 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15129 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15133 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15134 * traditional select (defaults to true)
15138 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15142 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15146 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15147 * listWidth has a higher value)
15151 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15152 * allow the user to set arbitrary text into the field (defaults to false)
15154 forceSelection:false,
15156 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15157 * if typeAhead = true (defaults to 250)
15159 typeAheadDelay : 250,
15161 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15162 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15164 valueNotFoundText : undefined,
15166 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15168 blockFocus : false,
15171 * @cfg {Boolean} disableClear Disable showing of clear button.
15173 disableClear : false,
15175 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15177 alwaysQuery : false,
15180 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15185 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15187 invalidClass : "has-warning",
15190 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15192 validClass : "has-success",
15195 * @cfg {Boolean} specialFilter (true|false) special filter default false
15197 specialFilter : false,
15200 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15202 mobileTouchView : true,
15205 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15207 useNativeIOS : false,
15210 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15212 mobile_restrict_height : false,
15214 ios_options : false,
15226 btnPosition : 'right',
15227 triggerList : true,
15228 showToggleBtn : true,
15230 emptyResultText: 'Empty',
15231 triggerText : 'Select',
15235 // element that contains real text value.. (when hidden is used..)
15237 getAutoCreate : function()
15242 * Render classic select for iso
15245 if(Roo.isIOS && this.useNativeIOS){
15246 cfg = this.getAutoCreateNativeIOS();
15254 if(Roo.isTouch && this.mobileTouchView){
15255 cfg = this.getAutoCreateTouchView();
15262 if(!this.tickable){
15263 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15268 * ComboBox with tickable selections
15271 var align = this.labelAlign || this.parentLabelAlign();
15274 cls : 'form-group roo-combobox-tickable' //input-group
15277 var btn_text_select = '';
15278 var btn_text_done = '';
15279 var btn_text_cancel = '';
15281 if (this.btn_text_show) {
15282 btn_text_select = 'Select';
15283 btn_text_done = 'Done';
15284 btn_text_cancel = 'Cancel';
15289 cls : 'tickable-buttons',
15294 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15295 //html : this.triggerText
15296 html: btn_text_select
15302 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15304 html: btn_text_done
15310 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15312 html: btn_text_cancel
15318 buttons.cn.unshift({
15320 cls: 'roo-select2-search-field-input'
15326 Roo.each(buttons.cn, function(c){
15328 c.cls += ' btn-' + _this.size;
15331 if (_this.disabled) {
15338 style : 'display: contents',
15343 cls: 'form-hidden-field'
15347 cls: 'roo-select2-choices',
15351 cls: 'roo-select2-search-field',
15362 cls: 'roo-select2-container input-group roo-select2-container-multi',
15368 // cls: 'typeahead typeahead-long dropdown-menu',
15369 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15374 if(this.hasFeedback && !this.allowBlank){
15378 cls: 'glyphicon form-control-feedback'
15381 combobox.cn.push(feedback);
15388 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15389 tooltip : 'This field is required'
15391 if (Roo.bootstrap.version == 4) {
15394 style : 'display:none'
15397 if (align ==='left' && this.fieldLabel.length) {
15399 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15406 cls : 'control-label col-form-label',
15407 html : this.fieldLabel
15419 var labelCfg = cfg.cn[1];
15420 var contentCfg = cfg.cn[2];
15423 if(this.indicatorpos == 'right'){
15429 cls : 'control-label col-form-label',
15433 html : this.fieldLabel
15449 labelCfg = cfg.cn[0];
15450 contentCfg = cfg.cn[1];
15454 if(this.labelWidth > 12){
15455 labelCfg.style = "width: " + this.labelWidth + 'px';
15457 if(this.width * 1 > 0){
15458 contentCfg.style = "width: " + this.width + 'px';
15460 if(this.labelWidth < 13 && this.labelmd == 0){
15461 this.labelmd = this.labelWidth;
15464 if(this.labellg > 0){
15465 labelCfg.cls += ' col-lg-' + this.labellg;
15466 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469 if(this.labelmd > 0){
15470 labelCfg.cls += ' col-md-' + this.labelmd;
15471 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474 if(this.labelsm > 0){
15475 labelCfg.cls += ' col-sm-' + this.labelsm;
15476 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479 if(this.labelxs > 0){
15480 labelCfg.cls += ' col-xs-' + this.labelxs;
15481 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15485 } else if ( this.fieldLabel.length) {
15486 // Roo.log(" label");
15491 //cls : 'input-group-addon',
15492 html : this.fieldLabel
15497 if(this.indicatorpos == 'right'){
15501 //cls : 'input-group-addon',
15502 html : this.fieldLabel
15512 // Roo.log(" no label && no align");
15519 ['xs','sm','md','lg'].map(function(size){
15520 if (settings[size]) {
15521 cfg.cls += ' col-' + size + '-' + settings[size];
15529 _initEventsCalled : false,
15532 initEvents: function()
15534 if (this._initEventsCalled) { // as we call render... prevent looping...
15537 this._initEventsCalled = true;
15540 throw "can not find store for combo";
15543 this.indicator = this.indicatorEl();
15545 this.store = Roo.factory(this.store, Roo.data);
15546 this.store.parent = this;
15548 // if we are building from html. then this element is so complex, that we can not really
15549 // use the rendered HTML.
15550 // so we have to trash and replace the previous code.
15551 if (Roo.XComponent.build_from_html) {
15552 // remove this element....
15553 var e = this.el.dom, k=0;
15554 while (e ) { e = e.previousSibling; ++k;}
15559 this.rendered = false;
15561 this.render(this.parent().getChildContainer(true), k);
15564 if(Roo.isIOS && this.useNativeIOS){
15565 this.initIOSView();
15573 if(Roo.isTouch && this.mobileTouchView){
15574 this.initTouchView();
15579 this.initTickableEvents();
15583 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15585 if(this.hiddenName){
15587 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15589 this.hiddenField.dom.value =
15590 this.hiddenValue !== undefined ? this.hiddenValue :
15591 this.value !== undefined ? this.value : '';
15593 // prevent input submission
15594 this.el.dom.removeAttribute('name');
15595 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15600 // this.el.dom.setAttribute('autocomplete', 'off');
15603 var cls = 'x-combo-list';
15605 //this.list = new Roo.Layer({
15606 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15612 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15613 _this.list.setWidth(lw);
15616 this.list.on('mouseover', this.onViewOver, this);
15617 this.list.on('mousemove', this.onViewMove, this);
15618 this.list.on('scroll', this.onViewScroll, this);
15621 this.list.swallowEvent('mousewheel');
15622 this.assetHeight = 0;
15625 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15626 this.assetHeight += this.header.getHeight();
15629 this.innerList = this.list.createChild({cls:cls+'-inner'});
15630 this.innerList.on('mouseover', this.onViewOver, this);
15631 this.innerList.on('mousemove', this.onViewMove, this);
15632 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15634 if(this.allowBlank && !this.pageSize && !this.disableClear){
15635 this.footer = this.list.createChild({cls:cls+'-ft'});
15636 this.pageTb = new Roo.Toolbar(this.footer);
15640 this.footer = this.list.createChild({cls:cls+'-ft'});
15641 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15642 {pageSize: this.pageSize});
15646 if (this.pageTb && this.allowBlank && !this.disableClear) {
15648 this.pageTb.add(new Roo.Toolbar.Fill(), {
15649 cls: 'x-btn-icon x-btn-clear',
15651 handler: function()
15654 _this.clearValue();
15655 _this.onSelect(false, -1);
15660 this.assetHeight += this.footer.getHeight();
15665 this.tpl = Roo.bootstrap.version == 4 ?
15666 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15667 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670 this.view = new Roo.View(this.list, this.tpl, {
15671 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15673 //this.view.wrapEl.setDisplayed(false);
15674 this.view.on('click', this.onViewClick, this);
15677 this.store.on('beforeload', this.onBeforeLoad, this);
15678 this.store.on('load', this.onLoad, this);
15679 this.store.on('loadexception', this.onLoadException, this);
15681 if(this.resizable){
15682 this.resizer = new Roo.Resizable(this.list, {
15683 pinned:true, handles:'se'
15685 this.resizer.on('resize', function(r, w, h){
15686 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15687 this.listWidth = w;
15688 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15689 this.restrictHeight();
15691 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694 if(!this.editable){
15695 this.editable = true;
15696 this.setEditable(false);
15701 if (typeof(this.events.add.listeners) != 'undefined') {
15703 this.addicon = this.wrap.createChild(
15704 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15706 this.addicon.on('click', function(e) {
15707 this.fireEvent('add', this);
15710 if (typeof(this.events.edit.listeners) != 'undefined') {
15712 this.editicon = this.wrap.createChild(
15713 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15714 if (this.addicon) {
15715 this.editicon.setStyle('margin-left', '40px');
15717 this.editicon.on('click', function(e) {
15719 // we fire even if inothing is selected..
15720 this.fireEvent('edit', this, this.lastData );
15726 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15727 "up" : function(e){
15728 this.inKeyMode = true;
15732 "down" : function(e){
15733 if(!this.isExpanded()){
15734 this.onTriggerClick();
15736 this.inKeyMode = true;
15741 "enter" : function(e){
15742 // this.onViewClick();
15746 if(this.fireEvent("specialkey", this, e)){
15747 this.onViewClick(false);
15753 "esc" : function(e){
15757 "tab" : function(e){
15760 if(this.fireEvent("specialkey", this, e)){
15761 this.onViewClick(false);
15769 doRelay : function(foo, bar, hname){
15770 if(hname == 'down' || this.scope.isExpanded()){
15771 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15780 this.queryDelay = Math.max(this.queryDelay || 10,
15781 this.mode == 'local' ? 10 : 250);
15784 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15786 if(this.typeAhead){
15787 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15789 if(this.editable !== false){
15790 this.inputEl().on("keyup", this.onKeyUp, this);
15792 if(this.forceSelection){
15793 this.inputEl().on('blur', this.doForce, this);
15797 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15798 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15802 initTickableEvents: function()
15806 if(this.hiddenName){
15808 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15810 this.hiddenField.dom.value =
15811 this.hiddenValue !== undefined ? this.hiddenValue :
15812 this.value !== undefined ? this.value : '';
15814 // prevent input submission
15815 this.el.dom.removeAttribute('name');
15816 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15821 // this.list = this.el.select('ul.dropdown-menu',true).first();
15823 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15824 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15825 if(this.triggerList){
15826 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15830 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15832 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15833 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15835 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15836 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15838 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15839 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15840 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843 this.cancelBtn.hide();
15848 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15849 _this.list.setWidth(lw);
15852 this.list.on('mouseover', this.onViewOver, this);
15853 this.list.on('mousemove', this.onViewMove, this);
15855 this.list.on('scroll', this.onViewScroll, this);
15858 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15859 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862 this.view = new Roo.View(this.list, this.tpl, {
15867 selectedClass: this.selectedClass
15870 //this.view.wrapEl.setDisplayed(false);
15871 this.view.on('click', this.onViewClick, this);
15875 this.store.on('beforeload', this.onBeforeLoad, this);
15876 this.store.on('load', this.onLoad, this);
15877 this.store.on('loadexception', this.onLoadException, this);
15880 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15881 "up" : function(e){
15882 this.inKeyMode = true;
15886 "down" : function(e){
15887 this.inKeyMode = true;
15891 "enter" : function(e){
15892 if(this.fireEvent("specialkey", this, e)){
15893 this.onViewClick(false);
15899 "esc" : function(e){
15900 this.onTickableFooterButtonClick(e, false, false);
15903 "tab" : function(e){
15904 this.fireEvent("specialkey", this, e);
15906 this.onTickableFooterButtonClick(e, false, false);
15913 doRelay : function(e, fn, key){
15914 if(this.scope.isExpanded()){
15915 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15924 this.queryDelay = Math.max(this.queryDelay || 10,
15925 this.mode == 'local' ? 10 : 250);
15928 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15930 if(this.typeAhead){
15931 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934 if(this.editable !== false){
15935 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938 this.indicator = this.indicatorEl();
15940 if(this.indicator){
15941 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15942 this.indicator.hide();
15947 onDestroy : function(){
15949 this.view.setStore(null);
15950 this.view.el.removeAllListeners();
15951 this.view.el.remove();
15952 this.view.purgeListeners();
15955 this.list.dom.innerHTML = '';
15959 this.store.un('beforeload', this.onBeforeLoad, this);
15960 this.store.un('load', this.onLoad, this);
15961 this.store.un('loadexception', this.onLoadException, this);
15963 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15967 fireKey : function(e){
15968 if(e.isNavKeyPress() && !this.list.isVisible()){
15969 this.fireEvent("specialkey", this, e);
15974 onResize: function(w, h)
15978 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15980 // if(typeof w != 'number'){
15981 // // we do not handle it!?!?
15984 // var tw = this.trigger.getWidth();
15985 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15986 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15988 // this.inputEl().setWidth( this.adjustWidth('input', x));
15990 // //this.trigger.setStyle('left', x+'px');
15992 // if(this.list && this.listWidth === undefined){
15993 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15994 // this.list.setWidth(lw);
15995 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16003 * Allow or prevent the user from directly editing the field text. If false is passed,
16004 * the user will only be able to select from the items defined in the dropdown list. This method
16005 * is the runtime equivalent of setting the 'editable' config option at config time.
16006 * @param {Boolean} value True to allow the user to directly edit the field text
16008 setEditable : function(value){
16009 if(value == this.editable){
16012 this.editable = value;
16014 this.inputEl().dom.setAttribute('readOnly', true);
16015 this.inputEl().on('mousedown', this.onTriggerClick, this);
16016 this.inputEl().addClass('x-combo-noedit');
16018 this.inputEl().dom.setAttribute('readOnly', false);
16019 this.inputEl().un('mousedown', this.onTriggerClick, this);
16020 this.inputEl().removeClass('x-combo-noedit');
16026 onBeforeLoad : function(combo,opts){
16027 if(!this.hasFocus){
16031 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16033 this.restrictHeight();
16034 this.selectedIndex = -1;
16038 onLoad : function(){
16040 this.hasQuery = false;
16042 if(!this.hasFocus){
16046 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16047 this.loading.hide();
16050 if(this.store.getCount() > 0){
16053 this.restrictHeight();
16054 if(this.lastQuery == this.allQuery){
16055 if(this.editable && !this.tickable){
16056 this.inputEl().dom.select();
16060 !this.selectByValue(this.value, true) &&
16063 !this.store.lastOptions ||
16064 typeof(this.store.lastOptions.add) == 'undefined' ||
16065 this.store.lastOptions.add != true
16068 this.select(0, true);
16071 if(this.autoFocus){
16074 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16075 this.taTask.delay(this.typeAheadDelay);
16079 this.onEmptyResults();
16085 onLoadException : function()
16087 this.hasQuery = false;
16089 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16090 this.loading.hide();
16093 if(this.tickable && this.editable){
16098 // only causes errors at present
16099 //Roo.log(this.store.reader.jsonData);
16100 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16102 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16108 onTypeAhead : function(){
16109 if(this.store.getCount() > 0){
16110 var r = this.store.getAt(0);
16111 var newValue = r.data[this.displayField];
16112 var len = newValue.length;
16113 var selStart = this.getRawValue().length;
16115 if(selStart != len){
16116 this.setRawValue(newValue);
16117 this.selectText(selStart, newValue.length);
16123 onSelect : function(record, index){
16125 if(this.fireEvent('beforeselect', this, record, index) !== false){
16127 this.setFromData(index > -1 ? record.data : false);
16130 this.fireEvent('select', this, record, index);
16135 * Returns the currently selected field value or empty string if no value is set.
16136 * @return {String} value The selected value
16138 getValue : function()
16140 if(Roo.isIOS && this.useNativeIOS){
16141 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16145 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148 if(this.valueField){
16149 return typeof this.value != 'undefined' ? this.value : '';
16151 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16155 getRawValue : function()
16157 if(Roo.isIOS && this.useNativeIOS){
16158 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161 var v = this.inputEl().getValue();
16167 * Clears any text/value currently set in the field
16169 clearValue : function(){
16171 if(this.hiddenField){
16172 this.hiddenField.dom.value = '';
16175 this.setRawValue('');
16176 this.lastSelectionText = '';
16177 this.lastData = false;
16179 var close = this.closeTriggerEl();
16190 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16191 * will be displayed in the field. If the value does not match the data value of an existing item,
16192 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16193 * Otherwise the field will be blank (although the value will still be set).
16194 * @param {String} value The value to match
16196 setValue : function(v)
16198 if(Roo.isIOS && this.useNativeIOS){
16199 this.setIOSValue(v);
16209 if(this.valueField){
16210 var r = this.findRecord(this.valueField, v);
16212 text = r.data[this.displayField];
16213 }else if(this.valueNotFoundText !== undefined){
16214 text = this.valueNotFoundText;
16217 this.lastSelectionText = text;
16218 if(this.hiddenField){
16219 this.hiddenField.dom.value = v;
16221 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224 var close = this.closeTriggerEl();
16227 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16233 * @property {Object} the last set data for the element
16238 * Sets the value of the field based on a object which is related to the record format for the store.
16239 * @param {Object} value the value to set as. or false on reset?
16241 setFromData : function(o){
16248 var dv = ''; // display value
16249 var vv = ''; // value value..
16251 if (this.displayField) {
16252 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16254 // this is an error condition!!!
16255 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16258 if(this.valueField){
16259 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262 var close = this.closeTriggerEl();
16265 if(dv.length || vv * 1 > 0){
16267 this.blockFocus=true;
16273 if(this.hiddenField){
16274 this.hiddenField.dom.value = vv;
16276 this.lastSelectionText = dv;
16277 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16281 // no hidden field.. - we store the value in 'value', but still display
16282 // display field!!!!
16283 this.lastSelectionText = dv;
16284 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16291 reset : function(){
16292 // overridden so that last data is reset..
16299 this.setValue(this.originalValue);
16300 //this.clearInvalid();
16301 this.lastData = false;
16303 this.view.clearSelections();
16309 findRecord : function(prop, value){
16311 if(this.store.getCount() > 0){
16312 this.store.each(function(r){
16313 if(r.data[prop] == value){
16323 getName: function()
16325 // returns hidden if it's set..
16326 if (!this.rendered) {return ''};
16327 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16331 onViewMove : function(e, t){
16332 this.inKeyMode = false;
16336 onViewOver : function(e, t){
16337 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340 var item = this.view.findItemFromChild(t);
16343 var index = this.view.indexOf(item);
16344 this.select(index, false);
16349 onViewClick : function(view, doFocus, el, e)
16351 var index = this.view.getSelectedIndexes()[0];
16353 var r = this.store.getAt(index);
16357 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16364 Roo.each(this.tickItems, function(v,k){
16366 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16368 _this.tickItems.splice(k, 1);
16370 if(typeof(e) == 'undefined' && view == false){
16371 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16383 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16384 this.tickItems.push(r.data);
16387 if(typeof(e) == 'undefined' && view == false){
16388 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16395 this.onSelect(r, index);
16397 if(doFocus !== false && !this.blockFocus){
16398 this.inputEl().focus();
16403 restrictHeight : function(){
16404 //this.innerList.dom.style.height = '';
16405 //var inner = this.innerList.dom;
16406 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16407 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16408 //this.list.beginUpdate();
16409 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16410 this.list.alignTo(this.inputEl(), this.listAlign);
16411 this.list.alignTo(this.inputEl(), this.listAlign);
16412 //this.list.endUpdate();
16416 onEmptyResults : function(){
16418 if(this.tickable && this.editable){
16419 this.hasFocus = false;
16420 this.restrictHeight();
16428 * Returns true if the dropdown list is expanded, else false.
16430 isExpanded : function(){
16431 return this.list.isVisible();
16435 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16436 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16437 * @param {String} value The data value of the item to select
16438 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16439 * selected item if it is not currently in view (defaults to true)
16440 * @return {Boolean} True if the value matched an item in the list, else false
16442 selectByValue : function(v, scrollIntoView){
16443 if(v !== undefined && v !== null){
16444 var r = this.findRecord(this.valueField || this.displayField, v);
16446 this.select(this.store.indexOf(r), scrollIntoView);
16454 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16455 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16456 * @param {Number} index The zero-based index of the list item to select
16457 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16458 * selected item if it is not currently in view (defaults to true)
16460 select : function(index, scrollIntoView){
16461 this.selectedIndex = index;
16462 this.view.select(index);
16463 if(scrollIntoView !== false){
16464 var el = this.view.getNode(index);
16466 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469 this.list.scrollChildIntoView(el, false);
16475 selectNext : function(){
16476 var ct = this.store.getCount();
16478 if(this.selectedIndex == -1){
16480 }else if(this.selectedIndex < ct-1){
16481 this.select(this.selectedIndex+1);
16487 selectPrev : function(){
16488 var ct = this.store.getCount();
16490 if(this.selectedIndex == -1){
16492 }else if(this.selectedIndex != 0){
16493 this.select(this.selectedIndex-1);
16499 onKeyUp : function(e){
16500 if(this.editable !== false && !e.isSpecialKey()){
16501 this.lastKey = e.getKey();
16502 this.dqTask.delay(this.queryDelay);
16507 validateBlur : function(){
16508 return !this.list || !this.list.isVisible();
16512 initQuery : function(){
16514 var v = this.getRawValue();
16516 if(this.tickable && this.editable){
16517 v = this.tickableInputEl().getValue();
16524 doForce : function(){
16525 if(this.inputEl().dom.value.length > 0){
16526 this.inputEl().dom.value =
16527 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16533 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16534 * query allowing the query action to be canceled if needed.
16535 * @param {String} query The SQL query to execute
16536 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16537 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16538 * saved in the current store (defaults to false)
16540 doQuery : function(q, forceAll){
16542 if(q === undefined || q === null){
16547 forceAll: forceAll,
16551 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16556 forceAll = qe.forceAll;
16557 if(forceAll === true || (q.length >= this.minChars)){
16559 this.hasQuery = true;
16561 if(this.lastQuery != q || this.alwaysQuery){
16562 this.lastQuery = q;
16563 if(this.mode == 'local'){
16564 this.selectedIndex = -1;
16566 this.store.clearFilter();
16569 if(this.specialFilter){
16570 this.fireEvent('specialfilter', this);
16575 this.store.filter(this.displayField, q);
16578 this.store.fireEvent("datachanged", this.store);
16585 this.store.baseParams[this.queryParam] = q;
16587 var options = {params : this.getParams(q)};
16590 options.add = true;
16591 options.params.start = this.page * this.pageSize;
16594 this.store.load(options);
16597 * this code will make the page width larger, at the beginning, the list not align correctly,
16598 * we should expand the list on onLoad
16599 * so command out it
16604 this.selectedIndex = -1;
16609 this.loadNext = false;
16613 getParams : function(q){
16615 //p[this.queryParam] = q;
16619 p.limit = this.pageSize;
16625 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16627 collapse : function(){
16628 if(!this.isExpanded()){
16634 this.hasFocus = false;
16638 this.cancelBtn.hide();
16639 this.trigger.show();
16642 this.tickableInputEl().dom.value = '';
16643 this.tickableInputEl().blur();
16648 Roo.get(document).un('mousedown', this.collapseIf, this);
16649 Roo.get(document).un('mousewheel', this.collapseIf, this);
16650 if (!this.editable) {
16651 Roo.get(document).un('keydown', this.listKeyPress, this);
16653 this.fireEvent('collapse', this);
16659 collapseIf : function(e){
16660 var in_combo = e.within(this.el);
16661 var in_list = e.within(this.list);
16662 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16664 if (in_combo || in_list || is_list) {
16665 //e.stopPropagation();
16670 this.onTickableFooterButtonClick(e, false, false);
16678 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16680 expand : function(){
16682 if(this.isExpanded() || !this.hasFocus){
16686 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16687 this.list.setWidth(lw);
16693 this.restrictHeight();
16697 this.tickItems = Roo.apply([], this.item);
16700 this.cancelBtn.show();
16701 this.trigger.hide();
16704 this.tickableInputEl().focus();
16709 Roo.get(document).on('mousedown', this.collapseIf, this);
16710 Roo.get(document).on('mousewheel', this.collapseIf, this);
16711 if (!this.editable) {
16712 Roo.get(document).on('keydown', this.listKeyPress, this);
16715 this.fireEvent('expand', this);
16719 // Implements the default empty TriggerField.onTriggerClick function
16720 onTriggerClick : function(e)
16722 Roo.log('trigger click');
16724 if(this.disabled || !this.triggerList){
16729 this.loadNext = false;
16731 if(this.isExpanded()){
16733 if (!this.blockFocus) {
16734 this.inputEl().focus();
16738 this.hasFocus = true;
16739 if(this.triggerAction == 'all') {
16740 this.doQuery(this.allQuery, true);
16742 this.doQuery(this.getRawValue());
16744 if (!this.blockFocus) {
16745 this.inputEl().focus();
16750 onTickableTriggerClick : function(e)
16757 this.loadNext = false;
16758 this.hasFocus = true;
16760 if(this.triggerAction == 'all') {
16761 this.doQuery(this.allQuery, true);
16763 this.doQuery(this.getRawValue());
16767 onSearchFieldClick : function(e)
16769 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16770 this.onTickableFooterButtonClick(e, false, false);
16774 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16779 this.loadNext = false;
16780 this.hasFocus = true;
16782 if(this.triggerAction == 'all') {
16783 this.doQuery(this.allQuery, true);
16785 this.doQuery(this.getRawValue());
16789 listKeyPress : function(e)
16791 //Roo.log('listkeypress');
16792 // scroll to first matching element based on key pres..
16793 if (e.isSpecialKey()) {
16796 var k = String.fromCharCode(e.getKey()).toUpperCase();
16799 var csel = this.view.getSelectedNodes();
16800 var cselitem = false;
16802 var ix = this.view.indexOf(csel[0]);
16803 cselitem = this.store.getAt(ix);
16804 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16810 this.store.each(function(v) {
16812 // start at existing selection.
16813 if (cselitem.id == v.id) {
16819 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16820 match = this.store.indexOf(v);
16826 if (match === false) {
16827 return true; // no more action?
16830 this.view.select(match);
16831 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16832 sn.scrollIntoView(sn.dom.parentNode, false);
16835 onViewScroll : function(e, t){
16837 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){
16841 this.hasQuery = true;
16843 this.loading = this.list.select('.loading', true).first();
16845 if(this.loading === null){
16846 this.list.createChild({
16848 cls: 'loading roo-select2-more-results roo-select2-active',
16849 html: 'Loading more results...'
16852 this.loading = this.list.select('.loading', true).first();
16854 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16856 this.loading.hide();
16859 this.loading.show();
16864 this.loadNext = true;
16866 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16871 addItem : function(o)
16873 var dv = ''; // display value
16875 if (this.displayField) {
16876 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16878 // this is an error condition!!!
16879 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16886 var choice = this.choices.createChild({
16888 cls: 'roo-select2-search-choice',
16897 cls: 'roo-select2-search-choice-close fa fa-times',
16902 }, this.searchField);
16904 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16906 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16914 this.inputEl().dom.value = '';
16919 onRemoveItem : function(e, _self, o)
16921 e.preventDefault();
16923 this.lastItem = Roo.apply([], this.item);
16925 var index = this.item.indexOf(o.data) * 1;
16928 Roo.log('not this item?!');
16932 this.item.splice(index, 1);
16937 this.fireEvent('remove', this, e);
16943 syncValue : function()
16945 if(!this.item.length){
16952 Roo.each(this.item, function(i){
16953 if(_this.valueField){
16954 value.push(i[_this.valueField]);
16961 this.value = value.join(',');
16963 if(this.hiddenField){
16964 this.hiddenField.dom.value = this.value;
16967 this.store.fireEvent("datachanged", this.store);
16972 clearItem : function()
16974 if(!this.multiple){
16980 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16988 if(this.tickable && !Roo.isTouch){
16989 this.view.refresh();
16993 inputEl: function ()
16995 if(Roo.isIOS && this.useNativeIOS){
16996 return this.el.select('select.roo-ios-select', true).first();
16999 if(Roo.isTouch && this.mobileTouchView){
17000 return this.el.select('input.form-control',true).first();
17004 return this.searchField;
17007 return this.el.select('input.form-control',true).first();
17010 onTickableFooterButtonClick : function(e, btn, el)
17012 e.preventDefault();
17014 this.lastItem = Roo.apply([], this.item);
17016 if(btn && btn.name == 'cancel'){
17017 this.tickItems = Roo.apply([], this.item);
17026 Roo.each(this.tickItems, function(o){
17034 validate : function()
17036 if(this.getVisibilityEl().hasClass('hidden')){
17040 var v = this.getRawValue();
17043 v = this.getValue();
17046 if(this.disabled || this.allowBlank || v.length){
17051 this.markInvalid();
17055 tickableInputEl : function()
17057 if(!this.tickable || !this.editable){
17058 return this.inputEl();
17061 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17065 getAutoCreateTouchView : function()
17070 cls: 'form-group' //input-group
17076 type : this.inputType,
17077 cls : 'form-control x-combo-noedit',
17078 autocomplete: 'new-password',
17079 placeholder : this.placeholder || '',
17084 input.name = this.name;
17088 input.cls += ' input-' + this.size;
17091 if (this.disabled) {
17092 input.disabled = true;
17096 cls : 'roo-combobox-wrap',
17103 inputblock.cls += ' input-group';
17105 inputblock.cn.unshift({
17107 cls : 'input-group-addon input-group-prepend input-group-text',
17112 if(this.removable && !this.multiple){
17113 inputblock.cls += ' roo-removable';
17115 inputblock.cn.push({
17118 cls : 'roo-combo-removable-btn close'
17122 if(this.hasFeedback && !this.allowBlank){
17124 inputblock.cls += ' has-feedback';
17126 inputblock.cn.push({
17128 cls: 'glyphicon form-control-feedback'
17135 inputblock.cls += (this.before) ? '' : ' input-group';
17137 inputblock.cn.push({
17139 cls : 'input-group-addon input-group-append input-group-text',
17145 var ibwrap = inputblock;
17150 cls: 'roo-select2-choices',
17154 cls: 'roo-select2-search-field',
17167 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17172 cls: 'form-hidden-field'
17178 if(!this.multiple && this.showToggleBtn){
17184 if (this.caret != false) {
17187 cls: 'fa fa-' + this.caret
17194 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17196 Roo.bootstrap.version == 3 ? caret : '',
17199 cls: 'combobox-clear',
17213 combobox.cls += ' roo-select2-container-multi';
17216 var align = this.labelAlign || this.parentLabelAlign();
17218 if (align ==='left' && this.fieldLabel.length) {
17223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17224 tooltip : 'This field is required'
17228 cls : 'control-label col-form-label',
17229 html : this.fieldLabel
17233 cls : 'roo-combobox-wrap ',
17240 var labelCfg = cfg.cn[1];
17241 var contentCfg = cfg.cn[2];
17244 if(this.indicatorpos == 'right'){
17249 cls : 'control-label col-form-label',
17253 html : this.fieldLabel
17257 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17258 tooltip : 'This field is required'
17263 cls : "roo-combobox-wrap ",
17271 labelCfg = cfg.cn[0];
17272 contentCfg = cfg.cn[1];
17277 if(this.labelWidth > 12){
17278 labelCfg.style = "width: " + this.labelWidth + 'px';
17281 if(this.labelWidth < 13 && this.labelmd == 0){
17282 this.labelmd = this.labelWidth;
17285 if(this.labellg > 0){
17286 labelCfg.cls += ' col-lg-' + this.labellg;
17287 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290 if(this.labelmd > 0){
17291 labelCfg.cls += ' col-md-' + this.labelmd;
17292 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295 if(this.labelsm > 0){
17296 labelCfg.cls += ' col-sm-' + this.labelsm;
17297 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300 if(this.labelxs > 0){
17301 labelCfg.cls += ' col-xs-' + this.labelxs;
17302 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17306 } else if ( this.fieldLabel.length) {
17310 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17311 tooltip : 'This field is required'
17315 cls : 'control-label',
17316 html : this.fieldLabel
17327 if(this.indicatorpos == 'right'){
17331 cls : 'control-label',
17332 html : this.fieldLabel,
17336 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17337 tooltip : 'This field is required'
17354 var settings = this;
17356 ['xs','sm','md','lg'].map(function(size){
17357 if (settings[size]) {
17358 cfg.cls += ' col-' + size + '-' + settings[size];
17365 initTouchView : function()
17367 this.renderTouchView();
17369 this.touchViewEl.on('scroll', function(){
17370 this.el.dom.scrollTop = 0;
17373 this.originalValue = this.getValue();
17375 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17377 this.inputEl().on("click", this.showTouchView, this);
17378 if (this.triggerEl) {
17379 this.triggerEl.on("click", this.showTouchView, this);
17383 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17384 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17386 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17388 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17389 this.store.on('load', this.onTouchViewLoad, this);
17390 this.store.on('loadexception', this.onTouchViewLoadException, this);
17392 if(this.hiddenName){
17394 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17396 this.hiddenField.dom.value =
17397 this.hiddenValue !== undefined ? this.hiddenValue :
17398 this.value !== undefined ? this.value : '';
17400 this.el.dom.removeAttribute('name');
17401 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17405 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17406 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409 if(this.removable && !this.multiple){
17410 var close = this.closeTriggerEl();
17412 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17413 close.on('click', this.removeBtnClick, this, close);
17417 * fix the bug in Safari iOS8
17419 this.inputEl().on("focus", function(e){
17420 document.activeElement.blur();
17423 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17430 renderTouchView : function()
17432 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17433 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17435 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17436 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17438 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17439 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17440 this.touchViewBodyEl.setStyle('overflow', 'auto');
17442 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17443 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17445 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17446 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17450 showTouchView : function()
17456 this.touchViewHeaderEl.hide();
17458 if(this.modalTitle.length){
17459 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17460 this.touchViewHeaderEl.show();
17463 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17464 this.touchViewEl.show();
17466 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17468 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17469 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17471 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17473 if(this.modalTitle.length){
17474 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477 this.touchViewBodyEl.setHeight(bodyHeight);
17481 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17483 this.touchViewEl.addClass(['in','show']);
17486 if(this._touchViewMask){
17487 Roo.get(document.body).addClass("x-body-masked");
17488 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17489 this._touchViewMask.setStyle('z-index', 10000);
17490 this._touchViewMask.addClass('show');
17493 this.doTouchViewQuery();
17497 hideTouchView : function()
17499 this.touchViewEl.removeClass(['in','show']);
17503 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17505 this.touchViewEl.setStyle('display', 'none');
17508 if(this._touchViewMask){
17509 this._touchViewMask.removeClass('show');
17510 Roo.get(document.body).removeClass("x-body-masked");
17514 setTouchViewValue : function()
17521 Roo.each(this.tickItems, function(o){
17526 this.hideTouchView();
17529 doTouchViewQuery : function()
17538 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17542 if(!this.alwaysQuery || this.mode == 'local'){
17543 this.onTouchViewLoad();
17550 onTouchViewBeforeLoad : function(combo,opts)
17556 onTouchViewLoad : function()
17558 if(this.store.getCount() < 1){
17559 this.onTouchViewEmptyResults();
17563 this.clearTouchView();
17565 var rawValue = this.getRawValue();
17567 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17569 this.tickItems = [];
17571 this.store.data.each(function(d, rowIndex){
17572 var row = this.touchViewListGroup.createChild(template);
17574 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17575 row.addClass(d.data.cls);
17578 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581 html : d.data[this.displayField]
17584 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17585 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588 row.removeClass('selected');
17589 if(!this.multiple && this.valueField &&
17590 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17594 row.addClass('selected');
17597 if(this.multiple && this.valueField &&
17598 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17602 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17603 this.tickItems.push(d.data);
17606 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17610 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17612 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17614 if(this.modalTitle.length){
17615 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17620 if(this.mobile_restrict_height && listHeight < bodyHeight){
17621 this.touchViewBodyEl.setHeight(listHeight);
17626 if(firstChecked && listHeight > bodyHeight){
17627 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17632 onTouchViewLoadException : function()
17634 this.hideTouchView();
17637 onTouchViewEmptyResults : function()
17639 this.clearTouchView();
17641 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17643 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17647 clearTouchView : function()
17649 this.touchViewListGroup.dom.innerHTML = '';
17652 onTouchViewClick : function(e, el, o)
17654 e.preventDefault();
17657 var rowIndex = o.rowIndex;
17659 var r = this.store.getAt(rowIndex);
17661 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17663 if(!this.multiple){
17664 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17665 c.dom.removeAttribute('checked');
17668 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17670 this.setFromData(r.data);
17672 var close = this.closeTriggerEl();
17678 this.hideTouchView();
17680 this.fireEvent('select', this, r, rowIndex);
17685 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17686 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17687 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17691 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17692 this.addItem(r.data);
17693 this.tickItems.push(r.data);
17697 getAutoCreateNativeIOS : function()
17700 cls: 'form-group' //input-group,
17705 cls : 'roo-ios-select'
17709 combobox.name = this.name;
17712 if (this.disabled) {
17713 combobox.disabled = true;
17716 var settings = this;
17718 ['xs','sm','md','lg'].map(function(size){
17719 if (settings[size]) {
17720 cfg.cls += ' col-' + size + '-' + settings[size];
17730 initIOSView : function()
17732 this.store.on('load', this.onIOSViewLoad, this);
17737 onIOSViewLoad : function()
17739 if(this.store.getCount() < 1){
17743 this.clearIOSView();
17745 if(this.allowBlank) {
17747 var default_text = '-- SELECT --';
17749 if(this.placeholder.length){
17750 default_text = this.placeholder;
17753 if(this.emptyTitle.length){
17754 default_text += ' - ' + this.emptyTitle + ' -';
17757 var opt = this.inputEl().createChild({
17760 html : default_text
17764 o[this.valueField] = 0;
17765 o[this.displayField] = default_text;
17767 this.ios_options.push({
17774 this.store.data.each(function(d, rowIndex){
17778 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17779 html = d.data[this.displayField];
17784 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17785 value = d.data[this.valueField];
17794 if(this.value == d.data[this.valueField]){
17795 option['selected'] = true;
17798 var opt = this.inputEl().createChild(option);
17800 this.ios_options.push({
17807 this.inputEl().on('change', function(){
17808 this.fireEvent('select', this);
17813 clearIOSView: function()
17815 this.inputEl().dom.innerHTML = '';
17817 this.ios_options = [];
17820 setIOSValue: function(v)
17824 if(!this.ios_options){
17828 Roo.each(this.ios_options, function(opts){
17830 opts.el.dom.removeAttribute('selected');
17832 if(opts.data[this.valueField] != v){
17836 opts.el.dom.setAttribute('selected', true);
17842 * @cfg {Boolean} grow
17846 * @cfg {Number} growMin
17850 * @cfg {Number} growMax
17859 Roo.apply(Roo.bootstrap.ComboBox, {
17863 cls: 'modal-header',
17885 cls: 'list-group-item',
17889 cls: 'roo-combobox-list-group-item-value'
17893 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17907 listItemCheckbox : {
17909 cls: 'list-group-item',
17913 cls: 'roo-combobox-list-group-item-value'
17917 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17933 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17938 cls: 'modal-footer',
17946 cls: 'col-xs-6 text-left',
17949 cls: 'btn btn-danger roo-touch-view-cancel',
17955 cls: 'col-xs-6 text-right',
17958 cls: 'btn btn-success roo-touch-view-ok',
17969 Roo.apply(Roo.bootstrap.ComboBox, {
17971 touchViewTemplate : {
17973 cls: 'modal fade roo-combobox-touch-view',
17977 cls: 'modal-dialog',
17978 style : 'position:fixed', // we have to fix position....
17982 cls: 'modal-content',
17984 Roo.bootstrap.ComboBox.header,
17985 Roo.bootstrap.ComboBox.body,
17986 Roo.bootstrap.ComboBox.footer
17995 * Ext JS Library 1.1.1
17996 * Copyright(c) 2006-2007, Ext JS, LLC.
17998 * Originally Released Under LGPL - original licence link has changed is not relivant.
18001 * <script type="text/javascript">
18006 * @extends Roo.util.Observable
18007 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18008 * This class also supports single and multi selection modes. <br>
18009 * Create a data model bound view:
18011 var store = new Roo.data.Store(...);
18013 var view = new Roo.View({
18015 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18017 singleSelect: true,
18018 selectedClass: "ydataview-selected",
18022 // listen for node click?
18023 view.on("click", function(vw, index, node, e){
18024 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18028 dataModel.load("foobar.xml");
18030 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18032 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18033 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18035 * Note: old style constructor is still suported (container, template, config)
18038 * Create a new View
18039 * @param {Object} config The config object
18042 Roo.View = function(config, depreciated_tpl, depreciated_config){
18044 this.parent = false;
18046 if (typeof(depreciated_tpl) == 'undefined') {
18047 // new way.. - universal constructor.
18048 Roo.apply(this, config);
18049 this.el = Roo.get(this.el);
18052 this.el = Roo.get(config);
18053 this.tpl = depreciated_tpl;
18054 Roo.apply(this, depreciated_config);
18056 this.wrapEl = this.el.wrap().wrap();
18057 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060 if(typeof(this.tpl) == "string"){
18061 this.tpl = new Roo.Template(this.tpl);
18063 // support xtype ctors..
18064 this.tpl = new Roo.factory(this.tpl, Roo);
18068 this.tpl.compile();
18073 * @event beforeclick
18074 * Fires before a click is processed. Returns false to cancel the default action.
18075 * @param {Roo.View} this
18076 * @param {Number} index The index of the target node
18077 * @param {HTMLElement} node The target node
18078 * @param {Roo.EventObject} e The raw event object
18080 "beforeclick" : true,
18083 * Fires when a template node is clicked.
18084 * @param {Roo.View} this
18085 * @param {Number} index The index of the target node
18086 * @param {HTMLElement} node The target node
18087 * @param {Roo.EventObject} e The raw event object
18092 * Fires when a template node is double clicked.
18093 * @param {Roo.View} this
18094 * @param {Number} index The index of the target node
18095 * @param {HTMLElement} node The target node
18096 * @param {Roo.EventObject} e The raw event object
18100 * @event contextmenu
18101 * Fires when a template node is right clicked.
18102 * @param {Roo.View} this
18103 * @param {Number} index The index of the target node
18104 * @param {HTMLElement} node The target node
18105 * @param {Roo.EventObject} e The raw event object
18107 "contextmenu" : true,
18109 * @event selectionchange
18110 * Fires when the selected nodes change.
18111 * @param {Roo.View} this
18112 * @param {Array} selections Array of the selected nodes
18114 "selectionchange" : true,
18117 * @event beforeselect
18118 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18119 * @param {Roo.View} this
18120 * @param {HTMLElement} node The node to be selected
18121 * @param {Array} selections Array of currently selected nodes
18123 "beforeselect" : true,
18125 * @event preparedata
18126 * Fires on every row to render, to allow you to change the data.
18127 * @param {Roo.View} this
18128 * @param {Object} data to be rendered (change this)
18130 "preparedata" : true
18138 "click": this.onClick,
18139 "dblclick": this.onDblClick,
18140 "contextmenu": this.onContextMenu,
18144 this.selections = [];
18146 this.cmp = new Roo.CompositeElementLite([]);
18148 this.store = Roo.factory(this.store, Roo.data);
18149 this.setStore(this.store, true);
18152 if ( this.footer && this.footer.xtype) {
18154 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18156 this.footer.dataSource = this.store;
18157 this.footer.container = fctr;
18158 this.footer = Roo.factory(this.footer, Roo);
18159 fctr.insertFirst(this.el);
18161 // this is a bit insane - as the paging toolbar seems to detach the el..
18162 // dom.parentNode.parentNode.parentNode
18163 // they get detached?
18167 Roo.View.superclass.constructor.call(this);
18172 Roo.extend(Roo.View, Roo.util.Observable, {
18175 * @cfg {Roo.data.Store} store Data store to load data from.
18180 * @cfg {String|Roo.Element} el The container element.
18185 * @cfg {String|Roo.Template} tpl The template used by this View
18189 * @cfg {String} dataName the named area of the template to use as the data area
18190 * Works with domtemplates roo-name="name"
18194 * @cfg {String} selectedClass The css class to add to selected nodes
18196 selectedClass : "x-view-selected",
18198 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18203 * @cfg {String} text to display on mask (default Loading)
18207 * @cfg {Boolean} multiSelect Allow multiple selection
18209 multiSelect : false,
18211 * @cfg {Boolean} singleSelect Allow single selection
18213 singleSelect: false,
18216 * @cfg {Boolean} toggleSelect - selecting
18218 toggleSelect : false,
18221 * @cfg {Boolean} tickable - selecting
18226 * Returns the element this view is bound to.
18227 * @return {Roo.Element}
18229 getEl : function(){
18230 return this.wrapEl;
18236 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18238 refresh : function(){
18239 //Roo.log('refresh');
18242 // if we are using something like 'domtemplate', then
18243 // the what gets used is:
18244 // t.applySubtemplate(NAME, data, wrapping data..)
18245 // the outer template then get' applied with
18246 // the store 'extra data'
18247 // and the body get's added to the
18248 // roo-name="data" node?
18249 // <span class='roo-tpl-{name}'></span> ?????
18253 this.clearSelections();
18254 this.el.update("");
18256 var records = this.store.getRange();
18257 if(records.length < 1) {
18259 // is this valid?? = should it render a template??
18261 this.el.update(this.emptyText);
18265 if (this.dataName) {
18266 this.el.update(t.apply(this.store.meta)); //????
18267 el = this.el.child('.roo-tpl-' + this.dataName);
18270 for(var i = 0, len = records.length; i < len; i++){
18271 var data = this.prepareData(records[i].data, i, records[i]);
18272 this.fireEvent("preparedata", this, data, i, records[i]);
18274 var d = Roo.apply({}, data);
18277 Roo.apply(d, {'roo-id' : Roo.id()});
18281 Roo.each(this.parent.item, function(item){
18282 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285 Roo.apply(d, {'roo-data-checked' : 'checked'});
18289 html[html.length] = Roo.util.Format.trim(
18291 t.applySubtemplate(this.dataName, d, this.store.meta) :
18298 el.update(html.join(""));
18299 this.nodes = el.dom.childNodes;
18300 this.updateIndexes(0);
18305 * Function to override to reformat the data that is sent to
18306 * the template for each node.
18307 * DEPRICATED - use the preparedata event handler.
18308 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18309 * a JSON object for an UpdateManager bound view).
18311 prepareData : function(data, index, record)
18313 this.fireEvent("preparedata", this, data, index, record);
18317 onUpdate : function(ds, record){
18318 // Roo.log('on update');
18319 this.clearSelections();
18320 var index = this.store.indexOf(record);
18321 var n = this.nodes[index];
18322 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18323 n.parentNode.removeChild(n);
18324 this.updateIndexes(index, index);
18330 onAdd : function(ds, records, index)
18332 //Roo.log(['on Add', ds, records, index] );
18333 this.clearSelections();
18334 if(this.nodes.length == 0){
18338 var n = this.nodes[index];
18339 for(var i = 0, len = records.length; i < len; i++){
18340 var d = this.prepareData(records[i].data, i, records[i]);
18342 this.tpl.insertBefore(n, d);
18345 this.tpl.append(this.el, d);
18348 this.updateIndexes(index);
18351 onRemove : function(ds, record, index){
18352 // Roo.log('onRemove');
18353 this.clearSelections();
18354 var el = this.dataName ?
18355 this.el.child('.roo-tpl-' + this.dataName) :
18358 el.dom.removeChild(this.nodes[index]);
18359 this.updateIndexes(index);
18363 * Refresh an individual node.
18364 * @param {Number} index
18366 refreshNode : function(index){
18367 this.onUpdate(this.store, this.store.getAt(index));
18370 updateIndexes : function(startIndex, endIndex){
18371 var ns = this.nodes;
18372 startIndex = startIndex || 0;
18373 endIndex = endIndex || ns.length - 1;
18374 for(var i = startIndex; i <= endIndex; i++){
18375 ns[i].nodeIndex = i;
18380 * Changes the data store this view uses and refresh the view.
18381 * @param {Store} store
18383 setStore : function(store, initial){
18384 if(!initial && this.store){
18385 this.store.un("datachanged", this.refresh);
18386 this.store.un("add", this.onAdd);
18387 this.store.un("remove", this.onRemove);
18388 this.store.un("update", this.onUpdate);
18389 this.store.un("clear", this.refresh);
18390 this.store.un("beforeload", this.onBeforeLoad);
18391 this.store.un("load", this.onLoad);
18392 this.store.un("loadexception", this.onLoad);
18396 store.on("datachanged", this.refresh, this);
18397 store.on("add", this.onAdd, this);
18398 store.on("remove", this.onRemove, this);
18399 store.on("update", this.onUpdate, this);
18400 store.on("clear", this.refresh, this);
18401 store.on("beforeload", this.onBeforeLoad, this);
18402 store.on("load", this.onLoad, this);
18403 store.on("loadexception", this.onLoad, this);
18411 * onbeforeLoad - masks the loading area.
18414 onBeforeLoad : function(store,opts)
18416 //Roo.log('onBeforeLoad');
18418 this.el.update("");
18420 this.el.mask(this.mask ? this.mask : "Loading" );
18422 onLoad : function ()
18429 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18430 * @param {HTMLElement} node
18431 * @return {HTMLElement} The template node
18433 findItemFromChild : function(node){
18434 var el = this.dataName ?
18435 this.el.child('.roo-tpl-' + this.dataName,true) :
18438 if(!node || node.parentNode == el){
18441 var p = node.parentNode;
18442 while(p && p != el){
18443 if(p.parentNode == el){
18452 onClick : function(e){
18453 var item = this.findItemFromChild(e.getTarget());
18455 var index = this.indexOf(item);
18456 if(this.onItemClick(item, index, e) !== false){
18457 this.fireEvent("click", this, index, item, e);
18460 this.clearSelections();
18465 onContextMenu : function(e){
18466 var item = this.findItemFromChild(e.getTarget());
18468 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18473 onDblClick : function(e){
18474 var item = this.findItemFromChild(e.getTarget());
18476 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18480 onItemClick : function(item, index, e)
18482 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485 if (this.toggleSelect) {
18486 var m = this.isSelected(item) ? 'unselect' : 'select';
18489 _t[m](item, true, false);
18492 if(this.multiSelect || this.singleSelect){
18493 if(this.multiSelect && e.shiftKey && this.lastSelection){
18494 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18496 this.select(item, this.multiSelect && e.ctrlKey);
18497 this.lastSelection = item;
18500 if(!this.tickable){
18501 e.preventDefault();
18509 * Get the number of selected nodes.
18512 getSelectionCount : function(){
18513 return this.selections.length;
18517 * Get the currently selected nodes.
18518 * @return {Array} An array of HTMLElements
18520 getSelectedNodes : function(){
18521 return this.selections;
18525 * Get the indexes of the selected nodes.
18528 getSelectedIndexes : function(){
18529 var indexes = [], s = this.selections;
18530 for(var i = 0, len = s.length; i < len; i++){
18531 indexes.push(s[i].nodeIndex);
18537 * Clear all selections
18538 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18540 clearSelections : function(suppressEvent){
18541 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18542 this.cmp.elements = this.selections;
18543 this.cmp.removeClass(this.selectedClass);
18544 this.selections = [];
18545 if(!suppressEvent){
18546 this.fireEvent("selectionchange", this, this.selections);
18552 * Returns true if the passed node is selected
18553 * @param {HTMLElement/Number} node The node or node index
18554 * @return {Boolean}
18556 isSelected : function(node){
18557 var s = this.selections;
18561 node = this.getNode(node);
18562 return s.indexOf(node) !== -1;
18567 * @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
18568 * @param {Boolean} keepExisting (optional) true to keep existing selections
18569 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18571 select : function(nodeInfo, keepExisting, suppressEvent){
18572 if(nodeInfo instanceof Array){
18574 this.clearSelections(true);
18576 for(var i = 0, len = nodeInfo.length; i < len; i++){
18577 this.select(nodeInfo[i], true, true);
18581 var node = this.getNode(nodeInfo);
18582 if(!node || this.isSelected(node)){
18583 return; // already selected.
18586 this.clearSelections(true);
18589 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18590 Roo.fly(node).addClass(this.selectedClass);
18591 this.selections.push(node);
18592 if(!suppressEvent){
18593 this.fireEvent("selectionchange", this, this.selections);
18601 * @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
18602 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18603 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18605 unselect : function(nodeInfo, keepExisting, suppressEvent)
18607 if(nodeInfo instanceof Array){
18608 Roo.each(this.selections, function(s) {
18609 this.unselect(s, nodeInfo);
18613 var node = this.getNode(nodeInfo);
18614 if(!node || !this.isSelected(node)){
18615 //Roo.log("not selected");
18616 return; // not selected.
18620 Roo.each(this.selections, function(s) {
18622 Roo.fly(node).removeClass(this.selectedClass);
18629 this.selections= ns;
18630 this.fireEvent("selectionchange", this, this.selections);
18634 * Gets a template node.
18635 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18636 * @return {HTMLElement} The node or null if it wasn't found
18638 getNode : function(nodeInfo){
18639 if(typeof nodeInfo == "string"){
18640 return document.getElementById(nodeInfo);
18641 }else if(typeof nodeInfo == "number"){
18642 return this.nodes[nodeInfo];
18648 * Gets a range template nodes.
18649 * @param {Number} startIndex
18650 * @param {Number} endIndex
18651 * @return {Array} An array of nodes
18653 getNodes : function(start, end){
18654 var ns = this.nodes;
18655 start = start || 0;
18656 end = typeof end == "undefined" ? ns.length - 1 : end;
18659 for(var i = start; i <= end; i++){
18663 for(var i = start; i >= end; i--){
18671 * Finds the index of the passed node
18672 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18673 * @return {Number} The index of the node or -1
18675 indexOf : function(node){
18676 node = this.getNode(node);
18677 if(typeof node.nodeIndex == "number"){
18678 return node.nodeIndex;
18680 var ns = this.nodes;
18681 for(var i = 0, len = ns.length; i < len; i++){
18692 * based on jquery fullcalendar
18696 Roo.bootstrap = Roo.bootstrap || {};
18698 * @class Roo.bootstrap.Calendar
18699 * @extends Roo.bootstrap.Component
18700 * Bootstrap Calendar class
18701 * @cfg {Boolean} loadMask (true|false) default false
18702 * @cfg {Object} header generate the user specific header of the calendar, default false
18705 * Create a new Container
18706 * @param {Object} config The config object
18711 Roo.bootstrap.Calendar = function(config){
18712 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18716 * Fires when a date is selected
18717 * @param {DatePicker} this
18718 * @param {Date} date The selected date
18722 * @event monthchange
18723 * Fires when the displayed month changes
18724 * @param {DatePicker} this
18725 * @param {Date} date The selected month
18727 'monthchange': true,
18729 * @event evententer
18730 * Fires when mouse over an event
18731 * @param {Calendar} this
18732 * @param {event} Event
18734 'evententer': true,
18736 * @event eventleave
18737 * Fires when the mouse leaves an
18738 * @param {Calendar} this
18741 'eventleave': true,
18743 * @event eventclick
18744 * Fires when the mouse click an
18745 * @param {Calendar} this
18754 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18757 * @cfg {Number} startDay
18758 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18766 getAutoCreate : function(){
18769 var fc_button = function(name, corner, style, content ) {
18770 return Roo.apply({},{
18772 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18774 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18788 style : 'width:100%',
18795 cls : 'fc-header-left',
18797 fc_button('prev', 'left', 'arrow', '‹' ),
18798 fc_button('next', 'right', 'arrow', '›' ),
18799 { tag: 'span', cls: 'fc-header-space' },
18800 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18808 cls : 'fc-header-center',
18812 cls: 'fc-header-title',
18815 html : 'month / year'
18823 cls : 'fc-header-right',
18825 /* fc_button('month', 'left', '', 'month' ),
18826 fc_button('week', '', '', 'week' ),
18827 fc_button('day', 'right', '', 'day' )
18839 header = this.header;
18842 var cal_heads = function() {
18844 // fixme - handle this.
18846 for (var i =0; i < Date.dayNames.length; i++) {
18847 var d = Date.dayNames[i];
18850 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18851 html : d.substring(0,3)
18855 ret[0].cls += ' fc-first';
18856 ret[6].cls += ' fc-last';
18859 var cal_cell = function(n) {
18862 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18867 cls: 'fc-day-number',
18871 cls: 'fc-day-content',
18875 style: 'position: relative;' // height: 17px;
18887 var cal_rows = function() {
18890 for (var r = 0; r < 6; r++) {
18897 for (var i =0; i < Date.dayNames.length; i++) {
18898 var d = Date.dayNames[i];
18899 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902 row.cn[0].cls+=' fc-first';
18903 row.cn[0].cn[0].style = 'min-height:90px';
18904 row.cn[6].cls+=' fc-last';
18908 ret[0].cls += ' fc-first';
18909 ret[4].cls += ' fc-prev-last';
18910 ret[5].cls += ' fc-last';
18917 cls: 'fc-border-separate',
18918 style : 'width:100%',
18926 cls : 'fc-first fc-last',
18944 cls : 'fc-content',
18945 style : "position: relative;",
18948 cls : 'fc-view fc-view-month fc-grid',
18949 style : 'position: relative',
18950 unselectable : 'on',
18953 cls : 'fc-event-container',
18954 style : 'position:absolute;z-index:8;top:0;left:0;'
18972 initEvents : function()
18975 throw "can not find store for calendar";
18981 style: "text-align:center",
18985 style: "background-color:white;width:50%;margin:250 auto",
18989 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19000 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19002 var size = this.el.select('.fc-content', true).first().getSize();
19003 this.maskEl.setSize(size.width, size.height);
19004 this.maskEl.enableDisplayMode("block");
19005 if(!this.loadMask){
19006 this.maskEl.hide();
19009 this.store = Roo.factory(this.store, Roo.data);
19010 this.store.on('load', this.onLoad, this);
19011 this.store.on('beforeload', this.onBeforeLoad, this);
19015 this.cells = this.el.select('.fc-day',true);
19016 //Roo.log(this.cells);
19017 this.textNodes = this.el.query('.fc-day-number');
19018 this.cells.addClassOnOver('fc-state-hover');
19020 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19021 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19022 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19023 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19025 this.on('monthchange', this.onMonthChange, this);
19027 this.update(new Date().clearTime());
19030 resize : function() {
19031 var sz = this.el.getSize();
19033 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19034 this.el.select('.fc-day-content div',true).setHeight(34);
19039 showPrevMonth : function(e){
19040 this.update(this.activeDate.add("mo", -1));
19042 showToday : function(e){
19043 this.update(new Date().clearTime());
19046 showNextMonth : function(e){
19047 this.update(this.activeDate.add("mo", 1));
19051 showPrevYear : function(){
19052 this.update(this.activeDate.add("y", -1));
19056 showNextYear : function(){
19057 this.update(this.activeDate.add("y", 1));
19062 update : function(date)
19064 var vd = this.activeDate;
19065 this.activeDate = date;
19066 // if(vd && this.el){
19067 // var t = date.getTime();
19068 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19069 // Roo.log('using add remove');
19071 // this.fireEvent('monthchange', this, date);
19073 // this.cells.removeClass("fc-state-highlight");
19074 // this.cells.each(function(c){
19075 // if(c.dateValue == t){
19076 // c.addClass("fc-state-highlight");
19077 // setTimeout(function(){
19078 // try{c.dom.firstChild.focus();}catch(e){}
19088 var days = date.getDaysInMonth();
19090 var firstOfMonth = date.getFirstDateOfMonth();
19091 var startingPos = firstOfMonth.getDay()-this.startDay;
19093 if(startingPos < this.startDay){
19097 var pm = date.add(Date.MONTH, -1);
19098 var prevStart = pm.getDaysInMonth()-startingPos;
19100 this.cells = this.el.select('.fc-day',true);
19101 this.textNodes = this.el.query('.fc-day-number');
19102 this.cells.addClassOnOver('fc-state-hover');
19104 var cells = this.cells.elements;
19105 var textEls = this.textNodes;
19107 Roo.each(cells, function(cell){
19108 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111 days += startingPos;
19113 // convert everything to numbers so it's fast
19114 var day = 86400000;
19115 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118 //Roo.log(prevStart);
19120 var today = new Date().clearTime().getTime();
19121 var sel = date.clearTime().getTime();
19122 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19123 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19124 var ddMatch = this.disabledDatesRE;
19125 var ddText = this.disabledDatesText;
19126 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19127 var ddaysText = this.disabledDaysText;
19128 var format = this.format;
19130 var setCellClass = function(cal, cell){
19134 //Roo.log('set Cell Class');
19136 var t = d.getTime();
19140 cell.dateValue = t;
19142 cell.className += " fc-today";
19143 cell.className += " fc-state-highlight";
19144 cell.title = cal.todayText;
19147 // disable highlight in other month..
19148 //cell.className += " fc-state-highlight";
19153 cell.className = " fc-state-disabled";
19154 cell.title = cal.minText;
19158 cell.className = " fc-state-disabled";
19159 cell.title = cal.maxText;
19163 if(ddays.indexOf(d.getDay()) != -1){
19164 cell.title = ddaysText;
19165 cell.className = " fc-state-disabled";
19168 if(ddMatch && format){
19169 var fvalue = d.dateFormat(format);
19170 if(ddMatch.test(fvalue)){
19171 cell.title = ddText.replace("%0", fvalue);
19172 cell.className = " fc-state-disabled";
19176 if (!cell.initialClassName) {
19177 cell.initialClassName = cell.dom.className;
19180 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19185 for(; i < startingPos; i++) {
19186 textEls[i].innerHTML = (++prevStart);
19187 d.setDate(d.getDate()+1);
19189 cells[i].className = "fc-past fc-other-month";
19190 setCellClass(this, cells[i]);
19195 for(; i < days; i++){
19196 intDay = i - startingPos + 1;
19197 textEls[i].innerHTML = (intDay);
19198 d.setDate(d.getDate()+1);
19200 cells[i].className = ''; // "x-date-active";
19201 setCellClass(this, cells[i]);
19205 for(; i < 42; i++) {
19206 textEls[i].innerHTML = (++extraDays);
19207 d.setDate(d.getDate()+1);
19209 cells[i].className = "fc-future fc-other-month";
19210 setCellClass(this, cells[i]);
19213 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19215 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19217 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19218 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19220 if(totalRows != 6){
19221 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19222 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225 this.fireEvent('monthchange', this, date);
19229 if(!this.internalRender){
19230 var main = this.el.dom.firstChild;
19231 var w = main.offsetWidth;
19232 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19233 Roo.fly(main).setWidth(w);
19234 this.internalRender = true;
19235 // opera does not respect the auto grow header center column
19236 // then, after it gets a width opera refuses to recalculate
19237 // without a second pass
19238 if(Roo.isOpera && !this.secondPass){
19239 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19240 this.secondPass = true;
19241 this.update.defer(10, this, [date]);
19248 findCell : function(dt) {
19249 dt = dt.clearTime().getTime();
19251 this.cells.each(function(c){
19252 //Roo.log("check " +c.dateValue + '?=' + dt);
19253 if(c.dateValue == dt){
19263 findCells : function(ev) {
19264 var s = ev.start.clone().clearTime().getTime();
19266 var e= ev.end.clone().clearTime().getTime();
19269 this.cells.each(function(c){
19270 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19272 if(c.dateValue > e){
19275 if(c.dateValue < s){
19284 // findBestRow: function(cells)
19288 // for (var i =0 ; i < cells.length;i++) {
19289 // ret = Math.max(cells[i].rows || 0,ret);
19296 addItem : function(ev)
19298 // look for vertical location slot in
19299 var cells = this.findCells(ev);
19301 // ev.row = this.findBestRow(cells);
19303 // work out the location.
19307 for(var i =0; i < cells.length; i++) {
19309 cells[i].row = cells[0].row;
19312 cells[i].row = cells[i].row + 1;
19322 if (crow.start.getY() == cells[i].getY()) {
19324 crow.end = cells[i];
19341 cells[0].events.push(ev);
19343 this.calevents.push(ev);
19346 clearEvents: function() {
19348 if(!this.calevents){
19352 Roo.each(this.cells.elements, function(c){
19358 Roo.each(this.calevents, function(e) {
19359 Roo.each(e.els, function(el) {
19360 el.un('mouseenter' ,this.onEventEnter, this);
19361 el.un('mouseleave' ,this.onEventLeave, this);
19366 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19372 renderEvents: function()
19376 this.cells.each(function(c) {
19385 if(c.row != c.events.length){
19386 r = 4 - (4 - (c.row - c.events.length));
19389 c.events = ev.slice(0, r);
19390 c.more = ev.slice(r);
19392 if(c.more.length && c.more.length == 1){
19393 c.events.push(c.more.pop());
19396 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19400 this.cells.each(function(c) {
19402 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405 for (var e = 0; e < c.events.length; e++){
19406 var ev = c.events[e];
19407 var rows = ev.rows;
19409 for(var i = 0; i < rows.length; i++) {
19411 // how many rows should it span..
19414 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19415 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19417 unselectable : "on",
19420 cls: 'fc-event-inner',
19424 // cls: 'fc-event-time',
19425 // html : cells.length > 1 ? '' : ev.time
19429 cls: 'fc-event-title',
19430 html : String.format('{0}', ev.title)
19437 cls: 'ui-resizable-handle ui-resizable-e',
19438 html : '  '
19445 cfg.cls += ' fc-event-start';
19447 if ((i+1) == rows.length) {
19448 cfg.cls += ' fc-event-end';
19451 var ctr = _this.el.select('.fc-event-container',true).first();
19452 var cg = ctr.createChild(cfg);
19454 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19455 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19457 var r = (c.more.length) ? 1 : 0;
19458 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19459 cg.setWidth(ebox.right - sbox.x -2);
19461 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19462 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19463 cg.on('click', _this.onEventClick, _this, ev);
19474 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19475 style : 'position: absolute',
19476 unselectable : "on",
19479 cls: 'fc-event-inner',
19483 cls: 'fc-event-title',
19491 cls: 'ui-resizable-handle ui-resizable-e',
19492 html : '  '
19498 var ctr = _this.el.select('.fc-event-container',true).first();
19499 var cg = ctr.createChild(cfg);
19501 var sbox = c.select('.fc-day-content',true).first().getBox();
19502 var ebox = c.select('.fc-day-content',true).first().getBox();
19504 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19505 cg.setWidth(ebox.right - sbox.x -2);
19507 cg.on('click', _this.onMoreEventClick, _this, c.more);
19517 onEventEnter: function (e, el,event,d) {
19518 this.fireEvent('evententer', this, el, event);
19521 onEventLeave: function (e, el,event,d) {
19522 this.fireEvent('eventleave', this, el, event);
19525 onEventClick: function (e, el,event,d) {
19526 this.fireEvent('eventclick', this, el, event);
19529 onMonthChange: function () {
19533 onMoreEventClick: function(e, el, more)
19537 this.calpopover.placement = 'right';
19538 this.calpopover.setTitle('More');
19540 this.calpopover.setContent('');
19542 var ctr = this.calpopover.el.select('.popover-content', true).first();
19544 Roo.each(more, function(m){
19546 cls : 'fc-event-hori fc-event-draggable',
19549 var cg = ctr.createChild(cfg);
19551 cg.on('click', _this.onEventClick, _this, m);
19554 this.calpopover.show(el);
19559 onLoad: function ()
19561 this.calevents = [];
19564 if(this.store.getCount() > 0){
19565 this.store.data.each(function(d){
19568 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19569 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19570 time : d.data.start_time,
19571 title : d.data.title,
19572 description : d.data.description,
19573 venue : d.data.venue
19578 this.renderEvents();
19580 if(this.calevents.length && this.loadMask){
19581 this.maskEl.hide();
19585 onBeforeLoad: function()
19587 this.clearEvents();
19589 this.maskEl.show();
19603 * @class Roo.bootstrap.Popover
19604 * @extends Roo.bootstrap.Component
19605 * Bootstrap Popover class
19606 * @cfg {String} html contents of the popover (or false to use children..)
19607 * @cfg {String} title of popover (or false to hide)
19608 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19609 * @cfg {String} trigger click || hover (or false to trigger manually)
19610 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19611 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19612 * - if false and it has a 'parent' then it will be automatically added to that element
19613 * - if string - Roo.get will be called
19614 * @cfg {Number} delay - delay before showing
19617 * Create a new Popover
19618 * @param {Object} config The config object
19621 Roo.bootstrap.Popover = function(config){
19622 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19628 * After the popover show
19630 * @param {Roo.bootstrap.Popover} this
19635 * After the popover hide
19637 * @param {Roo.bootstrap.Popover} this
19643 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19648 placement : 'right',
19649 trigger : 'hover', // hover
19655 can_build_overlaid : false,
19657 maskEl : false, // the mask element
19662 getChildContainer : function()
19664 return this.contentEl;
19667 getPopoverHeader : function()
19669 this.title = true; // flag not to hide it..
19670 return this.headerEl
19674 getAutoCreate : function(){
19677 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19678 style: 'display:block',
19684 cls : 'popover-inner ',
19688 cls: 'popover-title popover-header',
19689 html : this.title === false ? '' : this.title
19692 cls : 'popover-content popover-body ' + (this.cls || ''),
19693 html : this.html || ''
19704 * @param {string} the title
19706 setTitle: function(str)
19710 this.headerEl.dom.innerHTML = str;
19715 * @param {string} the body content
19717 setContent: function(str)
19720 if (this.contentEl) {
19721 this.contentEl.dom.innerHTML = str;
19725 // as it get's added to the bottom of the page.
19726 onRender : function(ct, position)
19728 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19733 var cfg = Roo.apply({}, this.getAutoCreate());
19737 cfg.cls += ' ' + this.cls;
19740 cfg.style = this.style;
19742 //Roo.log("adding to ");
19743 this.el = Roo.get(document.body).createChild(cfg, position);
19744 // Roo.log(this.el);
19747 this.contentEl = this.el.select('.popover-content',true).first();
19748 this.headerEl = this.el.select('.popover-title',true).first();
19751 if(typeof(this.items) != 'undefined'){
19752 var items = this.items;
19755 for(var i =0;i < items.length;i++) {
19756 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760 this.items = nitems;
19762 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19763 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19770 resizeMask : function()
19772 this.maskEl.setSize(
19773 Roo.lib.Dom.getViewWidth(true),
19774 Roo.lib.Dom.getViewHeight(true)
19778 initEvents : function()
19782 Roo.bootstrap.Popover.register(this);
19786 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19787 this.el.enableDisplayMode('block');
19789 if (this.over === false && !this.parent()) {
19792 if (this.triggers === false) {
19797 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19798 var triggers = this.trigger ? this.trigger.split(' ') : [];
19799 Roo.each(triggers, function(trigger) {
19801 if (trigger == 'click') {
19802 on_el.on('click', this.toggle, this);
19803 } else if (trigger != 'manual') {
19804 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19805 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19807 on_el.on(eventIn ,this.enter, this);
19808 on_el.on(eventOut, this.leave, this);
19819 toggle : function () {
19820 this.hoverState == 'in' ? this.leave() : this.enter();
19823 enter : function () {
19825 clearTimeout(this.timeout);
19827 this.hoverState = 'in';
19829 if (!this.delay || !this.delay.show) {
19834 this.timeout = setTimeout(function () {
19835 if (_t.hoverState == 'in') {
19838 }, this.delay.show)
19841 leave : function() {
19842 clearTimeout(this.timeout);
19844 this.hoverState = 'out';
19846 if (!this.delay || !this.delay.hide) {
19851 this.timeout = setTimeout(function () {
19852 if (_t.hoverState == 'out') {
19855 }, this.delay.hide)
19859 * @param {Roo.Element|string|false} - element to align and point to.
19861 show : function (on_el)
19864 on_el = on_el || false; // default to false
19866 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19867 on_el = this.parent().el;
19868 } else if (this.over) {
19869 Roo.get(this.over);
19875 this.render(document.body);
19879 this.el.removeClass([
19880 'fade','top','bottom', 'left', 'right','in',
19881 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19884 if (this.title === false) {
19885 this.headerEl.hide();
19889 var placement = typeof this.placement == 'function' ?
19890 this.placement.call(this, this.el, on_el) :
19894 var autoToken = /\s?auto?\s?/i; /// not sure how this was supposed to work? right auto ? what?
19896 // I think 'auto right' - but
19898 var autoPlace = autoToken.test(placement);
19900 placement = placement.replace(autoToken, '') || 'top';
19906 this.el.dom.style.display='block';
19908 //this.el.appendTo(on_el);
19910 var p = this.getPosition();
19911 var box = this.el.getBox();
19914 var align = Roo.bootstrap.Popover.alignment[placement];
19915 this.el.addClass(align[2]);
19920 this.el.alignTo(on_el, align[0],align[1]);
19922 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19923 var es = this.el.getSize();
19924 var x = Roo.lib.Dom.getViewWidth()/2;
19925 var y = Roo.lib.Dom.getViewHeight()/2;
19926 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19931 //var arrow = this.el.select('.arrow',true).first();
19932 //arrow.set(align[2],
19934 this.el.addClass('in');
19937 if (this.el.hasClass('fade')) {
19941 this.hoverState = 'in';
19944 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19945 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19946 this.maskEl.dom.style.display = 'block';
19947 this.maskEl.addClass('show');
19949 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19953 this.fireEvent('show', this);
19958 this.el.setXY([0,0]);
19959 this.el.removeClass('in');
19961 this.hoverState = null;
19962 this.maskEl.hide(); // always..
19963 this.fireEvent('hide', this);
19969 Roo.apply(Roo.bootstrap.Popover, {
19972 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19973 'right' : ['l-br', [10,0], 'right bs-popover-right'],
19974 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19975 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19980 clickHander : false,
19983 onMouseDown : function(e)
19985 if (!e.getTarget(".roo-popover")) {
19993 register : function(popup)
19995 if (!Roo.bootstrap.Popover.clickHandler) {
19996 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19998 // hide other popups.
20000 this.popups.push(popup);
20002 hideAll : function()
20004 this.popups.forEach(function(p) {
20012 * Card header - holder for the card header elements.
20017 * @class Roo.bootstrap.PopoverNav
20018 * @extends Roo.bootstrap.NavGroup
20019 * Bootstrap Popover header navigation class
20021 * Create a new Popover Header Navigation
20022 * @param {Object} config The config object
20025 Roo.bootstrap.PopoverNav = function(config){
20026 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20029 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavGroup, {
20032 container_method : 'getPopoverHeader'
20050 * @class Roo.bootstrap.Progress
20051 * @extends Roo.bootstrap.Component
20052 * Bootstrap Progress class
20053 * @cfg {Boolean} striped striped of the progress bar
20054 * @cfg {Boolean} active animated of the progress bar
20058 * Create a new Progress
20059 * @param {Object} config The config object
20062 Roo.bootstrap.Progress = function(config){
20063 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20066 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20071 getAutoCreate : function(){
20079 cfg.cls += ' progress-striped';
20083 cfg.cls += ' active';
20102 * @class Roo.bootstrap.ProgressBar
20103 * @extends Roo.bootstrap.Component
20104 * Bootstrap ProgressBar class
20105 * @cfg {Number} aria_valuenow aria-value now
20106 * @cfg {Number} aria_valuemin aria-value min
20107 * @cfg {Number} aria_valuemax aria-value max
20108 * @cfg {String} label label for the progress bar
20109 * @cfg {String} panel (success | info | warning | danger )
20110 * @cfg {String} role role of the progress bar
20111 * @cfg {String} sr_only text
20115 * Create a new ProgressBar
20116 * @param {Object} config The config object
20119 Roo.bootstrap.ProgressBar = function(config){
20120 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20123 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20127 aria_valuemax : 100,
20133 getAutoCreate : function()
20138 cls: 'progress-bar',
20139 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20151 cfg.role = this.role;
20154 if(this.aria_valuenow){
20155 cfg['aria-valuenow'] = this.aria_valuenow;
20158 if(this.aria_valuemin){
20159 cfg['aria-valuemin'] = this.aria_valuemin;
20162 if(this.aria_valuemax){
20163 cfg['aria-valuemax'] = this.aria_valuemax;
20166 if(this.label && !this.sr_only){
20167 cfg.html = this.label;
20171 cfg.cls += ' progress-bar-' + this.panel;
20177 update : function(aria_valuenow)
20179 this.aria_valuenow = aria_valuenow;
20181 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20196 * @class Roo.bootstrap.TabGroup
20197 * @extends Roo.bootstrap.Column
20198 * Bootstrap Column class
20199 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20200 * @cfg {Boolean} carousel true to make the group behave like a carousel
20201 * @cfg {Boolean} bullets show bullets for the panels
20202 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20203 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20204 * @cfg {Boolean} showarrow (true|false) show arrow default true
20207 * Create a new TabGroup
20208 * @param {Object} config The config object
20211 Roo.bootstrap.TabGroup = function(config){
20212 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20214 this.navId = Roo.id();
20217 Roo.bootstrap.TabGroup.register(this);
20221 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20224 transition : false,
20229 slideOnTouch : false,
20232 getAutoCreate : function()
20234 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20236 cfg.cls += ' tab-content';
20238 if (this.carousel) {
20239 cfg.cls += ' carousel slide';
20242 cls : 'carousel-inner',
20246 if(this.bullets && !Roo.isTouch){
20249 cls : 'carousel-bullets',
20253 if(this.bullets_cls){
20254 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20261 cfg.cn[0].cn.push(bullets);
20264 if(this.showarrow){
20265 cfg.cn[0].cn.push({
20267 class : 'carousel-arrow',
20271 class : 'carousel-prev',
20275 class : 'fa fa-chevron-left'
20281 class : 'carousel-next',
20285 class : 'fa fa-chevron-right'
20298 initEvents: function()
20300 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20301 // this.el.on("touchstart", this.onTouchStart, this);
20304 if(this.autoslide){
20307 this.slideFn = window.setInterval(function() {
20308 _this.showPanelNext();
20312 if(this.showarrow){
20313 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20314 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20320 // onTouchStart : function(e, el, o)
20322 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20326 // this.showPanelNext();
20330 getChildContainer : function()
20332 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20336 * register a Navigation item
20337 * @param {Roo.bootstrap.NavItem} the navitem to add
20339 register : function(item)
20341 this.tabs.push( item);
20342 item.navId = this.navId; // not really needed..
20347 getActivePanel : function()
20350 Roo.each(this.tabs, function(t) {
20360 getPanelByName : function(n)
20363 Roo.each(this.tabs, function(t) {
20364 if (t.tabId == n) {
20372 indexOfPanel : function(p)
20375 Roo.each(this.tabs, function(t,i) {
20376 if (t.tabId == p.tabId) {
20385 * show a specific panel
20386 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20387 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20389 showPanel : function (pan)
20391 if(this.transition || typeof(pan) == 'undefined'){
20392 Roo.log("waiting for the transitionend");
20396 if (typeof(pan) == 'number') {
20397 pan = this.tabs[pan];
20400 if (typeof(pan) == 'string') {
20401 pan = this.getPanelByName(pan);
20404 var cur = this.getActivePanel();
20407 Roo.log('pan or acitve pan is undefined');
20411 if (pan.tabId == this.getActivePanel().tabId) {
20415 if (false === cur.fireEvent('beforedeactivate')) {
20419 if(this.bullets > 0 && !Roo.isTouch){
20420 this.setActiveBullet(this.indexOfPanel(pan));
20423 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20425 //class="carousel-item carousel-item-next carousel-item-left"
20427 this.transition = true;
20428 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20429 var lr = dir == 'next' ? 'left' : 'right';
20430 pan.el.addClass(dir); // or prev
20431 pan.el.addClass('carousel-item-' + dir); // or prev
20432 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20433 cur.el.addClass(lr); // or right
20434 pan.el.addClass(lr);
20435 cur.el.addClass('carousel-item-' +lr); // or right
20436 pan.el.addClass('carousel-item-' +lr);
20440 cur.el.on('transitionend', function() {
20441 Roo.log("trans end?");
20443 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20444 pan.setActive(true);
20446 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20447 cur.setActive(false);
20449 _this.transition = false;
20451 }, this, { single: true } );
20456 cur.setActive(false);
20457 pan.setActive(true);
20462 showPanelNext : function()
20464 var i = this.indexOfPanel(this.getActivePanel());
20466 if (i >= this.tabs.length - 1 && !this.autoslide) {
20470 if (i >= this.tabs.length - 1 && this.autoslide) {
20474 this.showPanel(this.tabs[i+1]);
20477 showPanelPrev : function()
20479 var i = this.indexOfPanel(this.getActivePanel());
20481 if (i < 1 && !this.autoslide) {
20485 if (i < 1 && this.autoslide) {
20486 i = this.tabs.length;
20489 this.showPanel(this.tabs[i-1]);
20493 addBullet: function()
20495 if(!this.bullets || Roo.isTouch){
20498 var ctr = this.el.select('.carousel-bullets',true).first();
20499 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20500 var bullet = ctr.createChild({
20501 cls : 'bullet bullet-' + i
20502 },ctr.dom.lastChild);
20507 bullet.on('click', (function(e, el, o, ii, t){
20509 e.preventDefault();
20511 this.showPanel(ii);
20513 if(this.autoslide && this.slideFn){
20514 clearInterval(this.slideFn);
20515 this.slideFn = window.setInterval(function() {
20516 _this.showPanelNext();
20520 }).createDelegate(this, [i, bullet], true));
20525 setActiveBullet : function(i)
20531 Roo.each(this.el.select('.bullet', true).elements, function(el){
20532 el.removeClass('selected');
20535 var bullet = this.el.select('.bullet-' + i, true).first();
20541 bullet.addClass('selected');
20552 Roo.apply(Roo.bootstrap.TabGroup, {
20556 * register a Navigation Group
20557 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20559 register : function(navgrp)
20561 this.groups[navgrp.navId] = navgrp;
20565 * fetch a Navigation Group based on the navigation ID
20566 * if one does not exist , it will get created.
20567 * @param {string} the navgroup to add
20568 * @returns {Roo.bootstrap.NavGroup} the navgroup
20570 get: function(navId) {
20571 if (typeof(this.groups[navId]) == 'undefined') {
20572 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20574 return this.groups[navId] ;
20589 * @class Roo.bootstrap.TabPanel
20590 * @extends Roo.bootstrap.Component
20591 * Bootstrap TabPanel class
20592 * @cfg {Boolean} active panel active
20593 * @cfg {String} html panel content
20594 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20595 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20596 * @cfg {String} href click to link..
20597 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20601 * Create a new TabPanel
20602 * @param {Object} config The config object
20605 Roo.bootstrap.TabPanel = function(config){
20606 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20610 * Fires when the active status changes
20611 * @param {Roo.bootstrap.TabPanel} this
20612 * @param {Boolean} state the new state
20617 * @event beforedeactivate
20618 * Fires before a tab is de-activated - can be used to do validation on a form.
20619 * @param {Roo.bootstrap.TabPanel} this
20620 * @return {Boolean} false if there is an error
20623 'beforedeactivate': true
20626 this.tabId = this.tabId || Roo.id();
20630 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20637 touchSlide : false,
20638 getAutoCreate : function(){
20643 // item is needed for carousel - not sure if it has any effect otherwise
20644 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20645 html: this.html || ''
20649 cfg.cls += ' active';
20653 cfg.tabId = this.tabId;
20661 initEvents: function()
20663 var p = this.parent();
20665 this.navId = this.navId || p.navId;
20667 if (typeof(this.navId) != 'undefined') {
20668 // not really needed.. but just in case.. parent should be a NavGroup.
20669 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20673 var i = tg.tabs.length - 1;
20675 if(this.active && tg.bullets > 0 && i < tg.bullets){
20676 tg.setActiveBullet(i);
20680 this.el.on('click', this.onClick, this);
20682 if(Roo.isTouch && this.touchSlide){
20683 this.el.on("touchstart", this.onTouchStart, this);
20684 this.el.on("touchmove", this.onTouchMove, this);
20685 this.el.on("touchend", this.onTouchEnd, this);
20690 onRender : function(ct, position)
20692 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20695 setActive : function(state)
20697 Roo.log("panel - set active " + this.tabId + "=" + state);
20699 this.active = state;
20701 this.el.removeClass('active');
20703 } else if (!this.el.hasClass('active')) {
20704 this.el.addClass('active');
20707 this.fireEvent('changed', this, state);
20710 onClick : function(e)
20712 e.preventDefault();
20714 if(!this.href.length){
20718 window.location.href = this.href;
20727 onTouchStart : function(e)
20729 this.swiping = false;
20731 this.startX = e.browserEvent.touches[0].clientX;
20732 this.startY = e.browserEvent.touches[0].clientY;
20735 onTouchMove : function(e)
20737 this.swiping = true;
20739 this.endX = e.browserEvent.touches[0].clientX;
20740 this.endY = e.browserEvent.touches[0].clientY;
20743 onTouchEnd : function(e)
20750 var tabGroup = this.parent();
20752 if(this.endX > this.startX){ // swiping right
20753 tabGroup.showPanelPrev();
20757 if(this.startX > this.endX){ // swiping left
20758 tabGroup.showPanelNext();
20777 * @class Roo.bootstrap.DateField
20778 * @extends Roo.bootstrap.Input
20779 * Bootstrap DateField class
20780 * @cfg {Number} weekStart default 0
20781 * @cfg {String} viewMode default empty, (months|years)
20782 * @cfg {String} minViewMode default empty, (months|years)
20783 * @cfg {Number} startDate default -Infinity
20784 * @cfg {Number} endDate default Infinity
20785 * @cfg {Boolean} todayHighlight default false
20786 * @cfg {Boolean} todayBtn default false
20787 * @cfg {Boolean} calendarWeeks default false
20788 * @cfg {Object} daysOfWeekDisabled default empty
20789 * @cfg {Boolean} singleMode default false (true | false)
20791 * @cfg {Boolean} keyboardNavigation default true
20792 * @cfg {String} language default en
20795 * Create a new DateField
20796 * @param {Object} config The config object
20799 Roo.bootstrap.DateField = function(config){
20800 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20804 * Fires when this field show.
20805 * @param {Roo.bootstrap.DateField} this
20806 * @param {Mixed} date The date value
20811 * Fires when this field hide.
20812 * @param {Roo.bootstrap.DateField} this
20813 * @param {Mixed} date The date value
20818 * Fires when select a date.
20819 * @param {Roo.bootstrap.DateField} this
20820 * @param {Mixed} date The date value
20824 * @event beforeselect
20825 * Fires when before select a date.
20826 * @param {Roo.bootstrap.DateField} this
20827 * @param {Mixed} date The date value
20829 beforeselect : true
20833 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20836 * @cfg {String} format
20837 * The default date format string which can be overriden for localization support. The format must be
20838 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20842 * @cfg {String} altFormats
20843 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20844 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20846 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20854 todayHighlight : false,
20860 keyboardNavigation: true,
20862 calendarWeeks: false,
20864 startDate: -Infinity,
20868 daysOfWeekDisabled: [],
20872 singleMode : false,
20874 UTCDate: function()
20876 return new Date(Date.UTC.apply(Date, arguments));
20879 UTCToday: function()
20881 var today = new Date();
20882 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20885 getDate: function() {
20886 var d = this.getUTCDate();
20887 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20890 getUTCDate: function() {
20894 setDate: function(d) {
20895 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20898 setUTCDate: function(d) {
20900 this.setValue(this.formatDate(this.date));
20903 onRender: function(ct, position)
20906 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20908 this.language = this.language || 'en';
20909 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20910 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20912 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20913 this.format = this.format || 'm/d/y';
20914 this.isInline = false;
20915 this.isInput = true;
20916 this.component = this.el.select('.add-on', true).first() || false;
20917 this.component = (this.component && this.component.length === 0) ? false : this.component;
20918 this.hasInput = this.component && this.inputEl().length;
20920 if (typeof(this.minViewMode === 'string')) {
20921 switch (this.minViewMode) {
20923 this.minViewMode = 1;
20926 this.minViewMode = 2;
20929 this.minViewMode = 0;
20934 if (typeof(this.viewMode === 'string')) {
20935 switch (this.viewMode) {
20948 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20950 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20954 this.picker().on('mousedown', this.onMousedown, this);
20955 this.picker().on('click', this.onClick, this);
20957 this.picker().addClass('datepicker-dropdown');
20959 this.startViewMode = this.viewMode;
20961 if(this.singleMode){
20962 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20963 v.setVisibilityMode(Roo.Element.DISPLAY);
20967 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20968 v.setStyle('width', '189px');
20972 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20973 if(!this.calendarWeeks){
20978 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20979 v.attr('colspan', function(i, val){
20980 return parseInt(val) + 1;
20985 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20987 this.setStartDate(this.startDate);
20988 this.setEndDate(this.endDate);
20990 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20997 if(this.isInline) {
21002 picker : function()
21004 return this.pickerEl;
21005 // return this.el.select('.datepicker', true).first();
21008 fillDow: function()
21010 var dowCnt = this.weekStart;
21019 if(this.calendarWeeks){
21027 while (dowCnt < this.weekStart + 7) {
21031 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21035 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21038 fillMonths: function()
21041 var months = this.picker().select('>.datepicker-months td', true).first();
21043 months.dom.innerHTML = '';
21049 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21052 months.createChild(month);
21059 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;
21061 if (this.date < this.startDate) {
21062 this.viewDate = new Date(this.startDate);
21063 } else if (this.date > this.endDate) {
21064 this.viewDate = new Date(this.endDate);
21066 this.viewDate = new Date(this.date);
21074 var d = new Date(this.viewDate),
21075 year = d.getUTCFullYear(),
21076 month = d.getUTCMonth(),
21077 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21078 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21079 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21080 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21081 currentDate = this.date && this.date.valueOf(),
21082 today = this.UTCToday();
21084 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21086 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21088 // this.picker.select('>tfoot th.today').
21089 // .text(dates[this.language].today)
21090 // .toggle(this.todayBtn !== false);
21092 this.updateNavArrows();
21095 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21097 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21099 prevMonth.setUTCDate(day);
21101 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21103 var nextMonth = new Date(prevMonth);
21105 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21107 nextMonth = nextMonth.valueOf();
21109 var fillMonths = false;
21111 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21113 while(prevMonth.valueOf() <= nextMonth) {
21116 if (prevMonth.getUTCDay() === this.weekStart) {
21118 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21126 if(this.calendarWeeks){
21127 // ISO 8601: First week contains first thursday.
21128 // ISO also states week starts on Monday, but we can be more abstract here.
21130 // Start of current week: based on weekstart/current date
21131 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21132 // Thursday of this week
21133 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21134 // First Thursday of year, year from thursday
21135 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21136 // Calendar week: ms between thursdays, div ms per day, div 7 days
21137 calWeek = (th - yth) / 864e5 / 7 + 1;
21139 fillMonths.cn.push({
21147 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21149 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21152 if (this.todayHighlight &&
21153 prevMonth.getUTCFullYear() == today.getFullYear() &&
21154 prevMonth.getUTCMonth() == today.getMonth() &&
21155 prevMonth.getUTCDate() == today.getDate()) {
21156 clsName += ' today';
21159 if (currentDate && prevMonth.valueOf() === currentDate) {
21160 clsName += ' active';
21163 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21164 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21165 clsName += ' disabled';
21168 fillMonths.cn.push({
21170 cls: 'day ' + clsName,
21171 html: prevMonth.getDate()
21174 prevMonth.setDate(prevMonth.getDate()+1);
21177 var currentYear = this.date && this.date.getUTCFullYear();
21178 var currentMonth = this.date && this.date.getUTCMonth();
21180 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21182 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21183 v.removeClass('active');
21185 if(currentYear === year && k === currentMonth){
21186 v.addClass('active');
21189 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21190 v.addClass('disabled');
21196 year = parseInt(year/10, 10) * 10;
21198 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21200 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21203 for (var i = -1; i < 11; i++) {
21204 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21206 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21214 showMode: function(dir)
21217 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21220 Roo.each(this.picker().select('>div',true).elements, function(v){
21221 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21224 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21229 if(this.isInline) {
21233 this.picker().removeClass(['bottom', 'top']);
21235 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21237 * place to the top of element!
21241 this.picker().addClass('top');
21242 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21247 this.picker().addClass('bottom');
21249 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21252 parseDate : function(value)
21254 if(!value || value instanceof Date){
21257 var v = Date.parseDate(value, this.format);
21258 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21259 v = Date.parseDate(value, 'Y-m-d');
21261 if(!v && this.altFormats){
21262 if(!this.altFormatsArray){
21263 this.altFormatsArray = this.altFormats.split("|");
21265 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21266 v = Date.parseDate(value, this.altFormatsArray[i]);
21272 formatDate : function(date, fmt)
21274 return (!date || !(date instanceof Date)) ?
21275 date : date.dateFormat(fmt || this.format);
21278 onFocus : function()
21280 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21284 onBlur : function()
21286 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21288 var d = this.inputEl().getValue();
21295 showPopup : function()
21297 this.picker().show();
21301 this.fireEvent('showpopup', this, this.date);
21304 hidePopup : function()
21306 if(this.isInline) {
21309 this.picker().hide();
21310 this.viewMode = this.startViewMode;
21313 this.fireEvent('hidepopup', this, this.date);
21317 onMousedown: function(e)
21319 e.stopPropagation();
21320 e.preventDefault();
21325 Roo.bootstrap.DateField.superclass.keyup.call(this);
21329 setValue: function(v)
21331 if(this.fireEvent('beforeselect', this, v) !== false){
21332 var d = new Date(this.parseDate(v) ).clearTime();
21334 if(isNaN(d.getTime())){
21335 this.date = this.viewDate = '';
21336 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21340 v = this.formatDate(d);
21342 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21344 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21348 this.fireEvent('select', this, this.date);
21352 getValue: function()
21354 return this.formatDate(this.date);
21357 fireKey: function(e)
21359 if (!this.picker().isVisible()){
21360 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21366 var dateChanged = false,
21368 newDate, newViewDate;
21373 e.preventDefault();
21377 if (!this.keyboardNavigation) {
21380 dir = e.keyCode == 37 ? -1 : 1;
21383 newDate = this.moveYear(this.date, dir);
21384 newViewDate = this.moveYear(this.viewDate, dir);
21385 } else if (e.shiftKey){
21386 newDate = this.moveMonth(this.date, dir);
21387 newViewDate = this.moveMonth(this.viewDate, dir);
21389 newDate = new Date(this.date);
21390 newDate.setUTCDate(this.date.getUTCDate() + dir);
21391 newViewDate = new Date(this.viewDate);
21392 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21394 if (this.dateWithinRange(newDate)){
21395 this.date = newDate;
21396 this.viewDate = newViewDate;
21397 this.setValue(this.formatDate(this.date));
21399 e.preventDefault();
21400 dateChanged = true;
21405 if (!this.keyboardNavigation) {
21408 dir = e.keyCode == 38 ? -1 : 1;
21410 newDate = this.moveYear(this.date, dir);
21411 newViewDate = this.moveYear(this.viewDate, dir);
21412 } else if (e.shiftKey){
21413 newDate = this.moveMonth(this.date, dir);
21414 newViewDate = this.moveMonth(this.viewDate, dir);
21416 newDate = new Date(this.date);
21417 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21418 newViewDate = new Date(this.viewDate);
21419 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21421 if (this.dateWithinRange(newDate)){
21422 this.date = newDate;
21423 this.viewDate = newViewDate;
21424 this.setValue(this.formatDate(this.date));
21426 e.preventDefault();
21427 dateChanged = true;
21431 this.setValue(this.formatDate(this.date));
21433 e.preventDefault();
21436 this.setValue(this.formatDate(this.date));
21450 onClick: function(e)
21452 e.stopPropagation();
21453 e.preventDefault();
21455 var target = e.getTarget();
21457 if(target.nodeName.toLowerCase() === 'i'){
21458 target = Roo.get(target).dom.parentNode;
21461 var nodeName = target.nodeName;
21462 var className = target.className;
21463 var html = target.innerHTML;
21464 //Roo.log(nodeName);
21466 switch(nodeName.toLowerCase()) {
21468 switch(className) {
21474 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21475 switch(this.viewMode){
21477 this.viewDate = this.moveMonth(this.viewDate, dir);
21481 this.viewDate = this.moveYear(this.viewDate, dir);
21487 var date = new Date();
21488 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21490 this.setValue(this.formatDate(this.date));
21497 if (className.indexOf('disabled') < 0) {
21498 this.viewDate.setUTCDate(1);
21499 if (className.indexOf('month') > -1) {
21500 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21502 var year = parseInt(html, 10) || 0;
21503 this.viewDate.setUTCFullYear(year);
21507 if(this.singleMode){
21508 this.setValue(this.formatDate(this.viewDate));
21519 //Roo.log(className);
21520 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21521 var day = parseInt(html, 10) || 1;
21522 var year = this.viewDate.getUTCFullYear(),
21523 month = this.viewDate.getUTCMonth();
21525 if (className.indexOf('old') > -1) {
21532 } else if (className.indexOf('new') > -1) {
21540 //Roo.log([year,month,day]);
21541 this.date = this.UTCDate(year, month, day,0,0,0,0);
21542 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21544 //Roo.log(this.formatDate(this.date));
21545 this.setValue(this.formatDate(this.date));
21552 setStartDate: function(startDate)
21554 this.startDate = startDate || -Infinity;
21555 if (this.startDate !== -Infinity) {
21556 this.startDate = this.parseDate(this.startDate);
21559 this.updateNavArrows();
21562 setEndDate: function(endDate)
21564 this.endDate = endDate || Infinity;
21565 if (this.endDate !== Infinity) {
21566 this.endDate = this.parseDate(this.endDate);
21569 this.updateNavArrows();
21572 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21574 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21575 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21576 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21578 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21579 return parseInt(d, 10);
21582 this.updateNavArrows();
21585 updateNavArrows: function()
21587 if(this.singleMode){
21591 var d = new Date(this.viewDate),
21592 year = d.getUTCFullYear(),
21593 month = d.getUTCMonth();
21595 Roo.each(this.picker().select('.prev', true).elements, function(v){
21597 switch (this.viewMode) {
21600 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21606 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21613 Roo.each(this.picker().select('.next', true).elements, function(v){
21615 switch (this.viewMode) {
21618 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21624 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21632 moveMonth: function(date, dir)
21637 var new_date = new Date(date.valueOf()),
21638 day = new_date.getUTCDate(),
21639 month = new_date.getUTCMonth(),
21640 mag = Math.abs(dir),
21642 dir = dir > 0 ? 1 : -1;
21645 // If going back one month, make sure month is not current month
21646 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21648 return new_date.getUTCMonth() == month;
21650 // If going forward one month, make sure month is as expected
21651 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21653 return new_date.getUTCMonth() != new_month;
21655 new_month = month + dir;
21656 new_date.setUTCMonth(new_month);
21657 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21658 if (new_month < 0 || new_month > 11) {
21659 new_month = (new_month + 12) % 12;
21662 // For magnitudes >1, move one month at a time...
21663 for (var i=0; i<mag; i++) {
21664 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21665 new_date = this.moveMonth(new_date, dir);
21667 // ...then reset the day, keeping it in the new month
21668 new_month = new_date.getUTCMonth();
21669 new_date.setUTCDate(day);
21671 return new_month != new_date.getUTCMonth();
21674 // Common date-resetting loop -- if date is beyond end of month, make it
21677 new_date.setUTCDate(--day);
21678 new_date.setUTCMonth(new_month);
21683 moveYear: function(date, dir)
21685 return this.moveMonth(date, dir*12);
21688 dateWithinRange: function(date)
21690 return date >= this.startDate && date <= this.endDate;
21696 this.picker().remove();
21699 validateValue : function(value)
21701 if(this.getVisibilityEl().hasClass('hidden')){
21705 if(value.length < 1) {
21706 if(this.allowBlank){
21712 if(value.length < this.minLength){
21715 if(value.length > this.maxLength){
21719 var vt = Roo.form.VTypes;
21720 if(!vt[this.vtype](value, this)){
21724 if(typeof this.validator == "function"){
21725 var msg = this.validator(value);
21731 if(this.regex && !this.regex.test(value)){
21735 if(typeof(this.parseDate(value)) == 'undefined'){
21739 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21743 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21753 this.date = this.viewDate = '';
21755 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21760 Roo.apply(Roo.bootstrap.DateField, {
21771 html: '<i class="fa fa-arrow-left"/>'
21781 html: '<i class="fa fa-arrow-right"/>'
21823 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21824 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21825 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21826 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21827 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21840 navFnc: 'FullYear',
21845 navFnc: 'FullYear',
21850 Roo.apply(Roo.bootstrap.DateField, {
21854 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21858 cls: 'datepicker-days',
21862 cls: 'table-condensed',
21864 Roo.bootstrap.DateField.head,
21868 Roo.bootstrap.DateField.footer
21875 cls: 'datepicker-months',
21879 cls: 'table-condensed',
21881 Roo.bootstrap.DateField.head,
21882 Roo.bootstrap.DateField.content,
21883 Roo.bootstrap.DateField.footer
21890 cls: 'datepicker-years',
21894 cls: 'table-condensed',
21896 Roo.bootstrap.DateField.head,
21897 Roo.bootstrap.DateField.content,
21898 Roo.bootstrap.DateField.footer
21917 * @class Roo.bootstrap.TimeField
21918 * @extends Roo.bootstrap.Input
21919 * Bootstrap DateField class
21923 * Create a new TimeField
21924 * @param {Object} config The config object
21927 Roo.bootstrap.TimeField = function(config){
21928 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21932 * Fires when this field show.
21933 * @param {Roo.bootstrap.DateField} thisthis
21934 * @param {Mixed} date The date value
21939 * Fires when this field hide.
21940 * @param {Roo.bootstrap.DateField} this
21941 * @param {Mixed} date The date value
21946 * Fires when select a date.
21947 * @param {Roo.bootstrap.DateField} this
21948 * @param {Mixed} date The date value
21954 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21957 * @cfg {String} format
21958 * The default time format string which can be overriden for localization support. The format must be
21959 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21963 onRender: function(ct, position)
21966 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21968 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21970 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21972 this.pop = this.picker().select('>.datepicker-time',true).first();
21973 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21975 this.picker().on('mousedown', this.onMousedown, this);
21976 this.picker().on('click', this.onClick, this);
21978 this.picker().addClass('datepicker-dropdown');
21983 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21984 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21985 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21986 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21987 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21988 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21992 fireKey: function(e){
21993 if (!this.picker().isVisible()){
21994 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22000 e.preventDefault();
22008 this.onTogglePeriod();
22011 this.onIncrementMinutes();
22014 this.onDecrementMinutes();
22023 onClick: function(e) {
22024 e.stopPropagation();
22025 e.preventDefault();
22028 picker : function()
22030 return this.el.select('.datepicker', true).first();
22033 fillTime: function()
22035 var time = this.pop.select('tbody', true).first();
22037 time.dom.innerHTML = '';
22052 cls: 'hours-up glyphicon glyphicon-chevron-up'
22072 cls: 'minutes-up glyphicon glyphicon-chevron-up'
22093 cls: 'timepicker-hour',
22108 cls: 'timepicker-minute',
22123 cls: 'btn btn-primary period',
22145 cls: 'hours-down glyphicon glyphicon-chevron-down'
22165 cls: 'minutes-down glyphicon glyphicon-chevron-down'
22183 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22190 var hours = this.time.getHours();
22191 var minutes = this.time.getMinutes();
22204 hours = hours - 12;
22208 hours = '0' + hours;
22212 minutes = '0' + minutes;
22215 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22216 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22217 this.pop.select('button', true).first().dom.innerHTML = period;
22223 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22225 var cls = ['bottom'];
22227 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22234 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22239 this.picker().addClass(cls.join('-'));
22243 Roo.each(cls, function(c){
22245 _this.picker().setTop(_this.inputEl().getHeight());
22249 _this.picker().setTop(0 - _this.picker().getHeight());
22254 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22258 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22265 onFocus : function()
22267 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22271 onBlur : function()
22273 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22279 this.picker().show();
22284 this.fireEvent('show', this, this.date);
22289 this.picker().hide();
22292 this.fireEvent('hide', this, this.date);
22295 setTime : function()
22298 this.setValue(this.time.format(this.format));
22300 this.fireEvent('select', this, this.date);
22305 onMousedown: function(e){
22306 e.stopPropagation();
22307 e.preventDefault();
22310 onIncrementHours: function()
22312 Roo.log('onIncrementHours');
22313 this.time = this.time.add(Date.HOUR, 1);
22318 onDecrementHours: function()
22320 Roo.log('onDecrementHours');
22321 this.time = this.time.add(Date.HOUR, -1);
22325 onIncrementMinutes: function()
22327 Roo.log('onIncrementMinutes');
22328 this.time = this.time.add(Date.MINUTE, 1);
22332 onDecrementMinutes: function()
22334 Roo.log('onDecrementMinutes');
22335 this.time = this.time.add(Date.MINUTE, -1);
22339 onTogglePeriod: function()
22341 Roo.log('onTogglePeriod');
22342 this.time = this.time.add(Date.HOUR, 12);
22349 Roo.apply(Roo.bootstrap.TimeField, {
22379 cls: 'btn btn-info ok',
22391 Roo.apply(Roo.bootstrap.TimeField, {
22395 cls: 'datepicker dropdown-menu',
22399 cls: 'datepicker-time',
22403 cls: 'table-condensed',
22405 Roo.bootstrap.TimeField.content,
22406 Roo.bootstrap.TimeField.footer
22425 * @class Roo.bootstrap.MonthField
22426 * @extends Roo.bootstrap.Input
22427 * Bootstrap MonthField class
22429 * @cfg {String} language default en
22432 * Create a new MonthField
22433 * @param {Object} config The config object
22436 Roo.bootstrap.MonthField = function(config){
22437 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22442 * Fires when this field show.
22443 * @param {Roo.bootstrap.MonthField} this
22444 * @param {Mixed} date The date value
22449 * Fires when this field hide.
22450 * @param {Roo.bootstrap.MonthField} this
22451 * @param {Mixed} date The date value
22456 * Fires when select a date.
22457 * @param {Roo.bootstrap.MonthField} this
22458 * @param {String} oldvalue The old value
22459 * @param {String} newvalue The new value
22465 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22467 onRender: function(ct, position)
22470 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22472 this.language = this.language || 'en';
22473 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22474 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22476 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22477 this.isInline = false;
22478 this.isInput = true;
22479 this.component = this.el.select('.add-on', true).first() || false;
22480 this.component = (this.component && this.component.length === 0) ? false : this.component;
22481 this.hasInput = this.component && this.inputEL().length;
22483 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22485 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22487 this.picker().on('mousedown', this.onMousedown, this);
22488 this.picker().on('click', this.onClick, this);
22490 this.picker().addClass('datepicker-dropdown');
22492 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22493 v.setStyle('width', '189px');
22500 if(this.isInline) {
22506 setValue: function(v, suppressEvent)
22508 var o = this.getValue();
22510 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22514 if(suppressEvent !== true){
22515 this.fireEvent('select', this, o, v);
22520 getValue: function()
22525 onClick: function(e)
22527 e.stopPropagation();
22528 e.preventDefault();
22530 var target = e.getTarget();
22532 if(target.nodeName.toLowerCase() === 'i'){
22533 target = Roo.get(target).dom.parentNode;
22536 var nodeName = target.nodeName;
22537 var className = target.className;
22538 var html = target.innerHTML;
22540 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22544 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22546 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22552 picker : function()
22554 return this.pickerEl;
22557 fillMonths: function()
22560 var months = this.picker().select('>.datepicker-months td', true).first();
22562 months.dom.innerHTML = '';
22568 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22571 months.createChild(month);
22580 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22581 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22584 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22585 e.removeClass('active');
22587 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22588 e.addClass('active');
22595 if(this.isInline) {
22599 this.picker().removeClass(['bottom', 'top']);
22601 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22603 * place to the top of element!
22607 this.picker().addClass('top');
22608 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22613 this.picker().addClass('bottom');
22615 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22618 onFocus : function()
22620 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22624 onBlur : function()
22626 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22628 var d = this.inputEl().getValue();
22637 this.picker().show();
22638 this.picker().select('>.datepicker-months', true).first().show();
22642 this.fireEvent('show', this, this.date);
22647 if(this.isInline) {
22650 this.picker().hide();
22651 this.fireEvent('hide', this, this.date);
22655 onMousedown: function(e)
22657 e.stopPropagation();
22658 e.preventDefault();
22663 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22667 fireKey: function(e)
22669 if (!this.picker().isVisible()){
22670 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22681 e.preventDefault();
22685 dir = e.keyCode == 37 ? -1 : 1;
22687 this.vIndex = this.vIndex + dir;
22689 if(this.vIndex < 0){
22693 if(this.vIndex > 11){
22697 if(isNaN(this.vIndex)){
22701 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22707 dir = e.keyCode == 38 ? -1 : 1;
22709 this.vIndex = this.vIndex + dir * 4;
22711 if(this.vIndex < 0){
22715 if(this.vIndex > 11){
22719 if(isNaN(this.vIndex)){
22723 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22728 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22729 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22733 e.preventDefault();
22736 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22737 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22753 this.picker().remove();
22758 Roo.apply(Roo.bootstrap.MonthField, {
22777 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22778 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22783 Roo.apply(Roo.bootstrap.MonthField, {
22787 cls: 'datepicker dropdown-menu roo-dynamic',
22791 cls: 'datepicker-months',
22795 cls: 'table-condensed',
22797 Roo.bootstrap.DateField.content
22817 * @class Roo.bootstrap.CheckBox
22818 * @extends Roo.bootstrap.Input
22819 * Bootstrap CheckBox class
22821 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22822 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22823 * @cfg {String} boxLabel The text that appears beside the checkbox
22824 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22825 * @cfg {Boolean} checked initnal the element
22826 * @cfg {Boolean} inline inline the element (default false)
22827 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22828 * @cfg {String} tooltip label tooltip
22831 * Create a new CheckBox
22832 * @param {Object} config The config object
22835 Roo.bootstrap.CheckBox = function(config){
22836 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22841 * Fires when the element is checked or unchecked.
22842 * @param {Roo.bootstrap.CheckBox} this This input
22843 * @param {Boolean} checked The new checked value
22848 * Fires when the element is click.
22849 * @param {Roo.bootstrap.CheckBox} this This input
22856 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22858 inputType: 'checkbox',
22867 // checkbox success does not make any sense really..
22872 getAutoCreate : function()
22874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22880 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22883 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22889 type : this.inputType,
22890 value : this.inputValue,
22891 cls : 'roo-' + this.inputType, //'form-box',
22892 placeholder : this.placeholder || ''
22896 if(this.inputType != 'radio'){
22900 cls : 'roo-hidden-value',
22901 value : this.checked ? this.inputValue : this.valueOff
22906 if (this.weight) { // Validity check?
22907 cfg.cls += " " + this.inputType + "-" + this.weight;
22910 if (this.disabled) {
22911 input.disabled=true;
22915 input.checked = this.checked;
22920 input.name = this.name;
22922 if(this.inputType != 'radio'){
22923 hidden.name = this.name;
22924 input.name = '_hidden_' + this.name;
22929 input.cls += ' input-' + this.size;
22934 ['xs','sm','md','lg'].map(function(size){
22935 if (settings[size]) {
22936 cfg.cls += ' col-' + size + '-' + settings[size];
22940 var inputblock = input;
22942 if (this.before || this.after) {
22945 cls : 'input-group',
22950 inputblock.cn.push({
22952 cls : 'input-group-addon',
22957 inputblock.cn.push(input);
22959 if(this.inputType != 'radio'){
22960 inputblock.cn.push(hidden);
22964 inputblock.cn.push({
22966 cls : 'input-group-addon',
22972 var boxLabelCfg = false;
22978 //'for': id, // box label is handled by onclick - so no for...
22980 html: this.boxLabel
22983 boxLabelCfg.tooltip = this.tooltip;
22989 if (align ==='left' && this.fieldLabel.length) {
22990 // Roo.log("left and has label");
22995 cls : 'control-label',
22996 html : this.fieldLabel
23007 cfg.cn[1].cn.push(boxLabelCfg);
23010 if(this.labelWidth > 12){
23011 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23014 if(this.labelWidth < 13 && this.labelmd == 0){
23015 this.labelmd = this.labelWidth;
23018 if(this.labellg > 0){
23019 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23020 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23023 if(this.labelmd > 0){
23024 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23025 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23028 if(this.labelsm > 0){
23029 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23030 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23033 if(this.labelxs > 0){
23034 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23035 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23038 } else if ( this.fieldLabel.length) {
23039 // Roo.log(" label");
23043 tag: this.boxLabel ? 'span' : 'label',
23045 cls: 'control-label box-input-label',
23046 //cls : 'input-group-addon',
23047 html : this.fieldLabel
23054 cfg.cn.push(boxLabelCfg);
23059 // Roo.log(" no label && no align");
23060 cfg.cn = [ inputblock ] ;
23062 cfg.cn.push(boxLabelCfg);
23070 if(this.inputType != 'radio'){
23071 cfg.cn.push(hidden);
23079 * return the real input element.
23081 inputEl: function ()
23083 return this.el.select('input.roo-' + this.inputType,true).first();
23085 hiddenEl: function ()
23087 return this.el.select('input.roo-hidden-value',true).first();
23090 labelEl: function()
23092 return this.el.select('label.control-label',true).first();
23094 /* depricated... */
23098 return this.labelEl();
23101 boxLabelEl: function()
23103 return this.el.select('label.box-label',true).first();
23106 initEvents : function()
23108 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23110 this.inputEl().on('click', this.onClick, this);
23112 if (this.boxLabel) {
23113 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23116 this.startValue = this.getValue();
23119 Roo.bootstrap.CheckBox.register(this);
23123 onClick : function(e)
23125 if(this.fireEvent('click', this, e) !== false){
23126 this.setChecked(!this.checked);
23131 setChecked : function(state,suppressEvent)
23133 this.startValue = this.getValue();
23135 if(this.inputType == 'radio'){
23137 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23138 e.dom.checked = false;
23141 this.inputEl().dom.checked = true;
23143 this.inputEl().dom.value = this.inputValue;
23145 if(suppressEvent !== true){
23146 this.fireEvent('check', this, true);
23154 this.checked = state;
23156 this.inputEl().dom.checked = state;
23159 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23161 if(suppressEvent !== true){
23162 this.fireEvent('check', this, state);
23168 getValue : function()
23170 if(this.inputType == 'radio'){
23171 return this.getGroupValue();
23174 return this.hiddenEl().dom.value;
23178 getGroupValue : function()
23180 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23184 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23187 setValue : function(v,suppressEvent)
23189 if(this.inputType == 'radio'){
23190 this.setGroupValue(v, suppressEvent);
23194 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23199 setGroupValue : function(v, suppressEvent)
23201 this.startValue = this.getValue();
23203 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23204 e.dom.checked = false;
23206 if(e.dom.value == v){
23207 e.dom.checked = true;
23211 if(suppressEvent !== true){
23212 this.fireEvent('check', this, true);
23220 validate : function()
23222 if(this.getVisibilityEl().hasClass('hidden')){
23228 (this.inputType == 'radio' && this.validateRadio()) ||
23229 (this.inputType == 'checkbox' && this.validateCheckbox())
23235 this.markInvalid();
23239 validateRadio : function()
23241 if(this.getVisibilityEl().hasClass('hidden')){
23245 if(this.allowBlank){
23251 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23252 if(!e.dom.checked){
23264 validateCheckbox : function()
23267 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23268 //return (this.getValue() == this.inputValue) ? true : false;
23271 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23279 for(var i in group){
23280 if(group[i].el.isVisible(true)){
23288 for(var i in group){
23293 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23300 * Mark this field as valid
23302 markValid : function()
23306 this.fireEvent('valid', this);
23308 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23311 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23318 if(this.inputType == 'radio'){
23319 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23320 var fg = e.findParent('.form-group', false, true);
23321 if (Roo.bootstrap.version == 3) {
23322 fg.removeClass([_this.invalidClass, _this.validClass]);
23323 fg.addClass(_this.validClass);
23325 fg.removeClass(['is-valid', 'is-invalid']);
23326 fg.addClass('is-valid');
23334 var fg = this.el.findParent('.form-group', false, true);
23335 if (Roo.bootstrap.version == 3) {
23336 fg.removeClass([this.invalidClass, this.validClass]);
23337 fg.addClass(this.validClass);
23339 fg.removeClass(['is-valid', 'is-invalid']);
23340 fg.addClass('is-valid');
23345 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23351 for(var i in group){
23352 var fg = group[i].el.findParent('.form-group', false, true);
23353 if (Roo.bootstrap.version == 3) {
23354 fg.removeClass([this.invalidClass, this.validClass]);
23355 fg.addClass(this.validClass);
23357 fg.removeClass(['is-valid', 'is-invalid']);
23358 fg.addClass('is-valid');
23364 * Mark this field as invalid
23365 * @param {String} msg The validation message
23367 markInvalid : function(msg)
23369 if(this.allowBlank){
23375 this.fireEvent('invalid', this, msg);
23377 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23380 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23384 label.markInvalid();
23387 if(this.inputType == 'radio'){
23389 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23390 var fg = e.findParent('.form-group', false, true);
23391 if (Roo.bootstrap.version == 3) {
23392 fg.removeClass([_this.invalidClass, _this.validClass]);
23393 fg.addClass(_this.invalidClass);
23395 fg.removeClass(['is-invalid', 'is-valid']);
23396 fg.addClass('is-invalid');
23404 var fg = this.el.findParent('.form-group', false, true);
23405 if (Roo.bootstrap.version == 3) {
23406 fg.removeClass([_this.invalidClass, _this.validClass]);
23407 fg.addClass(_this.invalidClass);
23409 fg.removeClass(['is-invalid', 'is-valid']);
23410 fg.addClass('is-invalid');
23415 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23421 for(var i in group){
23422 var fg = group[i].el.findParent('.form-group', false, true);
23423 if (Roo.bootstrap.version == 3) {
23424 fg.removeClass([_this.invalidClass, _this.validClass]);
23425 fg.addClass(_this.invalidClass);
23427 fg.removeClass(['is-invalid', 'is-valid']);
23428 fg.addClass('is-invalid');
23434 clearInvalid : function()
23436 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23438 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23440 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23442 if (label && label.iconEl) {
23443 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23444 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23448 disable : function()
23450 if(this.inputType != 'radio'){
23451 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23458 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23459 _this.getActionEl().addClass(this.disabledClass);
23460 e.dom.disabled = true;
23464 this.disabled = true;
23465 this.fireEvent("disable", this);
23469 enable : function()
23471 if(this.inputType != 'radio'){
23472 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23479 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23480 _this.getActionEl().removeClass(this.disabledClass);
23481 e.dom.disabled = false;
23485 this.disabled = false;
23486 this.fireEvent("enable", this);
23490 setBoxLabel : function(v)
23495 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23501 Roo.apply(Roo.bootstrap.CheckBox, {
23506 * register a CheckBox Group
23507 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23509 register : function(checkbox)
23511 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23512 this.groups[checkbox.groupId] = {};
23515 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23519 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23523 * fetch a CheckBox Group based on the group ID
23524 * @param {string} the group ID
23525 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23527 get: function(groupId) {
23528 if (typeof(this.groups[groupId]) == 'undefined') {
23532 return this.groups[groupId] ;
23545 * @class Roo.bootstrap.Radio
23546 * @extends Roo.bootstrap.Component
23547 * Bootstrap Radio class
23548 * @cfg {String} boxLabel - the label associated
23549 * @cfg {String} value - the value of radio
23552 * Create a new Radio
23553 * @param {Object} config The config object
23555 Roo.bootstrap.Radio = function(config){
23556 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23560 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23566 getAutoCreate : function()
23570 cls : 'form-group radio',
23575 html : this.boxLabel
23583 initEvents : function()
23585 this.parent().register(this);
23587 this.el.on('click', this.onClick, this);
23591 onClick : function(e)
23593 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23594 this.setChecked(true);
23598 setChecked : function(state, suppressEvent)
23600 this.parent().setValue(this.value, suppressEvent);
23604 setBoxLabel : function(v)
23609 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23624 * @class Roo.bootstrap.SecurePass
23625 * @extends Roo.bootstrap.Input
23626 * Bootstrap SecurePass class
23630 * Create a new SecurePass
23631 * @param {Object} config The config object
23634 Roo.bootstrap.SecurePass = function (config) {
23635 // these go here, so the translation tool can replace them..
23637 PwdEmpty: "Please type a password, and then retype it to confirm.",
23638 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23639 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23640 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23641 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23642 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23643 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23644 TooWeak: "Your password is Too Weak."
23646 this.meterLabel = "Password strength:";
23647 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23648 this.meterClass = [
23649 "roo-password-meter-tooweak",
23650 "roo-password-meter-weak",
23651 "roo-password-meter-medium",
23652 "roo-password-meter-strong",
23653 "roo-password-meter-grey"
23658 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23661 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23663 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23665 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23666 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23667 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23668 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23669 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23670 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23671 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23681 * @cfg {String/Object} Label for the strength meter (defaults to
23682 * 'Password strength:')
23687 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23688 * ['Weak', 'Medium', 'Strong'])
23691 pwdStrengths: false,
23704 initEvents: function ()
23706 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23708 if (this.el.is('input[type=password]') && Roo.isSafari) {
23709 this.el.on('keydown', this.SafariOnKeyDown, this);
23712 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23715 onRender: function (ct, position)
23717 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23718 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23719 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23721 this.trigger.createChild({
23726 cls: 'roo-password-meter-grey col-xs-12',
23729 //width: this.meterWidth + 'px'
23733 cls: 'roo-password-meter-text'
23739 if (this.hideTrigger) {
23740 this.trigger.setDisplayed(false);
23742 this.setSize(this.width || '', this.height || '');
23745 onDestroy: function ()
23747 if (this.trigger) {
23748 this.trigger.removeAllListeners();
23749 this.trigger.remove();
23752 this.wrap.remove();
23754 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23757 checkStrength: function ()
23759 var pwd = this.inputEl().getValue();
23760 if (pwd == this._lastPwd) {
23765 if (this.ClientSideStrongPassword(pwd)) {
23767 } else if (this.ClientSideMediumPassword(pwd)) {
23769 } else if (this.ClientSideWeakPassword(pwd)) {
23775 Roo.log('strength1: ' + strength);
23777 //var pm = this.trigger.child('div/div/div').dom;
23778 var pm = this.trigger.child('div/div');
23779 pm.removeClass(this.meterClass);
23780 pm.addClass(this.meterClass[strength]);
23783 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23785 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23787 this._lastPwd = pwd;
23791 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23793 this._lastPwd = '';
23795 var pm = this.trigger.child('div/div');
23796 pm.removeClass(this.meterClass);
23797 pm.addClass('roo-password-meter-grey');
23800 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23803 this.inputEl().dom.type='password';
23806 validateValue: function (value)
23808 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23811 if (value.length == 0) {
23812 if (this.allowBlank) {
23813 this.clearInvalid();
23817 this.markInvalid(this.errors.PwdEmpty);
23818 this.errorMsg = this.errors.PwdEmpty;
23826 if (!value.match(/[\x21-\x7e]+/)) {
23827 this.markInvalid(this.errors.PwdBadChar);
23828 this.errorMsg = this.errors.PwdBadChar;
23831 if (value.length < 6) {
23832 this.markInvalid(this.errors.PwdShort);
23833 this.errorMsg = this.errors.PwdShort;
23836 if (value.length > 16) {
23837 this.markInvalid(this.errors.PwdLong);
23838 this.errorMsg = this.errors.PwdLong;
23842 if (this.ClientSideStrongPassword(value)) {
23844 } else if (this.ClientSideMediumPassword(value)) {
23846 } else if (this.ClientSideWeakPassword(value)) {
23853 if (strength < 2) {
23854 //this.markInvalid(this.errors.TooWeak);
23855 this.errorMsg = this.errors.TooWeak;
23860 console.log('strength2: ' + strength);
23862 //var pm = this.trigger.child('div/div/div').dom;
23864 var pm = this.trigger.child('div/div');
23865 pm.removeClass(this.meterClass);
23866 pm.addClass(this.meterClass[strength]);
23868 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23870 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23872 this.errorMsg = '';
23876 CharacterSetChecks: function (type)
23879 this.fResult = false;
23882 isctype: function (character, type)
23885 case this.kCapitalLetter:
23886 if (character >= 'A' && character <= 'Z') {
23891 case this.kSmallLetter:
23892 if (character >= 'a' && character <= 'z') {
23898 if (character >= '0' && character <= '9') {
23903 case this.kPunctuation:
23904 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23915 IsLongEnough: function (pwd, size)
23917 return !(pwd == null || isNaN(size) || pwd.length < size);
23920 SpansEnoughCharacterSets: function (word, nb)
23922 if (!this.IsLongEnough(word, nb))
23927 var characterSetChecks = new Array(
23928 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23929 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23932 for (var index = 0; index < word.length; ++index) {
23933 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23934 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23935 characterSetChecks[nCharSet].fResult = true;
23942 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23943 if (characterSetChecks[nCharSet].fResult) {
23948 if (nCharSets < nb) {
23954 ClientSideStrongPassword: function (pwd)
23956 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23959 ClientSideMediumPassword: function (pwd)
23961 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23964 ClientSideWeakPassword: function (pwd)
23966 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23969 })//<script type="text/javascript">
23972 * Based Ext JS Library 1.1.1
23973 * Copyright(c) 2006-2007, Ext JS, LLC.
23979 * @class Roo.HtmlEditorCore
23980 * @extends Roo.Component
23981 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23983 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23986 Roo.HtmlEditorCore = function(config){
23989 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23994 * @event initialize
23995 * Fires when the editor is fully initialized (including the iframe)
23996 * @param {Roo.HtmlEditorCore} this
24001 * Fires when the editor is first receives the focus. Any insertion must wait
24002 * until after this event.
24003 * @param {Roo.HtmlEditorCore} this
24007 * @event beforesync
24008 * Fires before the textarea is updated with content from the editor iframe. Return false
24009 * to cancel the sync.
24010 * @param {Roo.HtmlEditorCore} this
24011 * @param {String} html
24015 * @event beforepush
24016 * Fires before the iframe editor is updated with content from the textarea. Return false
24017 * to cancel the push.
24018 * @param {Roo.HtmlEditorCore} this
24019 * @param {String} html
24024 * Fires when the textarea is updated with content from the editor iframe.
24025 * @param {Roo.HtmlEditorCore} this
24026 * @param {String} html
24031 * Fires when the iframe editor is updated with content from the textarea.
24032 * @param {Roo.HtmlEditorCore} this
24033 * @param {String} html
24038 * @event editorevent
24039 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24040 * @param {Roo.HtmlEditorCore} this
24046 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24048 // defaults : white / black...
24049 this.applyBlacklists();
24056 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24060 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24066 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24071 * @cfg {Number} height (in pixels)
24075 * @cfg {Number} width (in pixels)
24080 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24083 stylesheets: false,
24088 // private properties
24089 validationEvent : false,
24091 initialized : false,
24093 sourceEditMode : false,
24094 onFocus : Roo.emptyFn,
24096 hideMode:'offsets',
24100 // blacklist + whitelisted elements..
24107 * Protected method that will not generally be called directly. It
24108 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24109 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24111 getDocMarkup : function(){
24115 // inherit styels from page...??
24116 if (this.stylesheets === false) {
24118 Roo.get(document.head).select('style').each(function(node) {
24119 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24122 Roo.get(document.head).select('link').each(function(node) {
24123 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24126 } else if (!this.stylesheets.length) {
24128 st = '<style type="text/css">' +
24129 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24132 for (var i in this.stylesheets) {
24133 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24138 st += '<style type="text/css">' +
24139 'IMG { cursor: pointer } ' +
24142 var cls = 'roo-htmleditor-body';
24144 if(this.bodyCls.length){
24145 cls += ' ' + this.bodyCls;
24148 return '<html><head>' + st +
24149 //<style type="text/css">' +
24150 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24152 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24156 onRender : function(ct, position)
24159 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24160 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24163 this.el.dom.style.border = '0 none';
24164 this.el.dom.setAttribute('tabIndex', -1);
24165 this.el.addClass('x-hidden hide');
24169 if(Roo.isIE){ // fix IE 1px bogus margin
24170 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24174 this.frameId = Roo.id();
24178 var iframe = this.owner.wrap.createChild({
24180 cls: 'form-control', // bootstrap..
24182 name: this.frameId,
24183 frameBorder : 'no',
24184 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24189 this.iframe = iframe.dom;
24191 this.assignDocWin();
24193 this.doc.designMode = 'on';
24196 this.doc.write(this.getDocMarkup());
24200 var task = { // must defer to wait for browser to be ready
24202 //console.log("run task?" + this.doc.readyState);
24203 this.assignDocWin();
24204 if(this.doc.body || this.doc.readyState == 'complete'){
24206 this.doc.designMode="on";
24210 Roo.TaskMgr.stop(task);
24211 this.initEditor.defer(10, this);
24218 Roo.TaskMgr.start(task);
24223 onResize : function(w, h)
24225 Roo.log('resize: ' +w + ',' + h );
24226 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24230 if(typeof w == 'number'){
24232 this.iframe.style.width = w + 'px';
24234 if(typeof h == 'number'){
24236 this.iframe.style.height = h + 'px';
24238 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24245 * Toggles the editor between standard and source edit mode.
24246 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24248 toggleSourceEdit : function(sourceEditMode){
24250 this.sourceEditMode = sourceEditMode === true;
24252 if(this.sourceEditMode){
24254 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24257 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24258 //this.iframe.className = '';
24261 //this.setSize(this.owner.wrap.getSize());
24262 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24269 * Protected method that will not generally be called directly. If you need/want
24270 * custom HTML cleanup, this is the method you should override.
24271 * @param {String} html The HTML to be cleaned
24272 * return {String} The cleaned HTML
24274 cleanHtml : function(html){
24275 html = String(html);
24276 if(html.length > 5){
24277 if(Roo.isSafari){ // strip safari nonsense
24278 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24281 if(html == ' '){
24288 * HTML Editor -> Textarea
24289 * Protected method that will not generally be called directly. Syncs the contents
24290 * of the editor iframe with the textarea.
24292 syncValue : function(){
24293 if(this.initialized){
24294 var bd = (this.doc.body || this.doc.documentElement);
24295 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24296 var html = bd.innerHTML;
24298 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24299 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24301 html = '<div style="'+m[0]+'">' + html + '</div>';
24304 html = this.cleanHtml(html);
24305 // fix up the special chars.. normaly like back quotes in word...
24306 // however we do not want to do this with chinese..
24307 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24309 var cc = match.charCodeAt();
24311 // Get the character value, handling surrogate pairs
24312 if (match.length == 2) {
24313 // It's a surrogate pair, calculate the Unicode code point
24314 var high = match.charCodeAt(0) - 0xD800;
24315 var low = match.charCodeAt(1) - 0xDC00;
24316 cc = (high * 0x400) + low + 0x10000;
24318 (cc >= 0x4E00 && cc < 0xA000 ) ||
24319 (cc >= 0x3400 && cc < 0x4E00 ) ||
24320 (cc >= 0xf900 && cc < 0xfb00 )
24325 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24326 return "&#" + cc + ";";
24333 if(this.owner.fireEvent('beforesync', this, html) !== false){
24334 this.el.dom.value = html;
24335 this.owner.fireEvent('sync', this, html);
24341 * Protected method that will not generally be called directly. Pushes the value of the textarea
24342 * into the iframe editor.
24344 pushValue : function(){
24345 if(this.initialized){
24346 var v = this.el.dom.value.trim();
24348 // if(v.length < 1){
24352 if(this.owner.fireEvent('beforepush', this, v) !== false){
24353 var d = (this.doc.body || this.doc.documentElement);
24355 this.cleanUpPaste();
24356 this.el.dom.value = d.innerHTML;
24357 this.owner.fireEvent('push', this, v);
24363 deferFocus : function(){
24364 this.focus.defer(10, this);
24368 focus : function(){
24369 if(this.win && !this.sourceEditMode){
24376 assignDocWin: function()
24378 var iframe = this.iframe;
24381 this.doc = iframe.contentWindow.document;
24382 this.win = iframe.contentWindow;
24384 // if (!Roo.get(this.frameId)) {
24387 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24388 // this.win = Roo.get(this.frameId).dom.contentWindow;
24390 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24394 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24395 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24400 initEditor : function(){
24401 //console.log("INIT EDITOR");
24402 this.assignDocWin();
24406 this.doc.designMode="on";
24408 this.doc.write(this.getDocMarkup());
24411 var dbody = (this.doc.body || this.doc.documentElement);
24412 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24413 // this copies styles from the containing element into thsi one..
24414 // not sure why we need all of this..
24415 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24417 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24418 //ss['background-attachment'] = 'fixed'; // w3c
24419 dbody.bgProperties = 'fixed'; // ie
24420 //Roo.DomHelper.applyStyles(dbody, ss);
24421 Roo.EventManager.on(this.doc, {
24422 //'mousedown': this.onEditorEvent,
24423 'mouseup': this.onEditorEvent,
24424 'dblclick': this.onEditorEvent,
24425 'click': this.onEditorEvent,
24426 'keyup': this.onEditorEvent,
24431 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24433 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24434 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24436 this.initialized = true;
24438 this.owner.fireEvent('initialize', this);
24443 onDestroy : function(){
24449 //for (var i =0; i < this.toolbars.length;i++) {
24450 // // fixme - ask toolbars for heights?
24451 // this.toolbars[i].onDestroy();
24454 //this.wrap.dom.innerHTML = '';
24455 //this.wrap.remove();
24460 onFirstFocus : function(){
24462 this.assignDocWin();
24465 this.activated = true;
24468 if(Roo.isGecko){ // prevent silly gecko errors
24470 var s = this.win.getSelection();
24471 if(!s.focusNode || s.focusNode.nodeType != 3){
24472 var r = s.getRangeAt(0);
24473 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24478 this.execCmd('useCSS', true);
24479 this.execCmd('styleWithCSS', false);
24482 this.owner.fireEvent('activate', this);
24486 adjustFont: function(btn){
24487 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24488 //if(Roo.isSafari){ // safari
24491 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24492 if(Roo.isSafari){ // safari
24493 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24494 v = (v < 10) ? 10 : v;
24495 v = (v > 48) ? 48 : v;
24496 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24501 v = Math.max(1, v+adjust);
24503 this.execCmd('FontSize', v );
24506 onEditorEvent : function(e)
24508 this.owner.fireEvent('editorevent', this, e);
24509 // this.updateToolbar();
24510 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24513 insertTag : function(tg)
24515 // could be a bit smarter... -> wrap the current selected tRoo..
24516 if (tg.toLowerCase() == 'span' ||
24517 tg.toLowerCase() == 'code' ||
24518 tg.toLowerCase() == 'sup' ||
24519 tg.toLowerCase() == 'sub'
24522 range = this.createRange(this.getSelection());
24523 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24524 wrappingNode.appendChild(range.extractContents());
24525 range.insertNode(wrappingNode);
24532 this.execCmd("formatblock", tg);
24536 insertText : function(txt)
24540 var range = this.createRange();
24541 range.deleteContents();
24542 //alert(Sender.getAttribute('label'));
24544 range.insertNode(this.doc.createTextNode(txt));
24550 * Executes a Midas editor command on the editor document and performs necessary focus and
24551 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24552 * @param {String} cmd The Midas command
24553 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24555 relayCmd : function(cmd, value){
24557 this.execCmd(cmd, value);
24558 this.owner.fireEvent('editorevent', this);
24559 //this.updateToolbar();
24560 this.owner.deferFocus();
24564 * Executes a Midas editor command directly on the editor document.
24565 * For visual commands, you should use {@link #relayCmd} instead.
24566 * <b>This should only be called after the editor is initialized.</b>
24567 * @param {String} cmd The Midas command
24568 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24570 execCmd : function(cmd, value){
24571 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24578 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24580 * @param {String} text | dom node..
24582 insertAtCursor : function(text)
24585 if(!this.activated){
24591 var r = this.doc.selection.createRange();
24602 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24606 // from jquery ui (MIT licenced)
24608 var win = this.win;
24610 if (win.getSelection && win.getSelection().getRangeAt) {
24611 range = win.getSelection().getRangeAt(0);
24612 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24613 range.insertNode(node);
24614 } else if (win.document.selection && win.document.selection.createRange) {
24615 // no firefox support
24616 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24617 win.document.selection.createRange().pasteHTML(txt);
24619 // no firefox support
24620 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24621 this.execCmd('InsertHTML', txt);
24630 mozKeyPress : function(e){
24632 var c = e.getCharCode(), cmd;
24635 c = String.fromCharCode(c).toLowerCase();
24649 this.cleanUpPaste.defer(100, this);
24657 e.preventDefault();
24665 fixKeys : function(){ // load time branching for fastest keydown performance
24667 return function(e){
24668 var k = e.getKey(), r;
24671 r = this.doc.selection.createRange();
24674 r.pasteHTML('    ');
24681 r = this.doc.selection.createRange();
24683 var target = r.parentElement();
24684 if(!target || target.tagName.toLowerCase() != 'li'){
24686 r.pasteHTML('<br />');
24692 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24693 this.cleanUpPaste.defer(100, this);
24699 }else if(Roo.isOpera){
24700 return function(e){
24701 var k = e.getKey();
24705 this.execCmd('InsertHTML','    ');
24708 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24709 this.cleanUpPaste.defer(100, this);
24714 }else if(Roo.isSafari){
24715 return function(e){
24716 var k = e.getKey();
24720 this.execCmd('InsertText','\t');
24724 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24725 this.cleanUpPaste.defer(100, this);
24733 getAllAncestors: function()
24735 var p = this.getSelectedNode();
24738 a.push(p); // push blank onto stack..
24739 p = this.getParentElement();
24743 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24747 a.push(this.doc.body);
24751 lastSelNode : false,
24754 getSelection : function()
24756 this.assignDocWin();
24757 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24760 getSelectedNode: function()
24762 // this may only work on Gecko!!!
24764 // should we cache this!!!!
24769 var range = this.createRange(this.getSelection()).cloneRange();
24772 var parent = range.parentElement();
24774 var testRange = range.duplicate();
24775 testRange.moveToElementText(parent);
24776 if (testRange.inRange(range)) {
24779 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24782 parent = parent.parentElement;
24787 // is ancestor a text element.
24788 var ac = range.commonAncestorContainer;
24789 if (ac.nodeType == 3) {
24790 ac = ac.parentNode;
24793 var ar = ac.childNodes;
24796 var other_nodes = [];
24797 var has_other_nodes = false;
24798 for (var i=0;i<ar.length;i++) {
24799 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24802 // fullly contained node.
24804 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24809 // probably selected..
24810 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24811 other_nodes.push(ar[i]);
24815 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24820 has_other_nodes = true;
24822 if (!nodes.length && other_nodes.length) {
24823 nodes= other_nodes;
24825 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24831 createRange: function(sel)
24833 // this has strange effects when using with
24834 // top toolbar - not sure if it's a great idea.
24835 //this.editor.contentWindow.focus();
24836 if (typeof sel != "undefined") {
24838 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24840 return this.doc.createRange();
24843 return this.doc.createRange();
24846 getParentElement: function()
24849 this.assignDocWin();
24850 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24852 var range = this.createRange(sel);
24855 var p = range.commonAncestorContainer;
24856 while (p.nodeType == 3) { // text node
24867 * Range intersection.. the hard stuff...
24871 * [ -- selected range --- ]
24875 * if end is before start or hits it. fail.
24876 * if start is after end or hits it fail.
24878 * if either hits (but other is outside. - then it's not
24884 // @see http://www.thismuchiknow.co.uk/?p=64.
24885 rangeIntersectsNode : function(range, node)
24887 var nodeRange = node.ownerDocument.createRange();
24889 nodeRange.selectNode(node);
24891 nodeRange.selectNodeContents(node);
24894 var rangeStartRange = range.cloneRange();
24895 rangeStartRange.collapse(true);
24897 var rangeEndRange = range.cloneRange();
24898 rangeEndRange.collapse(false);
24900 var nodeStartRange = nodeRange.cloneRange();
24901 nodeStartRange.collapse(true);
24903 var nodeEndRange = nodeRange.cloneRange();
24904 nodeEndRange.collapse(false);
24906 return rangeStartRange.compareBoundaryPoints(
24907 Range.START_TO_START, nodeEndRange) == -1 &&
24908 rangeEndRange.compareBoundaryPoints(
24909 Range.START_TO_START, nodeStartRange) == 1;
24913 rangeCompareNode : function(range, node)
24915 var nodeRange = node.ownerDocument.createRange();
24917 nodeRange.selectNode(node);
24919 nodeRange.selectNodeContents(node);
24923 range.collapse(true);
24925 nodeRange.collapse(true);
24927 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24928 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24930 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24932 var nodeIsBefore = ss == 1;
24933 var nodeIsAfter = ee == -1;
24935 if (nodeIsBefore && nodeIsAfter) {
24938 if (!nodeIsBefore && nodeIsAfter) {
24939 return 1; //right trailed.
24942 if (nodeIsBefore && !nodeIsAfter) {
24943 return 2; // left trailed.
24949 // private? - in a new class?
24950 cleanUpPaste : function()
24952 // cleans up the whole document..
24953 Roo.log('cleanuppaste');
24955 this.cleanUpChildren(this.doc.body);
24956 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24957 if (clean != this.doc.body.innerHTML) {
24958 this.doc.body.innerHTML = clean;
24963 cleanWordChars : function(input) {// change the chars to hex code
24964 var he = Roo.HtmlEditorCore;
24966 var output = input;
24967 Roo.each(he.swapCodes, function(sw) {
24968 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24970 output = output.replace(swapper, sw[1]);
24977 cleanUpChildren : function (n)
24979 if (!n.childNodes.length) {
24982 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24983 this.cleanUpChild(n.childNodes[i]);
24990 cleanUpChild : function (node)
24993 //console.log(node);
24994 if (node.nodeName == "#text") {
24995 // clean up silly Windows -- stuff?
24998 if (node.nodeName == "#comment") {
24999 node.parentNode.removeChild(node);
25000 // clean up silly Windows -- stuff?
25003 var lcname = node.tagName.toLowerCase();
25004 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25005 // whitelist of tags..
25007 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25009 node.parentNode.removeChild(node);
25014 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25016 // spans with no attributes - just remove them..
25017 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25018 remove_keep_children = true;
25021 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25022 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25024 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25025 // remove_keep_children = true;
25028 if (remove_keep_children) {
25029 this.cleanUpChildren(node);
25030 // inserts everything just before this node...
25031 while (node.childNodes.length) {
25032 var cn = node.childNodes[0];
25033 node.removeChild(cn);
25034 node.parentNode.insertBefore(cn, node);
25036 node.parentNode.removeChild(node);
25040 if (!node.attributes || !node.attributes.length) {
25045 this.cleanUpChildren(node);
25049 function cleanAttr(n,v)
25052 if (v.match(/^\./) || v.match(/^\//)) {
25055 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25058 if (v.match(/^#/)) {
25061 if (v.match(/^\{/)) { // allow template editing.
25064 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25065 node.removeAttribute(n);
25069 var cwhite = this.cwhite;
25070 var cblack = this.cblack;
25072 function cleanStyle(n,v)
25074 if (v.match(/expression/)) { //XSS?? should we even bother..
25075 node.removeAttribute(n);
25079 var parts = v.split(/;/);
25082 Roo.each(parts, function(p) {
25083 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25087 var l = p.split(':').shift().replace(/\s+/g,'');
25088 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25090 if ( cwhite.length && cblack.indexOf(l) > -1) {
25091 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25092 //node.removeAttribute(n);
25096 // only allow 'c whitelisted system attributes'
25097 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25098 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25099 //node.removeAttribute(n);
25109 if (clean.length) {
25110 node.setAttribute(n, clean.join(';'));
25112 node.removeAttribute(n);
25118 for (var i = node.attributes.length-1; i > -1 ; i--) {
25119 var a = node.attributes[i];
25122 if (a.name.toLowerCase().substr(0,2)=='on') {
25123 node.removeAttribute(a.name);
25126 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25127 node.removeAttribute(a.name);
25130 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25131 cleanAttr(a.name,a.value); // fixme..
25134 if (a.name == 'style') {
25135 cleanStyle(a.name,a.value);
25138 /// clean up MS crap..
25139 // tecnically this should be a list of valid class'es..
25142 if (a.name == 'class') {
25143 if (a.value.match(/^Mso/)) {
25144 node.removeAttribute('class');
25147 if (a.value.match(/^body$/)) {
25148 node.removeAttribute('class');
25159 this.cleanUpChildren(node);
25165 * Clean up MS wordisms...
25167 cleanWord : function(node)
25170 this.cleanWord(this.doc.body);
25175 node.nodeName == 'SPAN' &&
25176 !node.hasAttributes() &&
25177 node.childNodes.length == 1 &&
25178 node.firstChild.nodeName == "#text"
25180 var textNode = node.firstChild;
25181 node.removeChild(textNode);
25182 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25183 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25185 node.parentNode.insertBefore(textNode, node);
25186 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25187 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25189 node.parentNode.removeChild(node);
25192 if (node.nodeName == "#text") {
25193 // clean up silly Windows -- stuff?
25196 if (node.nodeName == "#comment") {
25197 node.parentNode.removeChild(node);
25198 // clean up silly Windows -- stuff?
25202 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25203 node.parentNode.removeChild(node);
25206 //Roo.log(node.tagName);
25207 // remove - but keep children..
25208 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25209 //Roo.log('-- removed');
25210 while (node.childNodes.length) {
25211 var cn = node.childNodes[0];
25212 node.removeChild(cn);
25213 node.parentNode.insertBefore(cn, node);
25214 // move node to parent - and clean it..
25215 this.cleanWord(cn);
25217 node.parentNode.removeChild(node);
25218 /// no need to iterate chidlren = it's got none..
25219 //this.iterateChildren(node, this.cleanWord);
25223 if (node.className.length) {
25225 var cn = node.className.split(/\W+/);
25227 Roo.each(cn, function(cls) {
25228 if (cls.match(/Mso[a-zA-Z]+/)) {
25233 node.className = cna.length ? cna.join(' ') : '';
25235 node.removeAttribute("class");
25239 if (node.hasAttribute("lang")) {
25240 node.removeAttribute("lang");
25243 if (node.hasAttribute("style")) {
25245 var styles = node.getAttribute("style").split(";");
25247 Roo.each(styles, function(s) {
25248 if (!s.match(/:/)) {
25251 var kv = s.split(":");
25252 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25255 // what ever is left... we allow.
25258 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25259 if (!nstyle.length) {
25260 node.removeAttribute('style');
25263 this.iterateChildren(node, this.cleanWord);
25269 * iterateChildren of a Node, calling fn each time, using this as the scole..
25270 * @param {DomNode} node node to iterate children of.
25271 * @param {Function} fn method of this class to call on each item.
25273 iterateChildren : function(node, fn)
25275 if (!node.childNodes.length) {
25278 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25279 fn.call(this, node.childNodes[i])
25285 * cleanTableWidths.
25287 * Quite often pasting from word etc.. results in tables with column and widths.
25288 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25291 cleanTableWidths : function(node)
25296 this.cleanTableWidths(this.doc.body);
25301 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25304 Roo.log(node.tagName);
25305 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25306 this.iterateChildren(node, this.cleanTableWidths);
25309 if (node.hasAttribute('width')) {
25310 node.removeAttribute('width');
25314 if (node.hasAttribute("style")) {
25317 var styles = node.getAttribute("style").split(";");
25319 Roo.each(styles, function(s) {
25320 if (!s.match(/:/)) {
25323 var kv = s.split(":");
25324 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25327 // what ever is left... we allow.
25330 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25331 if (!nstyle.length) {
25332 node.removeAttribute('style');
25336 this.iterateChildren(node, this.cleanTableWidths);
25344 domToHTML : function(currentElement, depth, nopadtext) {
25346 depth = depth || 0;
25347 nopadtext = nopadtext || false;
25349 if (!currentElement) {
25350 return this.domToHTML(this.doc.body);
25353 //Roo.log(currentElement);
25355 var allText = false;
25356 var nodeName = currentElement.nodeName;
25357 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25359 if (nodeName == '#text') {
25361 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25366 if (nodeName != 'BODY') {
25369 // Prints the node tagName, such as <A>, <IMG>, etc
25372 for(i = 0; i < currentElement.attributes.length;i++) {
25374 var aname = currentElement.attributes.item(i).name;
25375 if (!currentElement.attributes.item(i).value.length) {
25378 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25381 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25390 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25393 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25398 // Traverse the tree
25400 var currentElementChild = currentElement.childNodes.item(i);
25401 var allText = true;
25402 var innerHTML = '';
25404 while (currentElementChild) {
25405 // Formatting code (indent the tree so it looks nice on the screen)
25406 var nopad = nopadtext;
25407 if (lastnode == 'SPAN') {
25411 if (currentElementChild.nodeName == '#text') {
25412 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25413 toadd = nopadtext ? toadd : toadd.trim();
25414 if (!nopad && toadd.length > 80) {
25415 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25417 innerHTML += toadd;
25420 currentElementChild = currentElement.childNodes.item(i);
25426 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25428 // Recursively traverse the tree structure of the child node
25429 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25430 lastnode = currentElementChild.nodeName;
25432 currentElementChild=currentElement.childNodes.item(i);
25438 // The remaining code is mostly for formatting the tree
25439 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25444 ret+= "</"+tagName+">";
25450 applyBlacklists : function()
25452 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25453 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25457 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25458 if (b.indexOf(tag) > -1) {
25461 this.white.push(tag);
25465 Roo.each(w, function(tag) {
25466 if (b.indexOf(tag) > -1) {
25469 if (this.white.indexOf(tag) > -1) {
25472 this.white.push(tag);
25477 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25478 if (w.indexOf(tag) > -1) {
25481 this.black.push(tag);
25485 Roo.each(b, function(tag) {
25486 if (w.indexOf(tag) > -1) {
25489 if (this.black.indexOf(tag) > -1) {
25492 this.black.push(tag);
25497 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25498 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25502 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25503 if (b.indexOf(tag) > -1) {
25506 this.cwhite.push(tag);
25510 Roo.each(w, function(tag) {
25511 if (b.indexOf(tag) > -1) {
25514 if (this.cwhite.indexOf(tag) > -1) {
25517 this.cwhite.push(tag);
25522 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25523 if (w.indexOf(tag) > -1) {
25526 this.cblack.push(tag);
25530 Roo.each(b, function(tag) {
25531 if (w.indexOf(tag) > -1) {
25534 if (this.cblack.indexOf(tag) > -1) {
25537 this.cblack.push(tag);
25542 setStylesheets : function(stylesheets)
25544 if(typeof(stylesheets) == 'string'){
25545 Roo.get(this.iframe.contentDocument.head).createChild({
25547 rel : 'stylesheet',
25556 Roo.each(stylesheets, function(s) {
25561 Roo.get(_this.iframe.contentDocument.head).createChild({
25563 rel : 'stylesheet',
25572 removeStylesheets : function()
25576 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25581 setStyle : function(style)
25583 Roo.get(this.iframe.contentDocument.head).createChild({
25592 // hide stuff that is not compatible
25606 * @event specialkey
25610 * @cfg {String} fieldClass @hide
25613 * @cfg {String} focusClass @hide
25616 * @cfg {String} autoCreate @hide
25619 * @cfg {String} inputType @hide
25622 * @cfg {String} invalidClass @hide
25625 * @cfg {String} invalidText @hide
25628 * @cfg {String} msgFx @hide
25631 * @cfg {String} validateOnBlur @hide
25635 Roo.HtmlEditorCore.white = [
25636 'area', 'br', 'img', 'input', 'hr', 'wbr',
25638 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25639 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25640 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25641 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25642 'table', 'ul', 'xmp',
25644 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25647 'dir', 'menu', 'ol', 'ul', 'dl',
25653 Roo.HtmlEditorCore.black = [
25654 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25656 'base', 'basefont', 'bgsound', 'blink', 'body',
25657 'frame', 'frameset', 'head', 'html', 'ilayer',
25658 'iframe', 'layer', 'link', 'meta', 'object',
25659 'script', 'style' ,'title', 'xml' // clean later..
25661 Roo.HtmlEditorCore.clean = [
25662 'script', 'style', 'title', 'xml'
25664 Roo.HtmlEditorCore.remove = [
25669 Roo.HtmlEditorCore.ablack = [
25673 Roo.HtmlEditorCore.aclean = [
25674 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25678 Roo.HtmlEditorCore.pwhite= [
25679 'http', 'https', 'mailto'
25682 // white listed style attributes.
25683 Roo.HtmlEditorCore.cwhite= [
25684 // 'text-align', /// default is to allow most things..
25690 // black listed style attributes.
25691 Roo.HtmlEditorCore.cblack= [
25692 // 'font-size' -- this can be set by the project
25696 Roo.HtmlEditorCore.swapCodes =[
25715 * @class Roo.bootstrap.HtmlEditor
25716 * @extends Roo.bootstrap.TextArea
25717 * Bootstrap HtmlEditor class
25720 * Create a new HtmlEditor
25721 * @param {Object} config The config object
25724 Roo.bootstrap.HtmlEditor = function(config){
25725 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25726 if (!this.toolbars) {
25727 this.toolbars = [];
25730 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25733 * @event initialize
25734 * Fires when the editor is fully initialized (including the iframe)
25735 * @param {HtmlEditor} this
25740 * Fires when the editor is first receives the focus. Any insertion must wait
25741 * until after this event.
25742 * @param {HtmlEditor} this
25746 * @event beforesync
25747 * Fires before the textarea is updated with content from the editor iframe. Return false
25748 * to cancel the sync.
25749 * @param {HtmlEditor} this
25750 * @param {String} html
25754 * @event beforepush
25755 * Fires before the iframe editor is updated with content from the textarea. Return false
25756 * to cancel the push.
25757 * @param {HtmlEditor} this
25758 * @param {String} html
25763 * Fires when the textarea is updated with content from the editor iframe.
25764 * @param {HtmlEditor} this
25765 * @param {String} html
25770 * Fires when the iframe editor is updated with content from the textarea.
25771 * @param {HtmlEditor} this
25772 * @param {String} html
25776 * @event editmodechange
25777 * Fires when the editor switches edit modes
25778 * @param {HtmlEditor} this
25779 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25781 editmodechange: true,
25783 * @event editorevent
25784 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25785 * @param {HtmlEditor} this
25789 * @event firstfocus
25790 * Fires when on first focus - needed by toolbars..
25791 * @param {HtmlEditor} this
25796 * Auto save the htmlEditor value as a file into Events
25797 * @param {HtmlEditor} this
25801 * @event savedpreview
25802 * preview the saved version of htmlEditor
25803 * @param {HtmlEditor} this
25810 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25814 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25819 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25824 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25829 * @cfg {Number} height (in pixels)
25833 * @cfg {Number} width (in pixels)
25838 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25841 stylesheets: false,
25846 // private properties
25847 validationEvent : false,
25849 initialized : false,
25852 onFocus : Roo.emptyFn,
25854 hideMode:'offsets',
25856 tbContainer : false,
25860 toolbarContainer :function() {
25861 return this.wrap.select('.x-html-editor-tb',true).first();
25865 * Protected method that will not generally be called directly. It
25866 * is called when the editor creates its toolbar. Override this method if you need to
25867 * add custom toolbar buttons.
25868 * @param {HtmlEditor} editor
25870 createToolbar : function(){
25871 Roo.log('renewing');
25872 Roo.log("create toolbars");
25874 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25875 this.toolbars[0].render(this.toolbarContainer());
25879 // if (!editor.toolbars || !editor.toolbars.length) {
25880 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25883 // for (var i =0 ; i < editor.toolbars.length;i++) {
25884 // editor.toolbars[i] = Roo.factory(
25885 // typeof(editor.toolbars[i]) == 'string' ?
25886 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25887 // Roo.bootstrap.HtmlEditor);
25888 // editor.toolbars[i].init(editor);
25894 onRender : function(ct, position)
25896 // Roo.log("Call onRender: " + this.xtype);
25898 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25900 this.wrap = this.inputEl().wrap({
25901 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25904 this.editorcore.onRender(ct, position);
25906 if (this.resizable) {
25907 this.resizeEl = new Roo.Resizable(this.wrap, {
25911 minHeight : this.height,
25912 height: this.height,
25913 handles : this.resizable,
25916 resize : function(r, w, h) {
25917 _t.onResize(w,h); // -something
25923 this.createToolbar(this);
25926 if(!this.width && this.resizable){
25927 this.setSize(this.wrap.getSize());
25929 if (this.resizeEl) {
25930 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25931 // should trigger onReize..
25937 onResize : function(w, h)
25939 Roo.log('resize: ' +w + ',' + h );
25940 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25944 if(this.inputEl() ){
25945 if(typeof w == 'number'){
25946 var aw = w - this.wrap.getFrameWidth('lr');
25947 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25950 if(typeof h == 'number'){
25951 var tbh = -11; // fixme it needs to tool bar size!
25952 for (var i =0; i < this.toolbars.length;i++) {
25953 // fixme - ask toolbars for heights?
25954 tbh += this.toolbars[i].el.getHeight();
25955 //if (this.toolbars[i].footer) {
25956 // tbh += this.toolbars[i].footer.el.getHeight();
25964 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25965 ah -= 5; // knock a few pixes off for look..
25966 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25970 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25971 this.editorcore.onResize(ew,eh);
25976 * Toggles the editor between standard and source edit mode.
25977 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25979 toggleSourceEdit : function(sourceEditMode)
25981 this.editorcore.toggleSourceEdit(sourceEditMode);
25983 if(this.editorcore.sourceEditMode){
25984 Roo.log('editor - showing textarea');
25987 // Roo.log(this.syncValue());
25989 this.inputEl().removeClass(['hide', 'x-hidden']);
25990 this.inputEl().dom.removeAttribute('tabIndex');
25991 this.inputEl().focus();
25993 Roo.log('editor - hiding textarea');
25995 // Roo.log(this.pushValue());
25998 this.inputEl().addClass(['hide', 'x-hidden']);
25999 this.inputEl().dom.setAttribute('tabIndex', -1);
26000 //this.deferFocus();
26003 if(this.resizable){
26004 this.setSize(this.wrap.getSize());
26007 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26010 // private (for BoxComponent)
26011 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26013 // private (for BoxComponent)
26014 getResizeEl : function(){
26018 // private (for BoxComponent)
26019 getPositionEl : function(){
26024 initEvents : function(){
26025 this.originalValue = this.getValue();
26029 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26032 // markInvalid : Roo.emptyFn,
26034 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26037 // clearInvalid : Roo.emptyFn,
26039 setValue : function(v){
26040 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26041 this.editorcore.pushValue();
26046 deferFocus : function(){
26047 this.focus.defer(10, this);
26051 focus : function(){
26052 this.editorcore.focus();
26058 onDestroy : function(){
26064 for (var i =0; i < this.toolbars.length;i++) {
26065 // fixme - ask toolbars for heights?
26066 this.toolbars[i].onDestroy();
26069 this.wrap.dom.innerHTML = '';
26070 this.wrap.remove();
26075 onFirstFocus : function(){
26076 //Roo.log("onFirstFocus");
26077 this.editorcore.onFirstFocus();
26078 for (var i =0; i < this.toolbars.length;i++) {
26079 this.toolbars[i].onFirstFocus();
26085 syncValue : function()
26087 this.editorcore.syncValue();
26090 pushValue : function()
26092 this.editorcore.pushValue();
26096 // hide stuff that is not compatible
26110 * @event specialkey
26114 * @cfg {String} fieldClass @hide
26117 * @cfg {String} focusClass @hide
26120 * @cfg {String} autoCreate @hide
26123 * @cfg {String} inputType @hide
26127 * @cfg {String} invalidText @hide
26130 * @cfg {String} msgFx @hide
26133 * @cfg {String} validateOnBlur @hide
26142 Roo.namespace('Roo.bootstrap.htmleditor');
26144 * @class Roo.bootstrap.HtmlEditorToolbar1
26150 new Roo.bootstrap.HtmlEditor({
26153 new Roo.bootstrap.HtmlEditorToolbar1({
26154 disable : { fonts: 1 , format: 1, ..., ... , ...],
26160 * @cfg {Object} disable List of elements to disable..
26161 * @cfg {Array} btns List of additional buttons.
26165 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26168 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26171 Roo.apply(this, config);
26173 // default disabled, based on 'good practice'..
26174 this.disable = this.disable || {};
26175 Roo.applyIf(this.disable, {
26178 specialElements : true
26180 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26182 this.editor = config.editor;
26183 this.editorcore = config.editor.editorcore;
26185 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26187 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26188 // dont call parent... till later.
26190 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26195 editorcore : false,
26200 "h1","h2","h3","h4","h5","h6",
26202 "abbr", "acronym", "address", "cite", "samp", "var",
26206 onRender : function(ct, position)
26208 // Roo.log("Call onRender: " + this.xtype);
26210 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26212 this.el.dom.style.marginBottom = '0';
26214 var editorcore = this.editorcore;
26215 var editor= this.editor;
26218 var btn = function(id,cmd , toggle, handler, html){
26220 var event = toggle ? 'toggle' : 'click';
26225 xns: Roo.bootstrap,
26229 enableToggle:toggle !== false,
26231 pressed : toggle ? false : null,
26234 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26235 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26241 // var cb_box = function...
26246 xns: Roo.bootstrap,
26251 xns: Roo.bootstrap,
26255 Roo.each(this.formats, function(f) {
26256 style.menu.items.push({
26258 xns: Roo.bootstrap,
26259 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26264 editorcore.insertTag(this.tagname);
26271 children.push(style);
26273 btn('bold',false,true);
26274 btn('italic',false,true);
26275 btn('align-left', 'justifyleft',true);
26276 btn('align-center', 'justifycenter',true);
26277 btn('align-right' , 'justifyright',true);
26278 btn('link', false, false, function(btn) {
26279 //Roo.log("create link?");
26280 var url = prompt(this.createLinkText, this.defaultLinkValue);
26281 if(url && url != 'http:/'+'/'){
26282 this.editorcore.relayCmd('createlink', url);
26285 btn('list','insertunorderedlist',true);
26286 btn('pencil', false,true, function(btn){
26288 this.toggleSourceEdit(btn.pressed);
26291 if (this.editor.btns.length > 0) {
26292 for (var i = 0; i<this.editor.btns.length; i++) {
26293 children.push(this.editor.btns[i]);
26301 xns: Roo.bootstrap,
26306 xns: Roo.bootstrap,
26311 cog.menu.items.push({
26313 xns: Roo.bootstrap,
26314 html : Clean styles,
26319 editorcore.insertTag(this.tagname);
26328 this.xtype = 'NavSimplebar';
26330 for(var i=0;i< children.length;i++) {
26332 this.buttons.add(this.addxtypeChild(children[i]));
26336 editor.on('editorevent', this.updateToolbar, this);
26338 onBtnClick : function(id)
26340 this.editorcore.relayCmd(id);
26341 this.editorcore.focus();
26345 * Protected method that will not generally be called directly. It triggers
26346 * a toolbar update by reading the markup state of the current selection in the editor.
26348 updateToolbar: function(){
26350 if(!this.editorcore.activated){
26351 this.editor.onFirstFocus(); // is this neeed?
26355 var btns = this.buttons;
26356 var doc = this.editorcore.doc;
26357 btns.get('bold').setActive(doc.queryCommandState('bold'));
26358 btns.get('italic').setActive(doc.queryCommandState('italic'));
26359 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26361 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26362 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26363 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26365 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26366 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26369 var ans = this.editorcore.getAllAncestors();
26370 if (this.formatCombo) {
26373 var store = this.formatCombo.store;
26374 this.formatCombo.setValue("");
26375 for (var i =0; i < ans.length;i++) {
26376 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26378 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26386 // hides menus... - so this cant be on a menu...
26387 Roo.bootstrap.MenuMgr.hideAll();
26389 Roo.bootstrap.MenuMgr.hideAll();
26390 //this.editorsyncValue();
26392 onFirstFocus: function() {
26393 this.buttons.each(function(item){
26397 toggleSourceEdit : function(sourceEditMode){
26400 if(sourceEditMode){
26401 Roo.log("disabling buttons");
26402 this.buttons.each( function(item){
26403 if(item.cmd != 'pencil'){
26409 Roo.log("enabling buttons");
26410 if(this.editorcore.initialized){
26411 this.buttons.each( function(item){
26417 Roo.log("calling toggole on editor");
26418 // tell the editor that it's been pressed..
26419 this.editor.toggleSourceEdit(sourceEditMode);
26433 * @class Roo.bootstrap.Markdown
26434 * @extends Roo.bootstrap.TextArea
26435 * Bootstrap Showdown editable area
26436 * @cfg {string} content
26439 * Create a new Showdown
26442 Roo.bootstrap.Markdown = function(config){
26443 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26447 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26451 initEvents : function()
26454 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26455 this.markdownEl = this.el.createChild({
26456 cls : 'roo-markdown-area'
26458 this.inputEl().addClass('d-none');
26459 if (this.getValue() == '') {
26460 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26463 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26465 this.markdownEl.on('click', this.toggleTextEdit, this);
26466 this.on('blur', this.toggleTextEdit, this);
26467 this.on('specialkey', this.resizeTextArea, this);
26470 toggleTextEdit : function()
26472 var sh = this.markdownEl.getHeight();
26473 this.inputEl().addClass('d-none');
26474 this.markdownEl.addClass('d-none');
26475 if (!this.editing) {
26477 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26478 this.inputEl().removeClass('d-none');
26479 this.inputEl().focus();
26480 this.editing = true;
26483 // show showdown...
26484 this.updateMarkdown();
26485 this.markdownEl.removeClass('d-none');
26486 this.editing = false;
26489 updateMarkdown : function()
26491 if (this.getValue() == '') {
26492 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26496 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26499 resizeTextArea: function () {
26502 Roo.log([sh, this.getValue().split("\n").length * 30]);
26503 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26505 setValue : function(val)
26507 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26508 if (!this.editing) {
26509 this.updateMarkdown();
26515 if (!this.editing) {
26516 this.toggleTextEdit();
26524 * @class Roo.bootstrap.Table.AbstractSelectionModel
26525 * @extends Roo.util.Observable
26526 * Abstract base class for grid SelectionModels. It provides the interface that should be
26527 * implemented by descendant classes. This class should not be directly instantiated.
26530 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26531 this.locked = false;
26532 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26536 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26537 /** @ignore Called by the grid automatically. Do not call directly. */
26538 init : function(grid){
26544 * Locks the selections.
26547 this.locked = true;
26551 * Unlocks the selections.
26553 unlock : function(){
26554 this.locked = false;
26558 * Returns true if the selections are locked.
26559 * @return {Boolean}
26561 isLocked : function(){
26562 return this.locked;
26566 initEvents : function ()
26572 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26573 * @class Roo.bootstrap.Table.RowSelectionModel
26574 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26575 * It supports multiple selections and keyboard selection/navigation.
26577 * @param {Object} config
26580 Roo.bootstrap.Table.RowSelectionModel = function(config){
26581 Roo.apply(this, config);
26582 this.selections = new Roo.util.MixedCollection(false, function(o){
26587 this.lastActive = false;
26591 * @event selectionchange
26592 * Fires when the selection changes
26593 * @param {SelectionModel} this
26595 "selectionchange" : true,
26597 * @event afterselectionchange
26598 * Fires after the selection changes (eg. by key press or clicking)
26599 * @param {SelectionModel} this
26601 "afterselectionchange" : true,
26603 * @event beforerowselect
26604 * Fires when a row is selected being selected, return false to cancel.
26605 * @param {SelectionModel} this
26606 * @param {Number} rowIndex The selected index
26607 * @param {Boolean} keepExisting False if other selections will be cleared
26609 "beforerowselect" : true,
26612 * Fires when a row is selected.
26613 * @param {SelectionModel} this
26614 * @param {Number} rowIndex The selected index
26615 * @param {Roo.data.Record} r The record
26617 "rowselect" : true,
26619 * @event rowdeselect
26620 * Fires when a row is deselected.
26621 * @param {SelectionModel} this
26622 * @param {Number} rowIndex The selected index
26624 "rowdeselect" : true
26626 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26627 this.locked = false;
26630 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26632 * @cfg {Boolean} singleSelect
26633 * True to allow selection of only one row at a time (defaults to false)
26635 singleSelect : false,
26638 initEvents : function()
26641 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26642 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26643 //}else{ // allow click to work like normal
26644 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26646 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26647 this.grid.on("rowclick", this.handleMouseDown, this);
26649 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26650 "up" : function(e){
26652 this.selectPrevious(e.shiftKey);
26653 }else if(this.last !== false && this.lastActive !== false){
26654 var last = this.last;
26655 this.selectRange(this.last, this.lastActive-1);
26656 this.grid.getView().focusRow(this.lastActive);
26657 if(last !== false){
26661 this.selectFirstRow();
26663 this.fireEvent("afterselectionchange", this);
26665 "down" : function(e){
26667 this.selectNext(e.shiftKey);
26668 }else if(this.last !== false && this.lastActive !== false){
26669 var last = this.last;
26670 this.selectRange(this.last, this.lastActive+1);
26671 this.grid.getView().focusRow(this.lastActive);
26672 if(last !== false){
26676 this.selectFirstRow();
26678 this.fireEvent("afterselectionchange", this);
26682 this.grid.store.on('load', function(){
26683 this.selections.clear();
26686 var view = this.grid.view;
26687 view.on("refresh", this.onRefresh, this);
26688 view.on("rowupdated", this.onRowUpdated, this);
26689 view.on("rowremoved", this.onRemove, this);
26694 onRefresh : function()
26696 var ds = this.grid.store, i, v = this.grid.view;
26697 var s = this.selections;
26698 s.each(function(r){
26699 if((i = ds.indexOfId(r.id)) != -1){
26708 onRemove : function(v, index, r){
26709 this.selections.remove(r);
26713 onRowUpdated : function(v, index, r){
26714 if(this.isSelected(r)){
26715 v.onRowSelect(index);
26721 * @param {Array} records The records to select
26722 * @param {Boolean} keepExisting (optional) True to keep existing selections
26724 selectRecords : function(records, keepExisting)
26727 this.clearSelections();
26729 var ds = this.grid.store;
26730 for(var i = 0, len = records.length; i < len; i++){
26731 this.selectRow(ds.indexOf(records[i]), true);
26736 * Gets the number of selected rows.
26739 getCount : function(){
26740 return this.selections.length;
26744 * Selects the first row in the grid.
26746 selectFirstRow : function(){
26751 * Select the last row.
26752 * @param {Boolean} keepExisting (optional) True to keep existing selections
26754 selectLastRow : function(keepExisting){
26755 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26756 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26760 * Selects the row immediately following the last selected row.
26761 * @param {Boolean} keepExisting (optional) True to keep existing selections
26763 selectNext : function(keepExisting)
26765 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26766 this.selectRow(this.last+1, keepExisting);
26767 this.grid.getView().focusRow(this.last);
26772 * Selects the row that precedes the last selected row.
26773 * @param {Boolean} keepExisting (optional) True to keep existing selections
26775 selectPrevious : function(keepExisting){
26777 this.selectRow(this.last-1, keepExisting);
26778 this.grid.getView().focusRow(this.last);
26783 * Returns the selected records
26784 * @return {Array} Array of selected records
26786 getSelections : function(){
26787 return [].concat(this.selections.items);
26791 * Returns the first selected record.
26794 getSelected : function(){
26795 return this.selections.itemAt(0);
26800 * Clears all selections.
26802 clearSelections : function(fast)
26808 var ds = this.grid.store;
26809 var s = this.selections;
26810 s.each(function(r){
26811 this.deselectRow(ds.indexOfId(r.id));
26815 this.selections.clear();
26822 * Selects all rows.
26824 selectAll : function(){
26828 this.selections.clear();
26829 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26830 this.selectRow(i, true);
26835 * Returns True if there is a selection.
26836 * @return {Boolean}
26838 hasSelection : function(){
26839 return this.selections.length > 0;
26843 * Returns True if the specified row is selected.
26844 * @param {Number/Record} record The record or index of the record to check
26845 * @return {Boolean}
26847 isSelected : function(index){
26848 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26849 return (r && this.selections.key(r.id) ? true : false);
26853 * Returns True if the specified record id is selected.
26854 * @param {String} id The id of record to check
26855 * @return {Boolean}
26857 isIdSelected : function(id){
26858 return (this.selections.key(id) ? true : false);
26863 handleMouseDBClick : function(e, t){
26867 handleMouseDown : function(e, t)
26869 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26870 if(this.isLocked() || rowIndex < 0 ){
26873 if(e.shiftKey && this.last !== false){
26874 var last = this.last;
26875 this.selectRange(last, rowIndex, e.ctrlKey);
26876 this.last = last; // reset the last
26880 var isSelected = this.isSelected(rowIndex);
26881 //Roo.log("select row:" + rowIndex);
26883 this.deselectRow(rowIndex);
26885 this.selectRow(rowIndex, true);
26889 if(e.button !== 0 && isSelected){
26890 alert('rowIndex 2: ' + rowIndex);
26891 view.focusRow(rowIndex);
26892 }else if(e.ctrlKey && isSelected){
26893 this.deselectRow(rowIndex);
26894 }else if(!isSelected){
26895 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26896 view.focusRow(rowIndex);
26900 this.fireEvent("afterselectionchange", this);
26903 handleDragableRowClick : function(grid, rowIndex, e)
26905 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26906 this.selectRow(rowIndex, false);
26907 grid.view.focusRow(rowIndex);
26908 this.fireEvent("afterselectionchange", this);
26913 * Selects multiple rows.
26914 * @param {Array} rows Array of the indexes of the row to select
26915 * @param {Boolean} keepExisting (optional) True to keep existing selections
26917 selectRows : function(rows, keepExisting){
26919 this.clearSelections();
26921 for(var i = 0, len = rows.length; i < len; i++){
26922 this.selectRow(rows[i], true);
26927 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26928 * @param {Number} startRow The index of the first row in the range
26929 * @param {Number} endRow The index of the last row in the range
26930 * @param {Boolean} keepExisting (optional) True to retain existing selections
26932 selectRange : function(startRow, endRow, keepExisting){
26937 this.clearSelections();
26939 if(startRow <= endRow){
26940 for(var i = startRow; i <= endRow; i++){
26941 this.selectRow(i, true);
26944 for(var i = startRow; i >= endRow; i--){
26945 this.selectRow(i, true);
26951 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26952 * @param {Number} startRow The index of the first row in the range
26953 * @param {Number} endRow The index of the last row in the range
26955 deselectRange : function(startRow, endRow, preventViewNotify){
26959 for(var i = startRow; i <= endRow; i++){
26960 this.deselectRow(i, preventViewNotify);
26966 * @param {Number} row The index of the row to select
26967 * @param {Boolean} keepExisting (optional) True to keep existing selections
26969 selectRow : function(index, keepExisting, preventViewNotify)
26971 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26974 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26975 if(!keepExisting || this.singleSelect){
26976 this.clearSelections();
26979 var r = this.grid.store.getAt(index);
26980 //console.log('selectRow - record id :' + r.id);
26982 this.selections.add(r);
26983 this.last = this.lastActive = index;
26984 if(!preventViewNotify){
26985 var proxy = new Roo.Element(
26986 this.grid.getRowDom(index)
26988 proxy.addClass('bg-info info');
26990 this.fireEvent("rowselect", this, index, r);
26991 this.fireEvent("selectionchange", this);
26997 * @param {Number} row The index of the row to deselect
26999 deselectRow : function(index, preventViewNotify)
27004 if(this.last == index){
27007 if(this.lastActive == index){
27008 this.lastActive = false;
27011 var r = this.grid.store.getAt(index);
27016 this.selections.remove(r);
27017 //.console.log('deselectRow - record id :' + r.id);
27018 if(!preventViewNotify){
27020 var proxy = new Roo.Element(
27021 this.grid.getRowDom(index)
27023 proxy.removeClass('bg-info info');
27025 this.fireEvent("rowdeselect", this, index);
27026 this.fireEvent("selectionchange", this);
27030 restoreLast : function(){
27032 this.last = this._last;
27037 acceptsNav : function(row, col, cm){
27038 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27042 onEditorKey : function(field, e){
27043 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27048 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27050 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27052 }else if(k == e.ENTER && !e.ctrlKey){
27056 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27058 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27060 }else if(k == e.ESC){
27064 g.startEditing(newCell[0], newCell[1]);
27070 * Ext JS Library 1.1.1
27071 * Copyright(c) 2006-2007, Ext JS, LLC.
27073 * Originally Released Under LGPL - original licence link has changed is not relivant.
27076 * <script type="text/javascript">
27080 * @class Roo.bootstrap.PagingToolbar
27081 * @extends Roo.bootstrap.NavSimplebar
27082 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27084 * Create a new PagingToolbar
27085 * @param {Object} config The config object
27086 * @param {Roo.data.Store} store
27088 Roo.bootstrap.PagingToolbar = function(config)
27090 // old args format still supported... - xtype is prefered..
27091 // created from xtype...
27093 this.ds = config.dataSource;
27095 if (config.store && !this.ds) {
27096 this.store= Roo.factory(config.store, Roo.data);
27097 this.ds = this.store;
27098 this.ds.xmodule = this.xmodule || false;
27101 this.toolbarItems = [];
27102 if (config.items) {
27103 this.toolbarItems = config.items;
27106 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27111 this.bind(this.ds);
27114 if (Roo.bootstrap.version == 4) {
27115 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27117 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27122 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27124 * @cfg {Roo.data.Store} dataSource
27125 * The underlying data store providing the paged data
27128 * @cfg {String/HTMLElement/Element} container
27129 * container The id or element that will contain the toolbar
27132 * @cfg {Boolean} displayInfo
27133 * True to display the displayMsg (defaults to false)
27136 * @cfg {Number} pageSize
27137 * The number of records to display per page (defaults to 20)
27141 * @cfg {String} displayMsg
27142 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27144 displayMsg : 'Displaying {0} - {1} of {2}',
27146 * @cfg {String} emptyMsg
27147 * The message to display when no records are found (defaults to "No data to display")
27149 emptyMsg : 'No data to display',
27151 * Customizable piece of the default paging text (defaults to "Page")
27154 beforePageText : "Page",
27156 * Customizable piece of the default paging text (defaults to "of %0")
27159 afterPageText : "of {0}",
27161 * Customizable piece of the default paging text (defaults to "First Page")
27164 firstText : "First Page",
27166 * Customizable piece of the default paging text (defaults to "Previous Page")
27169 prevText : "Previous Page",
27171 * Customizable piece of the default paging text (defaults to "Next Page")
27174 nextText : "Next Page",
27176 * Customizable piece of the default paging text (defaults to "Last Page")
27179 lastText : "Last Page",
27181 * Customizable piece of the default paging text (defaults to "Refresh")
27184 refreshText : "Refresh",
27188 onRender : function(ct, position)
27190 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27191 this.navgroup.parentId = this.id;
27192 this.navgroup.onRender(this.el, null);
27193 // add the buttons to the navgroup
27195 if(this.displayInfo){
27196 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27197 this.displayEl = this.el.select('.x-paging-info', true).first();
27198 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27199 // this.displayEl = navel.el.select('span',true).first();
27205 Roo.each(_this.buttons, function(e){ // this might need to use render????
27206 Roo.factory(e).render(_this.el);
27210 Roo.each(_this.toolbarItems, function(e) {
27211 _this.navgroup.addItem(e);
27215 this.first = this.navgroup.addItem({
27216 tooltip: this.firstText,
27217 cls: "prev btn-outline-secondary",
27218 html : ' <i class="fa fa-step-backward"></i>',
27220 preventDefault: true,
27221 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27224 this.prev = this.navgroup.addItem({
27225 tooltip: this.prevText,
27226 cls: "prev btn-outline-secondary",
27227 html : ' <i class="fa fa-backward"></i>',
27229 preventDefault: true,
27230 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27232 //this.addSeparator();
27235 var field = this.navgroup.addItem( {
27237 cls : 'x-paging-position btn-outline-secondary',
27239 html : this.beforePageText +
27240 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27241 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27244 this.field = field.el.select('input', true).first();
27245 this.field.on("keydown", this.onPagingKeydown, this);
27246 this.field.on("focus", function(){this.dom.select();});
27249 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27250 //this.field.setHeight(18);
27251 //this.addSeparator();
27252 this.next = this.navgroup.addItem({
27253 tooltip: this.nextText,
27254 cls: "next btn-outline-secondary",
27255 html : ' <i class="fa fa-forward"></i>',
27257 preventDefault: true,
27258 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27260 this.last = this.navgroup.addItem({
27261 tooltip: this.lastText,
27262 html : ' <i class="fa fa-step-forward"></i>',
27263 cls: "next btn-outline-secondary",
27265 preventDefault: true,
27266 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27268 //this.addSeparator();
27269 this.loading = this.navgroup.addItem({
27270 tooltip: this.refreshText,
27271 cls: "btn-outline-secondary",
27272 html : ' <i class="fa fa-refresh"></i>',
27273 preventDefault: true,
27274 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27280 updateInfo : function(){
27281 if(this.displayEl){
27282 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27283 var msg = count == 0 ?
27287 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27289 this.displayEl.update(msg);
27294 onLoad : function(ds, r, o)
27296 this.cursor = o.params.start ? o.params.start : 0;
27298 var d = this.getPageData(),
27303 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27304 this.field.dom.value = ap;
27305 this.first.setDisabled(ap == 1);
27306 this.prev.setDisabled(ap == 1);
27307 this.next.setDisabled(ap == ps);
27308 this.last.setDisabled(ap == ps);
27309 this.loading.enable();
27314 getPageData : function(){
27315 var total = this.ds.getTotalCount();
27318 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27319 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27324 onLoadError : function(){
27325 this.loading.enable();
27329 onPagingKeydown : function(e){
27330 var k = e.getKey();
27331 var d = this.getPageData();
27333 var v = this.field.dom.value, pageNum;
27334 if(!v || isNaN(pageNum = parseInt(v, 10))){
27335 this.field.dom.value = d.activePage;
27338 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27339 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27342 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))
27344 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27345 this.field.dom.value = pageNum;
27346 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27349 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27351 var v = this.field.dom.value, pageNum;
27352 var increment = (e.shiftKey) ? 10 : 1;
27353 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27356 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27357 this.field.dom.value = d.activePage;
27360 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27362 this.field.dom.value = parseInt(v, 10) + increment;
27363 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27364 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27371 beforeLoad : function(){
27373 this.loading.disable();
27378 onClick : function(which){
27387 ds.load({params:{start: 0, limit: this.pageSize}});
27390 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27393 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27396 var total = ds.getTotalCount();
27397 var extra = total % this.pageSize;
27398 var lastStart = extra ? (total - extra) : total-this.pageSize;
27399 ds.load({params:{start: lastStart, limit: this.pageSize}});
27402 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27408 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27409 * @param {Roo.data.Store} store The data store to unbind
27411 unbind : function(ds){
27412 ds.un("beforeload", this.beforeLoad, this);
27413 ds.un("load", this.onLoad, this);
27414 ds.un("loadexception", this.onLoadError, this);
27415 ds.un("remove", this.updateInfo, this);
27416 ds.un("add", this.updateInfo, this);
27417 this.ds = undefined;
27421 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27422 * @param {Roo.data.Store} store The data store to bind
27424 bind : function(ds){
27425 ds.on("beforeload", this.beforeLoad, this);
27426 ds.on("load", this.onLoad, this);
27427 ds.on("loadexception", this.onLoadError, this);
27428 ds.on("remove", this.updateInfo, this);
27429 ds.on("add", this.updateInfo, this);
27440 * @class Roo.bootstrap.MessageBar
27441 * @extends Roo.bootstrap.Component
27442 * Bootstrap MessageBar class
27443 * @cfg {String} html contents of the MessageBar
27444 * @cfg {String} weight (info | success | warning | danger) default info
27445 * @cfg {String} beforeClass insert the bar before the given class
27446 * @cfg {Boolean} closable (true | false) default false
27447 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27450 * Create a new Element
27451 * @param {Object} config The config object
27454 Roo.bootstrap.MessageBar = function(config){
27455 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27458 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27464 beforeClass: 'bootstrap-sticky-wrap',
27466 getAutoCreate : function(){
27470 cls: 'alert alert-dismissable alert-' + this.weight,
27475 html: this.html || ''
27481 cfg.cls += ' alert-messages-fixed';
27495 onRender : function(ct, position)
27497 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27500 var cfg = Roo.apply({}, this.getAutoCreate());
27504 cfg.cls += ' ' + this.cls;
27507 cfg.style = this.style;
27509 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27511 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27514 this.el.select('>button.close').on('click', this.hide, this);
27520 if (!this.rendered) {
27526 this.fireEvent('show', this);
27532 if (!this.rendered) {
27538 this.fireEvent('hide', this);
27541 update : function()
27543 // var e = this.el.dom.firstChild;
27545 // if(this.closable){
27546 // e = e.nextSibling;
27549 // e.data = this.html || '';
27551 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27567 * @class Roo.bootstrap.Graph
27568 * @extends Roo.bootstrap.Component
27569 * Bootstrap Graph class
27573 @cfg {String} graphtype bar | vbar | pie
27574 @cfg {number} g_x coodinator | centre x (pie)
27575 @cfg {number} g_y coodinator | centre y (pie)
27576 @cfg {number} g_r radius (pie)
27577 @cfg {number} g_height height of the chart (respected by all elements in the set)
27578 @cfg {number} g_width width of the chart (respected by all elements in the set)
27579 @cfg {Object} title The title of the chart
27582 -opts (object) options for the chart
27584 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27585 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27587 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.
27588 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27590 o stretch (boolean)
27592 -opts (object) options for the pie
27595 o startAngle (number)
27596 o endAngle (number)
27600 * Create a new Input
27601 * @param {Object} config The config object
27604 Roo.bootstrap.Graph = function(config){
27605 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27611 * The img click event for the img.
27612 * @param {Roo.EventObject} e
27618 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27629 //g_colors: this.colors,
27636 getAutoCreate : function(){
27647 onRender : function(ct,position){
27650 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27652 if (typeof(Raphael) == 'undefined') {
27653 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27657 this.raphael = Raphael(this.el.dom);
27659 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27660 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27661 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27662 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27664 r.text(160, 10, "Single Series Chart").attr(txtattr);
27665 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27666 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27667 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27669 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27670 r.barchart(330, 10, 300, 220, data1);
27671 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27672 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27675 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27676 // r.barchart(30, 30, 560, 250, xdata, {
27677 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27678 // axis : "0 0 1 1",
27679 // axisxlabels : xdata
27680 // //yvalues : cols,
27683 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27685 // this.load(null,xdata,{
27686 // axis : "0 0 1 1",
27687 // axisxlabels : xdata
27692 load : function(graphtype,xdata,opts)
27694 this.raphael.clear();
27696 graphtype = this.graphtype;
27701 var r = this.raphael,
27702 fin = function () {
27703 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27705 fout = function () {
27706 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27708 pfin = function() {
27709 this.sector.stop();
27710 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27713 this.label[0].stop();
27714 this.label[0].attr({ r: 7.5 });
27715 this.label[1].attr({ "font-weight": 800 });
27718 pfout = function() {
27719 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27722 this.label[0].animate({ r: 5 }, 500, "bounce");
27723 this.label[1].attr({ "font-weight": 400 });
27729 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27732 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27735 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27736 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27738 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27745 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27750 setTitle: function(o)
27755 initEvents: function() {
27758 this.el.on('click', this.onClick, this);
27762 onClick : function(e)
27764 Roo.log('img onclick');
27765 this.fireEvent('click', this, e);
27777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27780 * @class Roo.bootstrap.dash.NumberBox
27781 * @extends Roo.bootstrap.Component
27782 * Bootstrap NumberBox class
27783 * @cfg {String} headline Box headline
27784 * @cfg {String} content Box content
27785 * @cfg {String} icon Box icon
27786 * @cfg {String} footer Footer text
27787 * @cfg {String} fhref Footer href
27790 * Create a new NumberBox
27791 * @param {Object} config The config object
27795 Roo.bootstrap.dash.NumberBox = function(config){
27796 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27800 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27809 getAutoCreate : function(){
27813 cls : 'small-box ',
27821 cls : 'roo-headline',
27822 html : this.headline
27826 cls : 'roo-content',
27827 html : this.content
27841 cls : 'ion ' + this.icon
27850 cls : 'small-box-footer',
27851 href : this.fhref || '#',
27855 cfg.cn.push(footer);
27862 onRender : function(ct,position){
27863 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27870 setHeadline: function (value)
27872 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27875 setFooter: function (value, href)
27877 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27880 this.el.select('a.small-box-footer',true).first().attr('href', href);
27885 setContent: function (value)
27887 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27890 initEvents: function()
27904 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27907 * @class Roo.bootstrap.dash.TabBox
27908 * @extends Roo.bootstrap.Component
27909 * Bootstrap TabBox class
27910 * @cfg {String} title Title of the TabBox
27911 * @cfg {String} icon Icon of the TabBox
27912 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27913 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27916 * Create a new TabBox
27917 * @param {Object} config The config object
27921 Roo.bootstrap.dash.TabBox = function(config){
27922 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27927 * When a pane is added
27928 * @param {Roo.bootstrap.dash.TabPane} pane
27932 * @event activatepane
27933 * When a pane is activated
27934 * @param {Roo.bootstrap.dash.TabPane} pane
27936 "activatepane" : true
27944 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27949 tabScrollable : false,
27951 getChildContainer : function()
27953 return this.el.select('.tab-content', true).first();
27956 getAutoCreate : function(){
27960 cls: 'pull-left header',
27968 cls: 'fa ' + this.icon
27974 cls: 'nav nav-tabs pull-right',
27980 if(this.tabScrollable){
27987 cls: 'nav nav-tabs pull-right',
27998 cls: 'nav-tabs-custom',
28003 cls: 'tab-content no-padding',
28011 initEvents : function()
28013 //Roo.log('add add pane handler');
28014 this.on('addpane', this.onAddPane, this);
28017 * Updates the box title
28018 * @param {String} html to set the title to.
28020 setTitle : function(value)
28022 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28024 onAddPane : function(pane)
28026 this.panes.push(pane);
28027 //Roo.log('addpane');
28029 // tabs are rendere left to right..
28030 if(!this.showtabs){
28034 var ctr = this.el.select('.nav-tabs', true).first();
28037 var existing = ctr.select('.nav-tab',true);
28038 var qty = existing.getCount();;
28041 var tab = ctr.createChild({
28043 cls : 'nav-tab' + (qty ? '' : ' active'),
28051 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28054 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28056 pane.el.addClass('active');
28061 onTabClick : function(ev,un,ob,pane)
28063 //Roo.log('tab - prev default');
28064 ev.preventDefault();
28067 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28068 pane.tab.addClass('active');
28069 //Roo.log(pane.title);
28070 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28071 // technically we should have a deactivate event.. but maybe add later.
28072 // and it should not de-activate the selected tab...
28073 this.fireEvent('activatepane', pane);
28074 pane.el.addClass('active');
28075 pane.fireEvent('activate');
28080 getActivePane : function()
28083 Roo.each(this.panes, function(p) {
28084 if(p.el.hasClass('active')){
28105 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28107 * @class Roo.bootstrap.TabPane
28108 * @extends Roo.bootstrap.Component
28109 * Bootstrap TabPane class
28110 * @cfg {Boolean} active (false | true) Default false
28111 * @cfg {String} title title of panel
28115 * Create a new TabPane
28116 * @param {Object} config The config object
28119 Roo.bootstrap.dash.TabPane = function(config){
28120 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28126 * When a pane is activated
28127 * @param {Roo.bootstrap.dash.TabPane} pane
28134 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28139 // the tabBox that this is attached to.
28142 getAutoCreate : function()
28150 cfg.cls += ' active';
28155 initEvents : function()
28157 //Roo.log('trigger add pane handler');
28158 this.parent().fireEvent('addpane', this)
28162 * Updates the tab title
28163 * @param {String} html to set the title to.
28165 setTitle: function(str)
28171 this.tab.select('a', true).first().dom.innerHTML = str;
28188 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28191 * @class Roo.bootstrap.menu.Menu
28192 * @extends Roo.bootstrap.Component
28193 * Bootstrap Menu class - container for Menu
28194 * @cfg {String} html Text of the menu
28195 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28196 * @cfg {String} icon Font awesome icon
28197 * @cfg {String} pos Menu align to (top | bottom) default bottom
28201 * Create a new Menu
28202 * @param {Object} config The config object
28206 Roo.bootstrap.menu.Menu = function(config){
28207 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28211 * @event beforeshow
28212 * Fires before this menu is displayed
28213 * @param {Roo.bootstrap.menu.Menu} this
28217 * @event beforehide
28218 * Fires before this menu is hidden
28219 * @param {Roo.bootstrap.menu.Menu} this
28224 * Fires after this menu is displayed
28225 * @param {Roo.bootstrap.menu.Menu} this
28230 * Fires after this menu is hidden
28231 * @param {Roo.bootstrap.menu.Menu} this
28236 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28237 * @param {Roo.bootstrap.menu.Menu} this
28238 * @param {Roo.EventObject} e
28245 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28249 weight : 'default',
28254 getChildContainer : function() {
28255 if(this.isSubMenu){
28259 return this.el.select('ul.dropdown-menu', true).first();
28262 getAutoCreate : function()
28267 cls : 'roo-menu-text',
28275 cls : 'fa ' + this.icon
28286 cls : 'dropdown-button btn btn-' + this.weight,
28291 cls : 'dropdown-toggle btn btn-' + this.weight,
28301 cls : 'dropdown-menu'
28307 if(this.pos == 'top'){
28308 cfg.cls += ' dropup';
28311 if(this.isSubMenu){
28314 cls : 'dropdown-menu'
28321 onRender : function(ct, position)
28323 this.isSubMenu = ct.hasClass('dropdown-submenu');
28325 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28328 initEvents : function()
28330 if(this.isSubMenu){
28334 this.hidden = true;
28336 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28337 this.triggerEl.on('click', this.onTriggerPress, this);
28339 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28340 this.buttonEl.on('click', this.onClick, this);
28346 if(this.isSubMenu){
28350 return this.el.select('ul.dropdown-menu', true).first();
28353 onClick : function(e)
28355 this.fireEvent("click", this, e);
28358 onTriggerPress : function(e)
28360 if (this.isVisible()) {
28367 isVisible : function(){
28368 return !this.hidden;
28373 this.fireEvent("beforeshow", this);
28375 this.hidden = false;
28376 this.el.addClass('open');
28378 Roo.get(document).on("mouseup", this.onMouseUp, this);
28380 this.fireEvent("show", this);
28387 this.fireEvent("beforehide", this);
28389 this.hidden = true;
28390 this.el.removeClass('open');
28392 Roo.get(document).un("mouseup", this.onMouseUp);
28394 this.fireEvent("hide", this);
28397 onMouseUp : function()
28411 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28414 * @class Roo.bootstrap.menu.Item
28415 * @extends Roo.bootstrap.Component
28416 * Bootstrap MenuItem class
28417 * @cfg {Boolean} submenu (true | false) default false
28418 * @cfg {String} html text of the item
28419 * @cfg {String} href the link
28420 * @cfg {Boolean} disable (true | false) default false
28421 * @cfg {Boolean} preventDefault (true | false) default true
28422 * @cfg {String} icon Font awesome icon
28423 * @cfg {String} pos Submenu align to (left | right) default right
28427 * Create a new Item
28428 * @param {Object} config The config object
28432 Roo.bootstrap.menu.Item = function(config){
28433 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28437 * Fires when the mouse is hovering over this menu
28438 * @param {Roo.bootstrap.menu.Item} this
28439 * @param {Roo.EventObject} e
28444 * Fires when the mouse exits this menu
28445 * @param {Roo.bootstrap.menu.Item} this
28446 * @param {Roo.EventObject} e
28452 * The raw click event for the entire grid.
28453 * @param {Roo.EventObject} e
28459 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28464 preventDefault: true,
28469 getAutoCreate : function()
28474 cls : 'roo-menu-item-text',
28482 cls : 'fa ' + this.icon
28491 href : this.href || '#',
28498 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28502 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28504 if(this.pos == 'left'){
28505 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28512 initEvents : function()
28514 this.el.on('mouseover', this.onMouseOver, this);
28515 this.el.on('mouseout', this.onMouseOut, this);
28517 this.el.select('a', true).first().on('click', this.onClick, this);
28521 onClick : function(e)
28523 if(this.preventDefault){
28524 e.preventDefault();
28527 this.fireEvent("click", this, e);
28530 onMouseOver : function(e)
28532 if(this.submenu && this.pos == 'left'){
28533 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28536 this.fireEvent("mouseover", this, e);
28539 onMouseOut : function(e)
28541 this.fireEvent("mouseout", this, e);
28553 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28556 * @class Roo.bootstrap.menu.Separator
28557 * @extends Roo.bootstrap.Component
28558 * Bootstrap Separator class
28561 * Create a new Separator
28562 * @param {Object} config The config object
28566 Roo.bootstrap.menu.Separator = function(config){
28567 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28570 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28572 getAutoCreate : function(){
28593 * @class Roo.bootstrap.Tooltip
28594 * Bootstrap Tooltip class
28595 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28596 * to determine which dom element triggers the tooltip.
28598 * It needs to add support for additional attributes like tooltip-position
28601 * Create a new Toolti
28602 * @param {Object} config The config object
28605 Roo.bootstrap.Tooltip = function(config){
28606 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28608 this.alignment = Roo.bootstrap.Tooltip.alignment;
28610 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28611 this.alignment = config.alignment;
28616 Roo.apply(Roo.bootstrap.Tooltip, {
28618 * @function init initialize tooltip monitoring.
28622 currentTip : false,
28623 currentRegion : false,
28629 Roo.get(document).on('mouseover', this.enter ,this);
28630 Roo.get(document).on('mouseout', this.leave, this);
28633 this.currentTip = new Roo.bootstrap.Tooltip();
28636 enter : function(ev)
28638 var dom = ev.getTarget();
28640 //Roo.log(['enter',dom]);
28641 var el = Roo.fly(dom);
28642 if (this.currentEl) {
28644 //Roo.log(this.currentEl);
28645 //Roo.log(this.currentEl.contains(dom));
28646 if (this.currentEl == el) {
28649 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28655 if (this.currentTip.el) {
28656 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28660 if(!el || el.dom == document){
28666 // you can not look for children, as if el is the body.. then everythign is the child..
28667 if (!el.attr('tooltip')) { //
28668 if (!el.select("[tooltip]").elements.length) {
28671 // is the mouse over this child...?
28672 bindEl = el.select("[tooltip]").first();
28673 var xy = ev.getXY();
28674 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28675 //Roo.log("not in region.");
28678 //Roo.log("child element over..");
28681 this.currentEl = bindEl;
28682 this.currentTip.bind(bindEl);
28683 this.currentRegion = Roo.lib.Region.getRegion(dom);
28684 this.currentTip.enter();
28687 leave : function(ev)
28689 var dom = ev.getTarget();
28690 //Roo.log(['leave',dom]);
28691 if (!this.currentEl) {
28696 if (dom != this.currentEl.dom) {
28699 var xy = ev.getXY();
28700 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28703 // only activate leave if mouse cursor is outside... bounding box..
28708 if (this.currentTip) {
28709 this.currentTip.leave();
28711 //Roo.log('clear currentEl');
28712 this.currentEl = false;
28717 'left' : ['r-l', [-2,0], 'right'],
28718 'right' : ['l-r', [2,0], 'left'],
28719 'bottom' : ['t-b', [0,2], 'top'],
28720 'top' : [ 'b-t', [0,-2], 'bottom']
28726 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28731 delay : null, // can be { show : 300 , hide: 500}
28735 hoverState : null, //???
28737 placement : 'bottom',
28741 getAutoCreate : function(){
28748 cls : 'tooltip-arrow arrow'
28751 cls : 'tooltip-inner'
28758 bind : function(el)
28763 initEvents : function()
28765 this.arrowEl = this.el.select('.arrow', true).first();
28766 this.innerEl = this.el.select('.tooltip-inner', true).first();
28769 enter : function () {
28771 if (this.timeout != null) {
28772 clearTimeout(this.timeout);
28775 this.hoverState = 'in';
28776 //Roo.log("enter - show");
28777 if (!this.delay || !this.delay.show) {
28782 this.timeout = setTimeout(function () {
28783 if (_t.hoverState == 'in') {
28786 }, this.delay.show);
28790 clearTimeout(this.timeout);
28792 this.hoverState = 'out';
28793 if (!this.delay || !this.delay.hide) {
28799 this.timeout = setTimeout(function () {
28800 //Roo.log("leave - timeout");
28802 if (_t.hoverState == 'out') {
28804 Roo.bootstrap.Tooltip.currentEl = false;
28809 show : function (msg)
28812 this.render(document.body);
28815 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28817 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28819 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28821 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28822 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28824 var placement = typeof this.placement == 'function' ?
28825 this.placement.call(this, this.el, on_el) :
28828 var autoToken = /\s?auto?\s?/i;
28829 var autoPlace = autoToken.test(placement);
28831 placement = placement.replace(autoToken, '') || 'top';
28835 //this.el.setXY([0,0]);
28837 //this.el.dom.style.display='block';
28839 //this.el.appendTo(on_el);
28841 var p = this.getPosition();
28842 var box = this.el.getBox();
28848 var align = this.alignment[placement];
28850 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28852 if(placement == 'top' || placement == 'bottom'){
28854 placement = 'right';
28857 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28858 placement = 'left';
28861 var scroll = Roo.select('body', true).first().getScroll();
28863 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28867 align = this.alignment[placement];
28869 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28873 this.el.alignTo(this.bindEl, align[0],align[1]);
28874 //var arrow = this.el.select('.arrow',true).first();
28875 //arrow.set(align[2],
28877 this.el.addClass(placement);
28878 this.el.addClass("bs-tooltip-"+ placement);
28880 this.el.addClass('in fade show');
28882 this.hoverState = null;
28884 if (this.el.hasClass('fade')) {
28899 //this.el.setXY([0,0]);
28900 this.el.removeClass(['show', 'in']);
28916 * @class Roo.bootstrap.LocationPicker
28917 * @extends Roo.bootstrap.Component
28918 * Bootstrap LocationPicker class
28919 * @cfg {Number} latitude Position when init default 0
28920 * @cfg {Number} longitude Position when init default 0
28921 * @cfg {Number} zoom default 15
28922 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28923 * @cfg {Boolean} mapTypeControl default false
28924 * @cfg {Boolean} disableDoubleClickZoom default false
28925 * @cfg {Boolean} scrollwheel default true
28926 * @cfg {Boolean} streetViewControl default false
28927 * @cfg {Number} radius default 0
28928 * @cfg {String} locationName
28929 * @cfg {Boolean} draggable default true
28930 * @cfg {Boolean} enableAutocomplete default false
28931 * @cfg {Boolean} enableReverseGeocode default true
28932 * @cfg {String} markerTitle
28935 * Create a new LocationPicker
28936 * @param {Object} config The config object
28940 Roo.bootstrap.LocationPicker = function(config){
28942 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28947 * Fires when the picker initialized.
28948 * @param {Roo.bootstrap.LocationPicker} this
28949 * @param {Google Location} location
28953 * @event positionchanged
28954 * Fires when the picker position changed.
28955 * @param {Roo.bootstrap.LocationPicker} this
28956 * @param {Google Location} location
28958 positionchanged : true,
28961 * Fires when the map resize.
28962 * @param {Roo.bootstrap.LocationPicker} this
28967 * Fires when the map show.
28968 * @param {Roo.bootstrap.LocationPicker} this
28973 * Fires when the map hide.
28974 * @param {Roo.bootstrap.LocationPicker} this
28979 * Fires when click the map.
28980 * @param {Roo.bootstrap.LocationPicker} this
28981 * @param {Map event} e
28985 * @event mapRightClick
28986 * Fires when right click the map.
28987 * @param {Roo.bootstrap.LocationPicker} this
28988 * @param {Map event} e
28990 mapRightClick : true,
28992 * @event markerClick
28993 * Fires when click the marker.
28994 * @param {Roo.bootstrap.LocationPicker} this
28995 * @param {Map event} e
28997 markerClick : true,
28999 * @event markerRightClick
29000 * Fires when right click the marker.
29001 * @param {Roo.bootstrap.LocationPicker} this
29002 * @param {Map event} e
29004 markerRightClick : true,
29006 * @event OverlayViewDraw
29007 * Fires when OverlayView Draw
29008 * @param {Roo.bootstrap.LocationPicker} this
29010 OverlayViewDraw : true,
29012 * @event OverlayViewOnAdd
29013 * Fires when OverlayView Draw
29014 * @param {Roo.bootstrap.LocationPicker} this
29016 OverlayViewOnAdd : true,
29018 * @event OverlayViewOnRemove
29019 * Fires when OverlayView Draw
29020 * @param {Roo.bootstrap.LocationPicker} this
29022 OverlayViewOnRemove : true,
29024 * @event OverlayViewShow
29025 * Fires when OverlayView Draw
29026 * @param {Roo.bootstrap.LocationPicker} this
29027 * @param {Pixel} cpx
29029 OverlayViewShow : true,
29031 * @event OverlayViewHide
29032 * Fires when OverlayView Draw
29033 * @param {Roo.bootstrap.LocationPicker} this
29035 OverlayViewHide : true,
29037 * @event loadexception
29038 * Fires when load google lib failed.
29039 * @param {Roo.bootstrap.LocationPicker} this
29041 loadexception : true
29046 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29048 gMapContext: false,
29054 mapTypeControl: false,
29055 disableDoubleClickZoom: false,
29057 streetViewControl: false,
29061 enableAutocomplete: false,
29062 enableReverseGeocode: true,
29065 getAutoCreate: function()
29070 cls: 'roo-location-picker'
29076 initEvents: function(ct, position)
29078 if(!this.el.getWidth() || this.isApplied()){
29082 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29087 initial: function()
29089 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29090 this.fireEvent('loadexception', this);
29094 if(!this.mapTypeId){
29095 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29098 this.gMapContext = this.GMapContext();
29100 this.initOverlayView();
29102 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29106 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29107 _this.setPosition(_this.gMapContext.marker.position);
29110 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29111 _this.fireEvent('mapClick', this, event);
29115 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29116 _this.fireEvent('mapRightClick', this, event);
29120 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29121 _this.fireEvent('markerClick', this, event);
29125 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29126 _this.fireEvent('markerRightClick', this, event);
29130 this.setPosition(this.gMapContext.location);
29132 this.fireEvent('initial', this, this.gMapContext.location);
29135 initOverlayView: function()
29139 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29143 _this.fireEvent('OverlayViewDraw', _this);
29148 _this.fireEvent('OverlayViewOnAdd', _this);
29151 onRemove: function()
29153 _this.fireEvent('OverlayViewOnRemove', _this);
29156 show: function(cpx)
29158 _this.fireEvent('OverlayViewShow', _this, cpx);
29163 _this.fireEvent('OverlayViewHide', _this);
29169 fromLatLngToContainerPixel: function(event)
29171 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29174 isApplied: function()
29176 return this.getGmapContext() == false ? false : true;
29179 getGmapContext: function()
29181 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29184 GMapContext: function()
29186 var position = new google.maps.LatLng(this.latitude, this.longitude);
29188 var _map = new google.maps.Map(this.el.dom, {
29191 mapTypeId: this.mapTypeId,
29192 mapTypeControl: this.mapTypeControl,
29193 disableDoubleClickZoom: this.disableDoubleClickZoom,
29194 scrollwheel: this.scrollwheel,
29195 streetViewControl: this.streetViewControl,
29196 locationName: this.locationName,
29197 draggable: this.draggable,
29198 enableAutocomplete: this.enableAutocomplete,
29199 enableReverseGeocode: this.enableReverseGeocode
29202 var _marker = new google.maps.Marker({
29203 position: position,
29205 title: this.markerTitle,
29206 draggable: this.draggable
29213 location: position,
29214 radius: this.radius,
29215 locationName: this.locationName,
29216 addressComponents: {
29217 formatted_address: null,
29218 addressLine1: null,
29219 addressLine2: null,
29221 streetNumber: null,
29225 stateOrProvince: null
29228 domContainer: this.el.dom,
29229 geodecoder: new google.maps.Geocoder()
29233 drawCircle: function(center, radius, options)
29235 if (this.gMapContext.circle != null) {
29236 this.gMapContext.circle.setMap(null);
29240 options = Roo.apply({}, options, {
29241 strokeColor: "#0000FF",
29242 strokeOpacity: .35,
29244 fillColor: "#0000FF",
29248 options.map = this.gMapContext.map;
29249 options.radius = radius;
29250 options.center = center;
29251 this.gMapContext.circle = new google.maps.Circle(options);
29252 return this.gMapContext.circle;
29258 setPosition: function(location)
29260 this.gMapContext.location = location;
29261 this.gMapContext.marker.setPosition(location);
29262 this.gMapContext.map.panTo(location);
29263 this.drawCircle(location, this.gMapContext.radius, {});
29267 if (this.gMapContext.settings.enableReverseGeocode) {
29268 this.gMapContext.geodecoder.geocode({
29269 latLng: this.gMapContext.location
29270 }, function(results, status) {
29272 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29273 _this.gMapContext.locationName = results[0].formatted_address;
29274 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29276 _this.fireEvent('positionchanged', this, location);
29283 this.fireEvent('positionchanged', this, location);
29288 google.maps.event.trigger(this.gMapContext.map, "resize");
29290 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29292 this.fireEvent('resize', this);
29295 setPositionByLatLng: function(latitude, longitude)
29297 this.setPosition(new google.maps.LatLng(latitude, longitude));
29300 getCurrentPosition: function()
29303 latitude: this.gMapContext.location.lat(),
29304 longitude: this.gMapContext.location.lng()
29308 getAddressName: function()
29310 return this.gMapContext.locationName;
29313 getAddressComponents: function()
29315 return this.gMapContext.addressComponents;
29318 address_component_from_google_geocode: function(address_components)
29322 for (var i = 0; i < address_components.length; i++) {
29323 var component = address_components[i];
29324 if (component.types.indexOf("postal_code") >= 0) {
29325 result.postalCode = component.short_name;
29326 } else if (component.types.indexOf("street_number") >= 0) {
29327 result.streetNumber = component.short_name;
29328 } else if (component.types.indexOf("route") >= 0) {
29329 result.streetName = component.short_name;
29330 } else if (component.types.indexOf("neighborhood") >= 0) {
29331 result.city = component.short_name;
29332 } else if (component.types.indexOf("locality") >= 0) {
29333 result.city = component.short_name;
29334 } else if (component.types.indexOf("sublocality") >= 0) {
29335 result.district = component.short_name;
29336 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29337 result.stateOrProvince = component.short_name;
29338 } else if (component.types.indexOf("country") >= 0) {
29339 result.country = component.short_name;
29343 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29344 result.addressLine2 = "";
29348 setZoomLevel: function(zoom)
29350 this.gMapContext.map.setZoom(zoom);
29363 this.fireEvent('show', this);
29374 this.fireEvent('hide', this);
29379 Roo.apply(Roo.bootstrap.LocationPicker, {
29381 OverlayView : function(map, options)
29383 options = options || {};
29390 * @class Roo.bootstrap.Alert
29391 * @extends Roo.bootstrap.Component
29392 * Bootstrap Alert class - shows an alert area box
29394 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29395 Enter a valid email address
29398 * @cfg {String} title The title of alert
29399 * @cfg {String} html The content of alert
29400 * @cfg {String} weight ( success | info | warning | danger )
29401 * @cfg {String} faicon font-awesomeicon
29404 * Create a new alert
29405 * @param {Object} config The config object
29409 Roo.bootstrap.Alert = function(config){
29410 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29414 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29421 getAutoCreate : function()
29430 cls : 'roo-alert-icon'
29435 cls : 'roo-alert-title',
29440 cls : 'roo-alert-text',
29447 cfg.cn[0].cls += ' fa ' + this.faicon;
29451 cfg.cls += ' alert-' + this.weight;
29457 initEvents: function()
29459 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29462 setTitle : function(str)
29464 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29467 setText : function(str)
29469 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29472 setWeight : function(weight)
29475 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29478 this.weight = weight;
29480 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29483 setIcon : function(icon)
29486 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29489 this.faicon = icon;
29491 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29512 * @class Roo.bootstrap.UploadCropbox
29513 * @extends Roo.bootstrap.Component
29514 * Bootstrap UploadCropbox class
29515 * @cfg {String} emptyText show when image has been loaded
29516 * @cfg {String} rotateNotify show when image too small to rotate
29517 * @cfg {Number} errorTimeout default 3000
29518 * @cfg {Number} minWidth default 300
29519 * @cfg {Number} minHeight default 300
29520 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29521 * @cfg {Boolean} isDocument (true|false) default false
29522 * @cfg {String} url action url
29523 * @cfg {String} paramName default 'imageUpload'
29524 * @cfg {String} method default POST
29525 * @cfg {Boolean} loadMask (true|false) default true
29526 * @cfg {Boolean} loadingText default 'Loading...'
29529 * Create a new UploadCropbox
29530 * @param {Object} config The config object
29533 Roo.bootstrap.UploadCropbox = function(config){
29534 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29538 * @event beforeselectfile
29539 * Fire before select file
29540 * @param {Roo.bootstrap.UploadCropbox} this
29542 "beforeselectfile" : true,
29545 * Fire after initEvent
29546 * @param {Roo.bootstrap.UploadCropbox} this
29551 * Fire after initEvent
29552 * @param {Roo.bootstrap.UploadCropbox} this
29553 * @param {String} data
29558 * Fire when preparing the file data
29559 * @param {Roo.bootstrap.UploadCropbox} this
29560 * @param {Object} file
29565 * Fire when get exception
29566 * @param {Roo.bootstrap.UploadCropbox} this
29567 * @param {XMLHttpRequest} xhr
29569 "exception" : true,
29571 * @event beforeloadcanvas
29572 * Fire before load the canvas
29573 * @param {Roo.bootstrap.UploadCropbox} this
29574 * @param {String} src
29576 "beforeloadcanvas" : true,
29579 * Fire when trash image
29580 * @param {Roo.bootstrap.UploadCropbox} this
29585 * Fire when download the image
29586 * @param {Roo.bootstrap.UploadCropbox} this
29590 * @event footerbuttonclick
29591 * Fire when footerbuttonclick
29592 * @param {Roo.bootstrap.UploadCropbox} this
29593 * @param {String} type
29595 "footerbuttonclick" : true,
29599 * @param {Roo.bootstrap.UploadCropbox} this
29604 * Fire when rotate the image
29605 * @param {Roo.bootstrap.UploadCropbox} this
29606 * @param {String} pos
29611 * Fire when inspect the file
29612 * @param {Roo.bootstrap.UploadCropbox} this
29613 * @param {Object} file
29618 * Fire when xhr upload the file
29619 * @param {Roo.bootstrap.UploadCropbox} this
29620 * @param {Object} data
29625 * Fire when arrange the file data
29626 * @param {Roo.bootstrap.UploadCropbox} this
29627 * @param {Object} formData
29632 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29635 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29637 emptyText : 'Click to upload image',
29638 rotateNotify : 'Image is too small to rotate',
29639 errorTimeout : 3000,
29653 cropType : 'image/jpeg',
29655 canvasLoaded : false,
29656 isDocument : false,
29658 paramName : 'imageUpload',
29660 loadingText : 'Loading...',
29663 getAutoCreate : function()
29667 cls : 'roo-upload-cropbox',
29671 cls : 'roo-upload-cropbox-selector',
29676 cls : 'roo-upload-cropbox-body',
29677 style : 'cursor:pointer',
29681 cls : 'roo-upload-cropbox-preview'
29685 cls : 'roo-upload-cropbox-thumb'
29689 cls : 'roo-upload-cropbox-empty-notify',
29690 html : this.emptyText
29694 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29695 html : this.rotateNotify
29701 cls : 'roo-upload-cropbox-footer',
29704 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29714 onRender : function(ct, position)
29716 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29718 if (this.buttons.length) {
29720 Roo.each(this.buttons, function(bb) {
29722 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29724 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29730 this.maskEl = this.el;
29734 initEvents : function()
29736 this.urlAPI = (window.createObjectURL && window) ||
29737 (window.URL && URL.revokeObjectURL && URL) ||
29738 (window.webkitURL && webkitURL);
29740 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29741 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29743 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29744 this.selectorEl.hide();
29746 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29747 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29749 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29750 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29751 this.thumbEl.hide();
29753 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29754 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29756 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29757 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29758 this.errorEl.hide();
29760 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29761 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29762 this.footerEl.hide();
29764 this.setThumbBoxSize();
29770 this.fireEvent('initial', this);
29777 window.addEventListener("resize", function() { _this.resize(); } );
29779 this.bodyEl.on('click', this.beforeSelectFile, this);
29782 this.bodyEl.on('touchstart', this.onTouchStart, this);
29783 this.bodyEl.on('touchmove', this.onTouchMove, this);
29784 this.bodyEl.on('touchend', this.onTouchEnd, this);
29788 this.bodyEl.on('mousedown', this.onMouseDown, this);
29789 this.bodyEl.on('mousemove', this.onMouseMove, this);
29790 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29791 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29792 Roo.get(document).on('mouseup', this.onMouseUp, this);
29795 this.selectorEl.on('change', this.onFileSelected, this);
29801 this.baseScale = 1;
29803 this.baseRotate = 1;
29804 this.dragable = false;
29805 this.pinching = false;
29808 this.cropData = false;
29809 this.notifyEl.dom.innerHTML = this.emptyText;
29811 this.selectorEl.dom.value = '';
29815 resize : function()
29817 if(this.fireEvent('resize', this) != false){
29818 this.setThumbBoxPosition();
29819 this.setCanvasPosition();
29823 onFooterButtonClick : function(e, el, o, type)
29826 case 'rotate-left' :
29827 this.onRotateLeft(e);
29829 case 'rotate-right' :
29830 this.onRotateRight(e);
29833 this.beforeSelectFile(e);
29848 this.fireEvent('footerbuttonclick', this, type);
29851 beforeSelectFile : function(e)
29853 e.preventDefault();
29855 if(this.fireEvent('beforeselectfile', this) != false){
29856 this.selectorEl.dom.click();
29860 onFileSelected : function(e)
29862 e.preventDefault();
29864 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29868 var file = this.selectorEl.dom.files[0];
29870 if(this.fireEvent('inspect', this, file) != false){
29871 this.prepare(file);
29876 trash : function(e)
29878 this.fireEvent('trash', this);
29881 download : function(e)
29883 this.fireEvent('download', this);
29886 loadCanvas : function(src)
29888 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29892 this.imageEl = document.createElement('img');
29896 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29898 this.imageEl.src = src;
29902 onLoadCanvas : function()
29904 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29905 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29907 this.bodyEl.un('click', this.beforeSelectFile, this);
29909 this.notifyEl.hide();
29910 this.thumbEl.show();
29911 this.footerEl.show();
29913 this.baseRotateLevel();
29915 if(this.isDocument){
29916 this.setThumbBoxSize();
29919 this.setThumbBoxPosition();
29921 this.baseScaleLevel();
29927 this.canvasLoaded = true;
29930 this.maskEl.unmask();
29935 setCanvasPosition : function()
29937 if(!this.canvasEl){
29941 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29942 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29944 this.previewEl.setLeft(pw);
29945 this.previewEl.setTop(ph);
29949 onMouseDown : function(e)
29953 this.dragable = true;
29954 this.pinching = false;
29956 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29957 this.dragable = false;
29961 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29962 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29966 onMouseMove : function(e)
29970 if(!this.canvasLoaded){
29974 if (!this.dragable){
29978 var minX = Math.ceil(this.thumbEl.getLeft(true));
29979 var minY = Math.ceil(this.thumbEl.getTop(true));
29981 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29982 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29984 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29985 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29987 x = x - this.mouseX;
29988 y = y - this.mouseY;
29990 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29991 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29993 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29994 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29996 this.previewEl.setLeft(bgX);
29997 this.previewEl.setTop(bgY);
29999 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30000 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30003 onMouseUp : function(e)
30007 this.dragable = false;
30010 onMouseWheel : function(e)
30014 this.startScale = this.scale;
30016 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30018 if(!this.zoomable()){
30019 this.scale = this.startScale;
30028 zoomable : function()
30030 var minScale = this.thumbEl.getWidth() / this.minWidth;
30032 if(this.minWidth < this.minHeight){
30033 minScale = this.thumbEl.getHeight() / this.minHeight;
30036 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30037 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30041 (this.rotate == 0 || this.rotate == 180) &&
30043 width > this.imageEl.OriginWidth ||
30044 height > this.imageEl.OriginHeight ||
30045 (width < this.minWidth && height < this.minHeight)
30053 (this.rotate == 90 || this.rotate == 270) &&
30055 width > this.imageEl.OriginWidth ||
30056 height > this.imageEl.OriginHeight ||
30057 (width < this.minHeight && height < this.minWidth)
30064 !this.isDocument &&
30065 (this.rotate == 0 || this.rotate == 180) &&
30067 width < this.minWidth ||
30068 width > this.imageEl.OriginWidth ||
30069 height < this.minHeight ||
30070 height > this.imageEl.OriginHeight
30077 !this.isDocument &&
30078 (this.rotate == 90 || this.rotate == 270) &&
30080 width < this.minHeight ||
30081 width > this.imageEl.OriginWidth ||
30082 height < this.minWidth ||
30083 height > this.imageEl.OriginHeight
30093 onRotateLeft : function(e)
30095 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30097 var minScale = this.thumbEl.getWidth() / this.minWidth;
30099 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30100 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30102 this.startScale = this.scale;
30104 while (this.getScaleLevel() < minScale){
30106 this.scale = this.scale + 1;
30108 if(!this.zoomable()){
30113 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30114 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30119 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30126 this.scale = this.startScale;
30128 this.onRotateFail();
30133 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30135 if(this.isDocument){
30136 this.setThumbBoxSize();
30137 this.setThumbBoxPosition();
30138 this.setCanvasPosition();
30143 this.fireEvent('rotate', this, 'left');
30147 onRotateRight : function(e)
30149 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30151 var minScale = this.thumbEl.getWidth() / this.minWidth;
30153 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30154 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30156 this.startScale = this.scale;
30158 while (this.getScaleLevel() < minScale){
30160 this.scale = this.scale + 1;
30162 if(!this.zoomable()){
30167 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30168 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30173 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30180 this.scale = this.startScale;
30182 this.onRotateFail();
30187 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30189 if(this.isDocument){
30190 this.setThumbBoxSize();
30191 this.setThumbBoxPosition();
30192 this.setCanvasPosition();
30197 this.fireEvent('rotate', this, 'right');
30200 onRotateFail : function()
30202 this.errorEl.show(true);
30206 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30211 this.previewEl.dom.innerHTML = '';
30213 var canvasEl = document.createElement("canvas");
30215 var contextEl = canvasEl.getContext("2d");
30217 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30218 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30219 var center = this.imageEl.OriginWidth / 2;
30221 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30222 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30223 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30224 center = this.imageEl.OriginHeight / 2;
30227 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30229 contextEl.translate(center, center);
30230 contextEl.rotate(this.rotate * Math.PI / 180);
30232 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30234 this.canvasEl = document.createElement("canvas");
30236 this.contextEl = this.canvasEl.getContext("2d");
30238 switch (this.rotate) {
30241 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30242 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30244 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30249 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30250 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30252 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30253 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);
30257 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30262 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30263 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30265 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30266 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);
30270 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);
30275 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30276 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30278 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30279 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30283 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);
30290 this.previewEl.appendChild(this.canvasEl);
30292 this.setCanvasPosition();
30297 if(!this.canvasLoaded){
30301 var imageCanvas = document.createElement("canvas");
30303 var imageContext = imageCanvas.getContext("2d");
30305 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30306 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30308 var center = imageCanvas.width / 2;
30310 imageContext.translate(center, center);
30312 imageContext.rotate(this.rotate * Math.PI / 180);
30314 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30316 var canvas = document.createElement("canvas");
30318 var context = canvas.getContext("2d");
30320 canvas.width = this.minWidth;
30321 canvas.height = this.minHeight;
30323 switch (this.rotate) {
30326 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30327 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30329 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30330 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30332 var targetWidth = this.minWidth - 2 * x;
30333 var targetHeight = this.minHeight - 2 * y;
30337 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30338 scale = targetWidth / width;
30341 if(x > 0 && y == 0){
30342 scale = targetHeight / height;
30345 if(x > 0 && y > 0){
30346 scale = targetWidth / width;
30348 if(width < height){
30349 scale = targetHeight / height;
30353 context.scale(scale, scale);
30355 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30356 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30358 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30359 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30361 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30366 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30367 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30369 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30370 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30372 var targetWidth = this.minWidth - 2 * x;
30373 var targetHeight = this.minHeight - 2 * y;
30377 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30378 scale = targetWidth / width;
30381 if(x > 0 && y == 0){
30382 scale = targetHeight / height;
30385 if(x > 0 && y > 0){
30386 scale = targetWidth / width;
30388 if(width < height){
30389 scale = targetHeight / height;
30393 context.scale(scale, scale);
30395 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30396 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30398 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30399 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30401 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30403 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30408 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30409 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30411 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30412 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30414 var targetWidth = this.minWidth - 2 * x;
30415 var targetHeight = this.minHeight - 2 * y;
30419 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30420 scale = targetWidth / width;
30423 if(x > 0 && y == 0){
30424 scale = targetHeight / height;
30427 if(x > 0 && y > 0){
30428 scale = targetWidth / width;
30430 if(width < height){
30431 scale = targetHeight / height;
30435 context.scale(scale, scale);
30437 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30438 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30440 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30441 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30443 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30444 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30446 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30451 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30452 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30454 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30455 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30457 var targetWidth = this.minWidth - 2 * x;
30458 var targetHeight = this.minHeight - 2 * y;
30462 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30463 scale = targetWidth / width;
30466 if(x > 0 && y == 0){
30467 scale = targetHeight / height;
30470 if(x > 0 && y > 0){
30471 scale = targetWidth / width;
30473 if(width < height){
30474 scale = targetHeight / height;
30478 context.scale(scale, scale);
30480 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30481 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30483 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30484 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30486 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30488 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30495 this.cropData = canvas.toDataURL(this.cropType);
30497 if(this.fireEvent('crop', this, this.cropData) !== false){
30498 this.process(this.file, this.cropData);
30505 setThumbBoxSize : function()
30509 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30510 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30511 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30513 this.minWidth = width;
30514 this.minHeight = height;
30516 if(this.rotate == 90 || this.rotate == 270){
30517 this.minWidth = height;
30518 this.minHeight = width;
30523 width = Math.ceil(this.minWidth * height / this.minHeight);
30525 if(this.minWidth > this.minHeight){
30527 height = Math.ceil(this.minHeight * width / this.minWidth);
30530 this.thumbEl.setStyle({
30531 width : width + 'px',
30532 height : height + 'px'
30539 setThumbBoxPosition : function()
30541 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30542 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30544 this.thumbEl.setLeft(x);
30545 this.thumbEl.setTop(y);
30549 baseRotateLevel : function()
30551 this.baseRotate = 1;
30554 typeof(this.exif) != 'undefined' &&
30555 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30556 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30558 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30561 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30565 baseScaleLevel : function()
30569 if(this.isDocument){
30571 if(this.baseRotate == 6 || this.baseRotate == 8){
30573 height = this.thumbEl.getHeight();
30574 this.baseScale = height / this.imageEl.OriginWidth;
30576 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30577 width = this.thumbEl.getWidth();
30578 this.baseScale = width / this.imageEl.OriginHeight;
30584 height = this.thumbEl.getHeight();
30585 this.baseScale = height / this.imageEl.OriginHeight;
30587 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30588 width = this.thumbEl.getWidth();
30589 this.baseScale = width / this.imageEl.OriginWidth;
30595 if(this.baseRotate == 6 || this.baseRotate == 8){
30597 width = this.thumbEl.getHeight();
30598 this.baseScale = width / this.imageEl.OriginHeight;
30600 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30601 height = this.thumbEl.getWidth();
30602 this.baseScale = height / this.imageEl.OriginHeight;
30605 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30606 height = this.thumbEl.getWidth();
30607 this.baseScale = height / this.imageEl.OriginHeight;
30609 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30610 width = this.thumbEl.getHeight();
30611 this.baseScale = width / this.imageEl.OriginWidth;
30618 width = this.thumbEl.getWidth();
30619 this.baseScale = width / this.imageEl.OriginWidth;
30621 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30622 height = this.thumbEl.getHeight();
30623 this.baseScale = height / this.imageEl.OriginHeight;
30626 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30628 height = this.thumbEl.getHeight();
30629 this.baseScale = height / this.imageEl.OriginHeight;
30631 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30632 width = this.thumbEl.getWidth();
30633 this.baseScale = width / this.imageEl.OriginWidth;
30641 getScaleLevel : function()
30643 return this.baseScale * Math.pow(1.1, this.scale);
30646 onTouchStart : function(e)
30648 if(!this.canvasLoaded){
30649 this.beforeSelectFile(e);
30653 var touches = e.browserEvent.touches;
30659 if(touches.length == 1){
30660 this.onMouseDown(e);
30664 if(touches.length != 2){
30670 for(var i = 0, finger; finger = touches[i]; i++){
30671 coords.push(finger.pageX, finger.pageY);
30674 var x = Math.pow(coords[0] - coords[2], 2);
30675 var y = Math.pow(coords[1] - coords[3], 2);
30677 this.startDistance = Math.sqrt(x + y);
30679 this.startScale = this.scale;
30681 this.pinching = true;
30682 this.dragable = false;
30686 onTouchMove : function(e)
30688 if(!this.pinching && !this.dragable){
30692 var touches = e.browserEvent.touches;
30699 this.onMouseMove(e);
30705 for(var i = 0, finger; finger = touches[i]; i++){
30706 coords.push(finger.pageX, finger.pageY);
30709 var x = Math.pow(coords[0] - coords[2], 2);
30710 var y = Math.pow(coords[1] - coords[3], 2);
30712 this.endDistance = Math.sqrt(x + y);
30714 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30716 if(!this.zoomable()){
30717 this.scale = this.startScale;
30725 onTouchEnd : function(e)
30727 this.pinching = false;
30728 this.dragable = false;
30732 process : function(file, crop)
30735 this.maskEl.mask(this.loadingText);
30738 this.xhr = new XMLHttpRequest();
30740 file.xhr = this.xhr;
30742 this.xhr.open(this.method, this.url, true);
30745 "Accept": "application/json",
30746 "Cache-Control": "no-cache",
30747 "X-Requested-With": "XMLHttpRequest"
30750 for (var headerName in headers) {
30751 var headerValue = headers[headerName];
30753 this.xhr.setRequestHeader(headerName, headerValue);
30759 this.xhr.onload = function()
30761 _this.xhrOnLoad(_this.xhr);
30764 this.xhr.onerror = function()
30766 _this.xhrOnError(_this.xhr);
30769 var formData = new FormData();
30771 formData.append('returnHTML', 'NO');
30774 formData.append('crop', crop);
30777 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30778 formData.append(this.paramName, file, file.name);
30781 if(typeof(file.filename) != 'undefined'){
30782 formData.append('filename', file.filename);
30785 if(typeof(file.mimetype) != 'undefined'){
30786 formData.append('mimetype', file.mimetype);
30789 if(this.fireEvent('arrange', this, formData) != false){
30790 this.xhr.send(formData);
30794 xhrOnLoad : function(xhr)
30797 this.maskEl.unmask();
30800 if (xhr.readyState !== 4) {
30801 this.fireEvent('exception', this, xhr);
30805 var response = Roo.decode(xhr.responseText);
30807 if(!response.success){
30808 this.fireEvent('exception', this, xhr);
30812 var response = Roo.decode(xhr.responseText);
30814 this.fireEvent('upload', this, response);
30818 xhrOnError : function()
30821 this.maskEl.unmask();
30824 Roo.log('xhr on error');
30826 var response = Roo.decode(xhr.responseText);
30832 prepare : function(file)
30835 this.maskEl.mask(this.loadingText);
30841 if(typeof(file) === 'string'){
30842 this.loadCanvas(file);
30846 if(!file || !this.urlAPI){
30851 this.cropType = file.type;
30855 if(this.fireEvent('prepare', this, this.file) != false){
30857 var reader = new FileReader();
30859 reader.onload = function (e) {
30860 if (e.target.error) {
30861 Roo.log(e.target.error);
30865 var buffer = e.target.result,
30866 dataView = new DataView(buffer),
30868 maxOffset = dataView.byteLength - 4,
30872 if (dataView.getUint16(0) === 0xffd8) {
30873 while (offset < maxOffset) {
30874 markerBytes = dataView.getUint16(offset);
30876 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30877 markerLength = dataView.getUint16(offset + 2) + 2;
30878 if (offset + markerLength > dataView.byteLength) {
30879 Roo.log('Invalid meta data: Invalid segment size.');
30883 if(markerBytes == 0xffe1){
30884 _this.parseExifData(
30891 offset += markerLength;
30901 var url = _this.urlAPI.createObjectURL(_this.file);
30903 _this.loadCanvas(url);
30908 reader.readAsArrayBuffer(this.file);
30914 parseExifData : function(dataView, offset, length)
30916 var tiffOffset = offset + 10,
30920 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30921 // No Exif data, might be XMP data instead
30925 // Check for the ASCII code for "Exif" (0x45786966):
30926 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30927 // No Exif data, might be XMP data instead
30930 if (tiffOffset + 8 > dataView.byteLength) {
30931 Roo.log('Invalid Exif data: Invalid segment size.');
30934 // Check for the two null bytes:
30935 if (dataView.getUint16(offset + 8) !== 0x0000) {
30936 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30939 // Check the byte alignment:
30940 switch (dataView.getUint16(tiffOffset)) {
30942 littleEndian = true;
30945 littleEndian = false;
30948 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30951 // Check for the TIFF tag marker (0x002A):
30952 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30953 Roo.log('Invalid Exif data: Missing TIFF marker.');
30956 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30957 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30959 this.parseExifTags(
30962 tiffOffset + dirOffset,
30967 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30972 if (dirOffset + 6 > dataView.byteLength) {
30973 Roo.log('Invalid Exif data: Invalid directory offset.');
30976 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30977 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30978 if (dirEndOffset + 4 > dataView.byteLength) {
30979 Roo.log('Invalid Exif data: Invalid directory size.');
30982 for (i = 0; i < tagsNumber; i += 1) {
30986 dirOffset + 2 + 12 * i, // tag offset
30990 // Return the offset to the next directory:
30991 return dataView.getUint32(dirEndOffset, littleEndian);
30994 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30996 var tag = dataView.getUint16(offset, littleEndian);
30998 this.exif[tag] = this.getExifValue(
31002 dataView.getUint16(offset + 2, littleEndian), // tag type
31003 dataView.getUint32(offset + 4, littleEndian), // tag length
31008 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31010 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31019 Roo.log('Invalid Exif data: Invalid tag type.');
31023 tagSize = tagType.size * length;
31024 // Determine if the value is contained in the dataOffset bytes,
31025 // or if the value at the dataOffset is a pointer to the actual data:
31026 dataOffset = tagSize > 4 ?
31027 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31028 if (dataOffset + tagSize > dataView.byteLength) {
31029 Roo.log('Invalid Exif data: Invalid data offset.');
31032 if (length === 1) {
31033 return tagType.getValue(dataView, dataOffset, littleEndian);
31036 for (i = 0; i < length; i += 1) {
31037 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31040 if (tagType.ascii) {
31042 // Concatenate the chars:
31043 for (i = 0; i < values.length; i += 1) {
31045 // Ignore the terminating NULL byte(s):
31046 if (c === '\u0000') {
31058 Roo.apply(Roo.bootstrap.UploadCropbox, {
31060 'Orientation': 0x0112
31064 1: 0, //'top-left',
31066 3: 180, //'bottom-right',
31067 // 4: 'bottom-left',
31069 6: 90, //'right-top',
31070 // 7: 'right-bottom',
31071 8: 270 //'left-bottom'
31075 // byte, 8-bit unsigned int:
31077 getValue: function (dataView, dataOffset) {
31078 return dataView.getUint8(dataOffset);
31082 // ascii, 8-bit byte:
31084 getValue: function (dataView, dataOffset) {
31085 return String.fromCharCode(dataView.getUint8(dataOffset));
31090 // short, 16 bit int:
31092 getValue: function (dataView, dataOffset, littleEndian) {
31093 return dataView.getUint16(dataOffset, littleEndian);
31097 // long, 32 bit int:
31099 getValue: function (dataView, dataOffset, littleEndian) {
31100 return dataView.getUint32(dataOffset, littleEndian);
31104 // rational = two long values, first is numerator, second is denominator:
31106 getValue: function (dataView, dataOffset, littleEndian) {
31107 return dataView.getUint32(dataOffset, littleEndian) /
31108 dataView.getUint32(dataOffset + 4, littleEndian);
31112 // slong, 32 bit signed int:
31114 getValue: function (dataView, dataOffset, littleEndian) {
31115 return dataView.getInt32(dataOffset, littleEndian);
31119 // srational, two slongs, first is numerator, second is denominator:
31121 getValue: function (dataView, dataOffset, littleEndian) {
31122 return dataView.getInt32(dataOffset, littleEndian) /
31123 dataView.getInt32(dataOffset + 4, littleEndian);
31133 cls : 'btn-group roo-upload-cropbox-rotate-left',
31134 action : 'rotate-left',
31138 cls : 'btn btn-default',
31139 html : '<i class="fa fa-undo"></i>'
31145 cls : 'btn-group roo-upload-cropbox-picture',
31146 action : 'picture',
31150 cls : 'btn btn-default',
31151 html : '<i class="fa fa-picture-o"></i>'
31157 cls : 'btn-group roo-upload-cropbox-rotate-right',
31158 action : 'rotate-right',
31162 cls : 'btn btn-default',
31163 html : '<i class="fa fa-repeat"></i>'
31171 cls : 'btn-group roo-upload-cropbox-rotate-left',
31172 action : 'rotate-left',
31176 cls : 'btn btn-default',
31177 html : '<i class="fa fa-undo"></i>'
31183 cls : 'btn-group roo-upload-cropbox-download',
31184 action : 'download',
31188 cls : 'btn btn-default',
31189 html : '<i class="fa fa-download"></i>'
31195 cls : 'btn-group roo-upload-cropbox-crop',
31200 cls : 'btn btn-default',
31201 html : '<i class="fa fa-crop"></i>'
31207 cls : 'btn-group roo-upload-cropbox-trash',
31212 cls : 'btn btn-default',
31213 html : '<i class="fa fa-trash"></i>'
31219 cls : 'btn-group roo-upload-cropbox-rotate-right',
31220 action : 'rotate-right',
31224 cls : 'btn btn-default',
31225 html : '<i class="fa fa-repeat"></i>'
31233 cls : 'btn-group roo-upload-cropbox-rotate-left',
31234 action : 'rotate-left',
31238 cls : 'btn btn-default',
31239 html : '<i class="fa fa-undo"></i>'
31245 cls : 'btn-group roo-upload-cropbox-rotate-right',
31246 action : 'rotate-right',
31250 cls : 'btn btn-default',
31251 html : '<i class="fa fa-repeat"></i>'
31264 * @class Roo.bootstrap.DocumentManager
31265 * @extends Roo.bootstrap.Component
31266 * Bootstrap DocumentManager class
31267 * @cfg {String} paramName default 'imageUpload'
31268 * @cfg {String} toolTipName default 'filename'
31269 * @cfg {String} method default POST
31270 * @cfg {String} url action url
31271 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31272 * @cfg {Boolean} multiple multiple upload default true
31273 * @cfg {Number} thumbSize default 300
31274 * @cfg {String} fieldLabel
31275 * @cfg {Number} labelWidth default 4
31276 * @cfg {String} labelAlign (left|top) default left
31277 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31278 * @cfg {Number} labellg set the width of label (1-12)
31279 * @cfg {Number} labelmd set the width of label (1-12)
31280 * @cfg {Number} labelsm set the width of label (1-12)
31281 * @cfg {Number} labelxs set the width of label (1-12)
31284 * Create a new DocumentManager
31285 * @param {Object} config The config object
31288 Roo.bootstrap.DocumentManager = function(config){
31289 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31292 this.delegates = [];
31297 * Fire when initial the DocumentManager
31298 * @param {Roo.bootstrap.DocumentManager} this
31303 * inspect selected file
31304 * @param {Roo.bootstrap.DocumentManager} this
31305 * @param {File} file
31310 * Fire when xhr load exception
31311 * @param {Roo.bootstrap.DocumentManager} this
31312 * @param {XMLHttpRequest} xhr
31314 "exception" : true,
31316 * @event afterupload
31317 * Fire when xhr load exception
31318 * @param {Roo.bootstrap.DocumentManager} this
31319 * @param {XMLHttpRequest} xhr
31321 "afterupload" : true,
31324 * prepare the form data
31325 * @param {Roo.bootstrap.DocumentManager} this
31326 * @param {Object} formData
31331 * Fire when remove the file
31332 * @param {Roo.bootstrap.DocumentManager} this
31333 * @param {Object} file
31338 * Fire after refresh the file
31339 * @param {Roo.bootstrap.DocumentManager} this
31344 * Fire after click the image
31345 * @param {Roo.bootstrap.DocumentManager} this
31346 * @param {Object} file
31351 * Fire when upload a image and editable set to true
31352 * @param {Roo.bootstrap.DocumentManager} this
31353 * @param {Object} file
31357 * @event beforeselectfile
31358 * Fire before select file
31359 * @param {Roo.bootstrap.DocumentManager} this
31361 "beforeselectfile" : true,
31364 * Fire before process file
31365 * @param {Roo.bootstrap.DocumentManager} this
31366 * @param {Object} file
31370 * @event previewrendered
31371 * Fire when preview rendered
31372 * @param {Roo.bootstrap.DocumentManager} this
31373 * @param {Object} file
31375 "previewrendered" : true,
31378 "previewResize" : true
31383 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31392 paramName : 'imageUpload',
31393 toolTipName : 'filename',
31396 labelAlign : 'left',
31406 getAutoCreate : function()
31408 var managerWidget = {
31410 cls : 'roo-document-manager',
31414 cls : 'roo-document-manager-selector',
31419 cls : 'roo-document-manager-uploader',
31423 cls : 'roo-document-manager-upload-btn',
31424 html : '<i class="fa fa-plus"></i>'
31435 cls : 'column col-md-12',
31440 if(this.fieldLabel.length){
31445 cls : 'column col-md-12',
31446 html : this.fieldLabel
31450 cls : 'column col-md-12',
31455 if(this.labelAlign == 'left'){
31460 html : this.fieldLabel
31469 if(this.labelWidth > 12){
31470 content[0].style = "width: " + this.labelWidth + 'px';
31473 if(this.labelWidth < 13 && this.labelmd == 0){
31474 this.labelmd = this.labelWidth;
31477 if(this.labellg > 0){
31478 content[0].cls += ' col-lg-' + this.labellg;
31479 content[1].cls += ' col-lg-' + (12 - this.labellg);
31482 if(this.labelmd > 0){
31483 content[0].cls += ' col-md-' + this.labelmd;
31484 content[1].cls += ' col-md-' + (12 - this.labelmd);
31487 if(this.labelsm > 0){
31488 content[0].cls += ' col-sm-' + this.labelsm;
31489 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31492 if(this.labelxs > 0){
31493 content[0].cls += ' col-xs-' + this.labelxs;
31494 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31502 cls : 'row clearfix',
31510 initEvents : function()
31512 this.managerEl = this.el.select('.roo-document-manager', true).first();
31513 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31515 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31516 this.selectorEl.hide();
31519 this.selectorEl.attr('multiple', 'multiple');
31522 this.selectorEl.on('change', this.onFileSelected, this);
31524 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31525 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31527 this.uploader.on('click', this.onUploaderClick, this);
31529 this.renderProgressDialog();
31533 window.addEventListener("resize", function() { _this.refresh(); } );
31535 this.fireEvent('initial', this);
31538 renderProgressDialog : function()
31542 this.progressDialog = new Roo.bootstrap.Modal({
31543 cls : 'roo-document-manager-progress-dialog',
31544 allow_close : false,
31555 btnclick : function() {
31556 _this.uploadCancel();
31562 this.progressDialog.render(Roo.get(document.body));
31564 this.progress = new Roo.bootstrap.Progress({
31565 cls : 'roo-document-manager-progress',
31570 this.progress.render(this.progressDialog.getChildContainer());
31572 this.progressBar = new Roo.bootstrap.ProgressBar({
31573 cls : 'roo-document-manager-progress-bar',
31576 aria_valuemax : 12,
31580 this.progressBar.render(this.progress.getChildContainer());
31583 onUploaderClick : function(e)
31585 e.preventDefault();
31587 if(this.fireEvent('beforeselectfile', this) != false){
31588 this.selectorEl.dom.click();
31593 onFileSelected : function(e)
31595 e.preventDefault();
31597 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31601 Roo.each(this.selectorEl.dom.files, function(file){
31602 if(this.fireEvent('inspect', this, file) != false){
31603 this.files.push(file);
31613 this.selectorEl.dom.value = '';
31615 if(!this.files || !this.files.length){
31619 if(this.boxes > 0 && this.files.length > this.boxes){
31620 this.files = this.files.slice(0, this.boxes);
31623 this.uploader.show();
31625 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31626 this.uploader.hide();
31635 Roo.each(this.files, function(file){
31637 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31638 var f = this.renderPreview(file);
31643 if(file.type.indexOf('image') != -1){
31644 this.delegates.push(
31646 _this.process(file);
31647 }).createDelegate(this)
31655 _this.process(file);
31656 }).createDelegate(this)
31661 this.files = files;
31663 this.delegates = this.delegates.concat(docs);
31665 if(!this.delegates.length){
31670 this.progressBar.aria_valuemax = this.delegates.length;
31677 arrange : function()
31679 if(!this.delegates.length){
31680 this.progressDialog.hide();
31685 var delegate = this.delegates.shift();
31687 this.progressDialog.show();
31689 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31691 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31696 refresh : function()
31698 this.uploader.show();
31700 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31701 this.uploader.hide();
31704 Roo.isTouch ? this.closable(false) : this.closable(true);
31706 this.fireEvent('refresh', this);
31709 onRemove : function(e, el, o)
31711 e.preventDefault();
31713 this.fireEvent('remove', this, o);
31717 remove : function(o)
31721 Roo.each(this.files, function(file){
31722 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31731 this.files = files;
31738 Roo.each(this.files, function(file){
31743 file.target.remove();
31752 onClick : function(e, el, o)
31754 e.preventDefault();
31756 this.fireEvent('click', this, o);
31760 closable : function(closable)
31762 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31764 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31776 xhrOnLoad : function(xhr)
31778 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31782 if (xhr.readyState !== 4) {
31784 this.fireEvent('exception', this, xhr);
31788 var response = Roo.decode(xhr.responseText);
31790 if(!response.success){
31792 this.fireEvent('exception', this, xhr);
31796 var file = this.renderPreview(response.data);
31798 this.files.push(file);
31802 this.fireEvent('afterupload', this, xhr);
31806 xhrOnError : function(xhr)
31808 Roo.log('xhr on error');
31810 var response = Roo.decode(xhr.responseText);
31817 process : function(file)
31819 if(this.fireEvent('process', this, file) !== false){
31820 if(this.editable && file.type.indexOf('image') != -1){
31821 this.fireEvent('edit', this, file);
31825 this.uploadStart(file, false);
31832 uploadStart : function(file, crop)
31834 this.xhr = new XMLHttpRequest();
31836 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31841 file.xhr = this.xhr;
31843 this.managerEl.createChild({
31845 cls : 'roo-document-manager-loading',
31849 tooltip : file.name,
31850 cls : 'roo-document-manager-thumb',
31851 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31857 this.xhr.open(this.method, this.url, true);
31860 "Accept": "application/json",
31861 "Cache-Control": "no-cache",
31862 "X-Requested-With": "XMLHttpRequest"
31865 for (var headerName in headers) {
31866 var headerValue = headers[headerName];
31868 this.xhr.setRequestHeader(headerName, headerValue);
31874 this.xhr.onload = function()
31876 _this.xhrOnLoad(_this.xhr);
31879 this.xhr.onerror = function()
31881 _this.xhrOnError(_this.xhr);
31884 var formData = new FormData();
31886 formData.append('returnHTML', 'NO');
31889 formData.append('crop', crop);
31892 formData.append(this.paramName, file, file.name);
31899 if(this.fireEvent('prepare', this, formData, options) != false){
31901 if(options.manually){
31905 this.xhr.send(formData);
31909 this.uploadCancel();
31912 uploadCancel : function()
31918 this.delegates = [];
31920 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31927 renderPreview : function(file)
31929 if(typeof(file.target) != 'undefined' && file.target){
31933 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31935 var previewEl = this.managerEl.createChild({
31937 cls : 'roo-document-manager-preview',
31941 tooltip : file[this.toolTipName],
31942 cls : 'roo-document-manager-thumb',
31943 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31948 html : '<i class="fa fa-times-circle"></i>'
31953 var close = previewEl.select('button.close', true).first();
31955 close.on('click', this.onRemove, this, file);
31957 file.target = previewEl;
31959 var image = previewEl.select('img', true).first();
31963 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31965 image.on('click', this.onClick, this, file);
31967 this.fireEvent('previewrendered', this, file);
31973 onPreviewLoad : function(file, image)
31975 if(typeof(file.target) == 'undefined' || !file.target){
31979 var width = image.dom.naturalWidth || image.dom.width;
31980 var height = image.dom.naturalHeight || image.dom.height;
31982 if(!this.previewResize) {
31986 if(width > height){
31987 file.target.addClass('wide');
31991 file.target.addClass('tall');
31996 uploadFromSource : function(file, crop)
31998 this.xhr = new XMLHttpRequest();
32000 this.managerEl.createChild({
32002 cls : 'roo-document-manager-loading',
32006 tooltip : file.name,
32007 cls : 'roo-document-manager-thumb',
32008 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32014 this.xhr.open(this.method, this.url, true);
32017 "Accept": "application/json",
32018 "Cache-Control": "no-cache",
32019 "X-Requested-With": "XMLHttpRequest"
32022 for (var headerName in headers) {
32023 var headerValue = headers[headerName];
32025 this.xhr.setRequestHeader(headerName, headerValue);
32031 this.xhr.onload = function()
32033 _this.xhrOnLoad(_this.xhr);
32036 this.xhr.onerror = function()
32038 _this.xhrOnError(_this.xhr);
32041 var formData = new FormData();
32043 formData.append('returnHTML', 'NO');
32045 formData.append('crop', crop);
32047 if(typeof(file.filename) != 'undefined'){
32048 formData.append('filename', file.filename);
32051 if(typeof(file.mimetype) != 'undefined'){
32052 formData.append('mimetype', file.mimetype);
32057 if(this.fireEvent('prepare', this, formData) != false){
32058 this.xhr.send(formData);
32068 * @class Roo.bootstrap.DocumentViewer
32069 * @extends Roo.bootstrap.Component
32070 * Bootstrap DocumentViewer class
32071 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32072 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32075 * Create a new DocumentViewer
32076 * @param {Object} config The config object
32079 Roo.bootstrap.DocumentViewer = function(config){
32080 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32085 * Fire after initEvent
32086 * @param {Roo.bootstrap.DocumentViewer} this
32092 * @param {Roo.bootstrap.DocumentViewer} this
32097 * Fire after download button
32098 * @param {Roo.bootstrap.DocumentViewer} this
32103 * Fire after trash button
32104 * @param {Roo.bootstrap.DocumentViewer} this
32111 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32113 showDownload : true,
32117 getAutoCreate : function()
32121 cls : 'roo-document-viewer',
32125 cls : 'roo-document-viewer-body',
32129 cls : 'roo-document-viewer-thumb',
32133 cls : 'roo-document-viewer-image'
32141 cls : 'roo-document-viewer-footer',
32144 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32148 cls : 'btn-group roo-document-viewer-download',
32152 cls : 'btn btn-default',
32153 html : '<i class="fa fa-download"></i>'
32159 cls : 'btn-group roo-document-viewer-trash',
32163 cls : 'btn btn-default',
32164 html : '<i class="fa fa-trash"></i>'
32177 initEvents : function()
32179 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32180 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32182 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32183 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32185 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32186 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32188 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32189 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32191 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32192 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32194 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32195 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32197 this.bodyEl.on('click', this.onClick, this);
32198 this.downloadBtn.on('click', this.onDownload, this);
32199 this.trashBtn.on('click', this.onTrash, this);
32201 this.downloadBtn.hide();
32202 this.trashBtn.hide();
32204 if(this.showDownload){
32205 this.downloadBtn.show();
32208 if(this.showTrash){
32209 this.trashBtn.show();
32212 if(!this.showDownload && !this.showTrash) {
32213 this.footerEl.hide();
32218 initial : function()
32220 this.fireEvent('initial', this);
32224 onClick : function(e)
32226 e.preventDefault();
32228 this.fireEvent('click', this);
32231 onDownload : function(e)
32233 e.preventDefault();
32235 this.fireEvent('download', this);
32238 onTrash : function(e)
32240 e.preventDefault();
32242 this.fireEvent('trash', this);
32254 * @class Roo.bootstrap.NavProgressBar
32255 * @extends Roo.bootstrap.Component
32256 * Bootstrap NavProgressBar class
32259 * Create a new nav progress bar
32260 * @param {Object} config The config object
32263 Roo.bootstrap.NavProgressBar = function(config){
32264 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32266 this.bullets = this.bullets || [];
32268 // Roo.bootstrap.NavProgressBar.register(this);
32272 * Fires when the active item changes
32273 * @param {Roo.bootstrap.NavProgressBar} this
32274 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32275 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32282 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32287 getAutoCreate : function()
32289 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32293 cls : 'roo-navigation-bar-group',
32297 cls : 'roo-navigation-top-bar'
32301 cls : 'roo-navigation-bullets-bar',
32305 cls : 'roo-navigation-bar'
32312 cls : 'roo-navigation-bottom-bar'
32322 initEvents: function()
32327 onRender : function(ct, position)
32329 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32331 if(this.bullets.length){
32332 Roo.each(this.bullets, function(b){
32341 addItem : function(cfg)
32343 var item = new Roo.bootstrap.NavProgressItem(cfg);
32345 item.parentId = this.id;
32346 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32349 var top = new Roo.bootstrap.Element({
32351 cls : 'roo-navigation-bar-text'
32354 var bottom = new Roo.bootstrap.Element({
32356 cls : 'roo-navigation-bar-text'
32359 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32360 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32362 var topText = new Roo.bootstrap.Element({
32364 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32367 var bottomText = new Roo.bootstrap.Element({
32369 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32372 topText.onRender(top.el, null);
32373 bottomText.onRender(bottom.el, null);
32376 item.bottomEl = bottom;
32379 this.barItems.push(item);
32384 getActive : function()
32386 var active = false;
32388 Roo.each(this.barItems, function(v){
32390 if (!v.isActive()) {
32402 setActiveItem : function(item)
32406 Roo.each(this.barItems, function(v){
32407 if (v.rid == item.rid) {
32411 if (v.isActive()) {
32412 v.setActive(false);
32417 item.setActive(true);
32419 this.fireEvent('changed', this, item, prev);
32422 getBarItem: function(rid)
32426 Roo.each(this.barItems, function(e) {
32427 if (e.rid != rid) {
32438 indexOfItem : function(item)
32442 Roo.each(this.barItems, function(v, i){
32444 if (v.rid != item.rid) {
32455 setActiveNext : function()
32457 var i = this.indexOfItem(this.getActive());
32459 if (i > this.barItems.length) {
32463 this.setActiveItem(this.barItems[i+1]);
32466 setActivePrev : function()
32468 var i = this.indexOfItem(this.getActive());
32474 this.setActiveItem(this.barItems[i-1]);
32477 format : function()
32479 if(!this.barItems.length){
32483 var width = 100 / this.barItems.length;
32485 Roo.each(this.barItems, function(i){
32486 i.el.setStyle('width', width + '%');
32487 i.topEl.el.setStyle('width', width + '%');
32488 i.bottomEl.el.setStyle('width', width + '%');
32497 * Nav Progress Item
32502 * @class Roo.bootstrap.NavProgressItem
32503 * @extends Roo.bootstrap.Component
32504 * Bootstrap NavProgressItem class
32505 * @cfg {String} rid the reference id
32506 * @cfg {Boolean} active (true|false) Is item active default false
32507 * @cfg {Boolean} disabled (true|false) Is item active default false
32508 * @cfg {String} html
32509 * @cfg {String} position (top|bottom) text position default bottom
32510 * @cfg {String} icon show icon instead of number
32513 * Create a new NavProgressItem
32514 * @param {Object} config The config object
32516 Roo.bootstrap.NavProgressItem = function(config){
32517 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32522 * The raw click event for the entire grid.
32523 * @param {Roo.bootstrap.NavProgressItem} this
32524 * @param {Roo.EventObject} e
32531 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32537 position : 'bottom',
32540 getAutoCreate : function()
32542 var iconCls = 'roo-navigation-bar-item-icon';
32544 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32548 cls: 'roo-navigation-bar-item',
32558 cfg.cls += ' active';
32561 cfg.cls += ' disabled';
32567 disable : function()
32569 this.setDisabled(true);
32572 enable : function()
32574 this.setDisabled(false);
32577 initEvents: function()
32579 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32581 this.iconEl.on('click', this.onClick, this);
32584 onClick : function(e)
32586 e.preventDefault();
32592 if(this.fireEvent('click', this, e) === false){
32596 this.parent().setActiveItem(this);
32599 isActive: function ()
32601 return this.active;
32604 setActive : function(state)
32606 if(this.active == state){
32610 this.active = state;
32613 this.el.addClass('active');
32617 this.el.removeClass('active');
32622 setDisabled : function(state)
32624 if(this.disabled == state){
32628 this.disabled = state;
32631 this.el.addClass('disabled');
32635 this.el.removeClass('disabled');
32638 tooltipEl : function()
32640 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32653 * @class Roo.bootstrap.FieldLabel
32654 * @extends Roo.bootstrap.Component
32655 * Bootstrap FieldLabel class
32656 * @cfg {String} html contents of the element
32657 * @cfg {String} tag tag of the element default label
32658 * @cfg {String} cls class of the element
32659 * @cfg {String} target label target
32660 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32661 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32662 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32663 * @cfg {String} iconTooltip default "This field is required"
32664 * @cfg {String} indicatorpos (left|right) default left
32667 * Create a new FieldLabel
32668 * @param {Object} config The config object
32671 Roo.bootstrap.FieldLabel = function(config){
32672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32677 * Fires after the field has been marked as invalid.
32678 * @param {Roo.form.FieldLabel} this
32679 * @param {String} msg The validation message
32684 * Fires after the field has been validated with no errors.
32685 * @param {Roo.form.FieldLabel} this
32691 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32698 invalidClass : 'has-warning',
32699 validClass : 'has-success',
32700 iconTooltip : 'This field is required',
32701 indicatorpos : 'left',
32703 getAutoCreate : function(){
32706 if (!this.allowBlank) {
32712 cls : 'roo-bootstrap-field-label ' + this.cls,
32717 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32718 tooltip : this.iconTooltip
32727 if(this.indicatorpos == 'right'){
32730 cls : 'roo-bootstrap-field-label ' + this.cls,
32739 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32740 tooltip : this.iconTooltip
32749 initEvents: function()
32751 Roo.bootstrap.Element.superclass.initEvents.call(this);
32753 this.indicator = this.indicatorEl();
32755 if(this.indicator){
32756 this.indicator.removeClass('visible');
32757 this.indicator.addClass('invisible');
32760 Roo.bootstrap.FieldLabel.register(this);
32763 indicatorEl : function()
32765 var indicator = this.el.select('i.roo-required-indicator',true).first();
32776 * Mark this field as valid
32778 markValid : function()
32780 if(this.indicator){
32781 this.indicator.removeClass('visible');
32782 this.indicator.addClass('invisible');
32784 if (Roo.bootstrap.version == 3) {
32785 this.el.removeClass(this.invalidClass);
32786 this.el.addClass(this.validClass);
32788 this.el.removeClass('is-invalid');
32789 this.el.addClass('is-valid');
32793 this.fireEvent('valid', this);
32797 * Mark this field as invalid
32798 * @param {String} msg The validation message
32800 markInvalid : function(msg)
32802 if(this.indicator){
32803 this.indicator.removeClass('invisible');
32804 this.indicator.addClass('visible');
32806 if (Roo.bootstrap.version == 3) {
32807 this.el.removeClass(this.validClass);
32808 this.el.addClass(this.invalidClass);
32810 this.el.removeClass('is-valid');
32811 this.el.addClass('is-invalid');
32815 this.fireEvent('invalid', this, msg);
32821 Roo.apply(Roo.bootstrap.FieldLabel, {
32826 * register a FieldLabel Group
32827 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32829 register : function(label)
32831 if(this.groups.hasOwnProperty(label.target)){
32835 this.groups[label.target] = label;
32839 * fetch a FieldLabel Group based on the target
32840 * @param {string} target
32841 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32843 get: function(target) {
32844 if (typeof(this.groups[target]) == 'undefined') {
32848 return this.groups[target] ;
32857 * page DateSplitField.
32863 * @class Roo.bootstrap.DateSplitField
32864 * @extends Roo.bootstrap.Component
32865 * Bootstrap DateSplitField class
32866 * @cfg {string} fieldLabel - the label associated
32867 * @cfg {Number} labelWidth set the width of label (0-12)
32868 * @cfg {String} labelAlign (top|left)
32869 * @cfg {Boolean} dayAllowBlank (true|false) default false
32870 * @cfg {Boolean} monthAllowBlank (true|false) default false
32871 * @cfg {Boolean} yearAllowBlank (true|false) default false
32872 * @cfg {string} dayPlaceholder
32873 * @cfg {string} monthPlaceholder
32874 * @cfg {string} yearPlaceholder
32875 * @cfg {string} dayFormat default 'd'
32876 * @cfg {string} monthFormat default 'm'
32877 * @cfg {string} yearFormat default 'Y'
32878 * @cfg {Number} labellg set the width of label (1-12)
32879 * @cfg {Number} labelmd set the width of label (1-12)
32880 * @cfg {Number} labelsm set the width of label (1-12)
32881 * @cfg {Number} labelxs set the width of label (1-12)
32885 * Create a new DateSplitField
32886 * @param {Object} config The config object
32889 Roo.bootstrap.DateSplitField = function(config){
32890 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32896 * getting the data of years
32897 * @param {Roo.bootstrap.DateSplitField} this
32898 * @param {Object} years
32903 * getting the data of days
32904 * @param {Roo.bootstrap.DateSplitField} this
32905 * @param {Object} days
32910 * Fires after the field has been marked as invalid.
32911 * @param {Roo.form.Field} this
32912 * @param {String} msg The validation message
32917 * Fires after the field has been validated with no errors.
32918 * @param {Roo.form.Field} this
32924 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32927 labelAlign : 'top',
32929 dayAllowBlank : false,
32930 monthAllowBlank : false,
32931 yearAllowBlank : false,
32932 dayPlaceholder : '',
32933 monthPlaceholder : '',
32934 yearPlaceholder : '',
32938 isFormField : true,
32944 getAutoCreate : function()
32948 cls : 'row roo-date-split-field-group',
32953 cls : 'form-hidden-field roo-date-split-field-group-value',
32959 var labelCls = 'col-md-12';
32960 var contentCls = 'col-md-4';
32962 if(this.fieldLabel){
32966 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32970 html : this.fieldLabel
32975 if(this.labelAlign == 'left'){
32977 if(this.labelWidth > 12){
32978 label.style = "width: " + this.labelWidth + 'px';
32981 if(this.labelWidth < 13 && this.labelmd == 0){
32982 this.labelmd = this.labelWidth;
32985 if(this.labellg > 0){
32986 labelCls = ' col-lg-' + this.labellg;
32987 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32990 if(this.labelmd > 0){
32991 labelCls = ' col-md-' + this.labelmd;
32992 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32995 if(this.labelsm > 0){
32996 labelCls = ' col-sm-' + this.labelsm;
32997 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33000 if(this.labelxs > 0){
33001 labelCls = ' col-xs-' + this.labelxs;
33002 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33006 label.cls += ' ' + labelCls;
33008 cfg.cn.push(label);
33011 Roo.each(['day', 'month', 'year'], function(t){
33014 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33021 inputEl: function ()
33023 return this.el.select('.roo-date-split-field-group-value', true).first();
33026 onRender : function(ct, position)
33030 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33032 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33034 this.dayField = new Roo.bootstrap.ComboBox({
33035 allowBlank : this.dayAllowBlank,
33036 alwaysQuery : true,
33037 displayField : 'value',
33040 forceSelection : true,
33042 placeholder : this.dayPlaceholder,
33043 selectOnFocus : true,
33044 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33045 triggerAction : 'all',
33047 valueField : 'value',
33048 store : new Roo.data.SimpleStore({
33049 data : (function() {
33051 _this.fireEvent('days', _this, days);
33054 fields : [ 'value' ]
33057 select : function (_self, record, index)
33059 _this.setValue(_this.getValue());
33064 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33066 this.monthField = new Roo.bootstrap.MonthField({
33067 after : '<i class=\"fa fa-calendar\"></i>',
33068 allowBlank : this.monthAllowBlank,
33069 placeholder : this.monthPlaceholder,
33072 render : function (_self)
33074 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33075 e.preventDefault();
33079 select : function (_self, oldvalue, newvalue)
33081 _this.setValue(_this.getValue());
33086 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33088 this.yearField = new Roo.bootstrap.ComboBox({
33089 allowBlank : this.yearAllowBlank,
33090 alwaysQuery : true,
33091 displayField : 'value',
33094 forceSelection : true,
33096 placeholder : this.yearPlaceholder,
33097 selectOnFocus : true,
33098 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33099 triggerAction : 'all',
33101 valueField : 'value',
33102 store : new Roo.data.SimpleStore({
33103 data : (function() {
33105 _this.fireEvent('years', _this, years);
33108 fields : [ 'value' ]
33111 select : function (_self, record, index)
33113 _this.setValue(_this.getValue());
33118 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33121 setValue : function(v, format)
33123 this.inputEl.dom.value = v;
33125 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33127 var d = Date.parseDate(v, f);
33134 this.setDay(d.format(this.dayFormat));
33135 this.setMonth(d.format(this.monthFormat));
33136 this.setYear(d.format(this.yearFormat));
33143 setDay : function(v)
33145 this.dayField.setValue(v);
33146 this.inputEl.dom.value = this.getValue();
33151 setMonth : function(v)
33153 this.monthField.setValue(v, true);
33154 this.inputEl.dom.value = this.getValue();
33159 setYear : function(v)
33161 this.yearField.setValue(v);
33162 this.inputEl.dom.value = this.getValue();
33167 getDay : function()
33169 return this.dayField.getValue();
33172 getMonth : function()
33174 return this.monthField.getValue();
33177 getYear : function()
33179 return this.yearField.getValue();
33182 getValue : function()
33184 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33186 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33196 this.inputEl.dom.value = '';
33201 validate : function()
33203 var d = this.dayField.validate();
33204 var m = this.monthField.validate();
33205 var y = this.yearField.validate();
33210 (!this.dayAllowBlank && !d) ||
33211 (!this.monthAllowBlank && !m) ||
33212 (!this.yearAllowBlank && !y)
33217 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33226 this.markInvalid();
33231 markValid : function()
33234 var label = this.el.select('label', true).first();
33235 var icon = this.el.select('i.fa-star', true).first();
33241 this.fireEvent('valid', this);
33245 * Mark this field as invalid
33246 * @param {String} msg The validation message
33248 markInvalid : function(msg)
33251 var label = this.el.select('label', true).first();
33252 var icon = this.el.select('i.fa-star', true).first();
33254 if(label && !icon){
33255 this.el.select('.roo-date-split-field-label', true).createChild({
33257 cls : 'text-danger fa fa-lg fa-star',
33258 tooltip : 'This field is required',
33259 style : 'margin-right:5px;'
33263 this.fireEvent('invalid', this, msg);
33266 clearInvalid : function()
33268 var label = this.el.select('label', true).first();
33269 var icon = this.el.select('i.fa-star', true).first();
33275 this.fireEvent('valid', this);
33278 getName: function()
33288 * http://masonry.desandro.com
33290 * The idea is to render all the bricks based on vertical width...
33292 * The original code extends 'outlayer' - we might need to use that....
33298 * @class Roo.bootstrap.LayoutMasonry
33299 * @extends Roo.bootstrap.Component
33300 * Bootstrap Layout Masonry class
33303 * Create a new Element
33304 * @param {Object} config The config object
33307 Roo.bootstrap.LayoutMasonry = function(config){
33309 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33313 Roo.bootstrap.LayoutMasonry.register(this);
33319 * Fire after layout the items
33320 * @param {Roo.bootstrap.LayoutMasonry} this
33321 * @param {Roo.EventObject} e
33328 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33331 * @cfg {Boolean} isLayoutInstant = no animation?
33333 isLayoutInstant : false, // needed?
33336 * @cfg {Number} boxWidth width of the columns
33341 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33346 * @cfg {Number} padWidth padding below box..
33351 * @cfg {Number} gutter gutter width..
33356 * @cfg {Number} maxCols maximum number of columns
33362 * @cfg {Boolean} isAutoInitial defalut true
33364 isAutoInitial : true,
33369 * @cfg {Boolean} isHorizontal defalut false
33371 isHorizontal : false,
33373 currentSize : null,
33379 bricks: null, //CompositeElement
33383 _isLayoutInited : false,
33385 // isAlternative : false, // only use for vertical layout...
33388 * @cfg {Number} alternativePadWidth padding below box..
33390 alternativePadWidth : 50,
33392 selectedBrick : [],
33394 getAutoCreate : function(){
33396 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33400 cls: 'blog-masonary-wrapper ' + this.cls,
33402 cls : 'mas-boxes masonary'
33409 getChildContainer: function( )
33411 if (this.boxesEl) {
33412 return this.boxesEl;
33415 this.boxesEl = this.el.select('.mas-boxes').first();
33417 return this.boxesEl;
33421 initEvents : function()
33425 if(this.isAutoInitial){
33426 Roo.log('hook children rendered');
33427 this.on('childrenrendered', function() {
33428 Roo.log('children rendered');
33434 initial : function()
33436 this.selectedBrick = [];
33438 this.currentSize = this.el.getBox(true);
33440 Roo.EventManager.onWindowResize(this.resize, this);
33442 if(!this.isAutoInitial){
33450 //this.layout.defer(500,this);
33454 resize : function()
33456 var cs = this.el.getBox(true);
33459 this.currentSize.width == cs.width &&
33460 this.currentSize.x == cs.x &&
33461 this.currentSize.height == cs.height &&
33462 this.currentSize.y == cs.y
33464 Roo.log("no change in with or X or Y");
33468 this.currentSize = cs;
33474 layout : function()
33476 this._resetLayout();
33478 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33480 this.layoutItems( isInstant );
33482 this._isLayoutInited = true;
33484 this.fireEvent('layout', this);
33488 _resetLayout : function()
33490 if(this.isHorizontal){
33491 this.horizontalMeasureColumns();
33495 this.verticalMeasureColumns();
33499 verticalMeasureColumns : function()
33501 this.getContainerWidth();
33503 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33504 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33508 var boxWidth = this.boxWidth + this.padWidth;
33510 if(this.containerWidth < this.boxWidth){
33511 boxWidth = this.containerWidth
33514 var containerWidth = this.containerWidth;
33516 var cols = Math.floor(containerWidth / boxWidth);
33518 this.cols = Math.max( cols, 1 );
33520 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33522 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33524 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33526 this.colWidth = boxWidth + avail - this.padWidth;
33528 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33529 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33532 horizontalMeasureColumns : function()
33534 this.getContainerWidth();
33536 var boxWidth = this.boxWidth;
33538 if(this.containerWidth < boxWidth){
33539 boxWidth = this.containerWidth;
33542 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33544 this.el.setHeight(boxWidth);
33548 getContainerWidth : function()
33550 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33553 layoutItems : function( isInstant )
33555 Roo.log(this.bricks);
33557 var items = Roo.apply([], this.bricks);
33559 if(this.isHorizontal){
33560 this._horizontalLayoutItems( items , isInstant );
33564 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33565 // this._verticalAlternativeLayoutItems( items , isInstant );
33569 this._verticalLayoutItems( items , isInstant );
33573 _verticalLayoutItems : function ( items , isInstant)
33575 if ( !items || !items.length ) {
33580 ['xs', 'xs', 'xs', 'tall'],
33581 ['xs', 'xs', 'tall'],
33582 ['xs', 'xs', 'sm'],
33583 ['xs', 'xs', 'xs'],
33589 ['sm', 'xs', 'xs'],
33593 ['tall', 'xs', 'xs', 'xs'],
33594 ['tall', 'xs', 'xs'],
33606 Roo.each(items, function(item, k){
33608 switch (item.size) {
33609 // these layouts take up a full box,
33620 boxes.push([item]);
33643 var filterPattern = function(box, length)
33651 var pattern = box.slice(0, length);
33655 Roo.each(pattern, function(i){
33656 format.push(i.size);
33659 Roo.each(standard, function(s){
33661 if(String(s) != String(format)){
33670 if(!match && length == 1){
33675 filterPattern(box, length - 1);
33679 queue.push(pattern);
33681 box = box.slice(length, box.length);
33683 filterPattern(box, 4);
33689 Roo.each(boxes, function(box, k){
33695 if(box.length == 1){
33700 filterPattern(box, 4);
33704 this._processVerticalLayoutQueue( queue, isInstant );
33708 // _verticalAlternativeLayoutItems : function( items , isInstant )
33710 // if ( !items || !items.length ) {
33714 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33718 _horizontalLayoutItems : function ( items , isInstant)
33720 if ( !items || !items.length || items.length < 3) {
33726 var eItems = items.slice(0, 3);
33728 items = items.slice(3, items.length);
33731 ['xs', 'xs', 'xs', 'wide'],
33732 ['xs', 'xs', 'wide'],
33733 ['xs', 'xs', 'sm'],
33734 ['xs', 'xs', 'xs'],
33740 ['sm', 'xs', 'xs'],
33744 ['wide', 'xs', 'xs', 'xs'],
33745 ['wide', 'xs', 'xs'],
33758 Roo.each(items, function(item, k){
33760 switch (item.size) {
33771 boxes.push([item]);
33795 var filterPattern = function(box, length)
33803 var pattern = box.slice(0, length);
33807 Roo.each(pattern, function(i){
33808 format.push(i.size);
33811 Roo.each(standard, function(s){
33813 if(String(s) != String(format)){
33822 if(!match && length == 1){
33827 filterPattern(box, length - 1);
33831 queue.push(pattern);
33833 box = box.slice(length, box.length);
33835 filterPattern(box, 4);
33841 Roo.each(boxes, function(box, k){
33847 if(box.length == 1){
33852 filterPattern(box, 4);
33859 var pos = this.el.getBox(true);
33863 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33865 var hit_end = false;
33867 Roo.each(queue, function(box){
33871 Roo.each(box, function(b){
33873 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33883 Roo.each(box, function(b){
33885 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33888 mx = Math.max(mx, b.x);
33892 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33896 Roo.each(box, function(b){
33898 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33912 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33915 /** Sets position of item in DOM
33916 * @param {Element} item
33917 * @param {Number} x - horizontal position
33918 * @param {Number} y - vertical position
33919 * @param {Boolean} isInstant - disables transitions
33921 _processVerticalLayoutQueue : function( queue, isInstant )
33923 var pos = this.el.getBox(true);
33928 for (var i = 0; i < this.cols; i++){
33932 Roo.each(queue, function(box, k){
33934 var col = k % this.cols;
33936 Roo.each(box, function(b,kk){
33938 b.el.position('absolute');
33940 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33941 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33943 if(b.size == 'md-left' || b.size == 'md-right'){
33944 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33945 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33948 b.el.setWidth(width);
33949 b.el.setHeight(height);
33951 b.el.select('iframe',true).setSize(width,height);
33955 for (var i = 0; i < this.cols; i++){
33957 if(maxY[i] < maxY[col]){
33962 col = Math.min(col, i);
33966 x = pos.x + col * (this.colWidth + this.padWidth);
33970 var positions = [];
33972 switch (box.length){
33974 positions = this.getVerticalOneBoxColPositions(x, y, box);
33977 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33980 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33983 positions = this.getVerticalFourBoxColPositions(x, y, box);
33989 Roo.each(box, function(b,kk){
33991 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33993 var sz = b.el.getSize();
33995 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34003 for (var i = 0; i < this.cols; i++){
34004 mY = Math.max(mY, maxY[i]);
34007 this.el.setHeight(mY - pos.y);
34011 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34013 // var pos = this.el.getBox(true);
34016 // var maxX = pos.right;
34018 // var maxHeight = 0;
34020 // Roo.each(items, function(item, k){
34024 // item.el.position('absolute');
34026 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34028 // item.el.setWidth(width);
34030 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34032 // item.el.setHeight(height);
34035 // item.el.setXY([x, y], isInstant ? false : true);
34037 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34040 // y = y + height + this.alternativePadWidth;
34042 // maxHeight = maxHeight + height + this.alternativePadWidth;
34046 // this.el.setHeight(maxHeight);
34050 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34052 var pos = this.el.getBox(true);
34057 var maxX = pos.right;
34059 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34061 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34063 Roo.each(queue, function(box, k){
34065 Roo.each(box, function(b, kk){
34067 b.el.position('absolute');
34069 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34070 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34072 if(b.size == 'md-left' || b.size == 'md-right'){
34073 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34074 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34077 b.el.setWidth(width);
34078 b.el.setHeight(height);
34086 var positions = [];
34088 switch (box.length){
34090 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34093 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34096 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34099 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34105 Roo.each(box, function(b,kk){
34107 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34109 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34117 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34119 Roo.each(eItems, function(b,k){
34121 b.size = (k == 0) ? 'sm' : 'xs';
34122 b.x = (k == 0) ? 2 : 1;
34123 b.y = (k == 0) ? 2 : 1;
34125 b.el.position('absolute');
34127 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34129 b.el.setWidth(width);
34131 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34133 b.el.setHeight(height);
34137 var positions = [];
34140 x : maxX - this.unitWidth * 2 - this.gutter,
34145 x : maxX - this.unitWidth,
34146 y : minY + (this.unitWidth + this.gutter) * 2
34150 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34154 Roo.each(eItems, function(b,k){
34156 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34162 getVerticalOneBoxColPositions : function(x, y, box)
34166 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34168 if(box[0].size == 'md-left'){
34172 if(box[0].size == 'md-right'){
34177 x : x + (this.unitWidth + this.gutter) * rand,
34184 getVerticalTwoBoxColPositions : function(x, y, box)
34188 if(box[0].size == 'xs'){
34192 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34196 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34210 x : x + (this.unitWidth + this.gutter) * 2,
34211 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34218 getVerticalThreeBoxColPositions : function(x, y, box)
34222 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34230 x : x + (this.unitWidth + this.gutter) * 1,
34235 x : x + (this.unitWidth + this.gutter) * 2,
34243 if(box[0].size == 'xs' && box[1].size == 'xs'){
34252 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34256 x : x + (this.unitWidth + this.gutter) * 1,
34270 x : x + (this.unitWidth + this.gutter) * 2,
34275 x : x + (this.unitWidth + this.gutter) * 2,
34276 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34283 getVerticalFourBoxColPositions : function(x, y, box)
34287 if(box[0].size == 'xs'){
34296 y : y + (this.unitHeight + this.gutter) * 1
34301 y : y + (this.unitHeight + this.gutter) * 2
34305 x : x + (this.unitWidth + this.gutter) * 1,
34319 x : x + (this.unitWidth + this.gutter) * 2,
34324 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34325 y : y + (this.unitHeight + this.gutter) * 1
34329 x : x + (this.unitWidth + this.gutter) * 2,
34330 y : y + (this.unitWidth + this.gutter) * 2
34337 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34341 if(box[0].size == 'md-left'){
34343 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34350 if(box[0].size == 'md-right'){
34352 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34353 y : minY + (this.unitWidth + this.gutter) * 1
34359 var rand = Math.floor(Math.random() * (4 - box[0].y));
34362 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34363 y : minY + (this.unitWidth + this.gutter) * rand
34370 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34374 if(box[0].size == 'xs'){
34377 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34382 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34383 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34391 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34396 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34397 y : minY + (this.unitWidth + this.gutter) * 2
34404 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34408 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34411 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34416 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34417 y : minY + (this.unitWidth + this.gutter) * 1
34421 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34422 y : minY + (this.unitWidth + this.gutter) * 2
34429 if(box[0].size == 'xs' && box[1].size == 'xs'){
34432 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34442 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34443 y : minY + (this.unitWidth + this.gutter) * 1
34451 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34456 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34457 y : minY + (this.unitWidth + this.gutter) * 2
34461 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34462 y : minY + (this.unitWidth + this.gutter) * 2
34469 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34473 if(box[0].size == 'xs'){
34476 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34481 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34486 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),
34491 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34492 y : minY + (this.unitWidth + this.gutter) * 1
34500 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34505 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34506 y : minY + (this.unitWidth + this.gutter) * 2
34510 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34511 y : minY + (this.unitWidth + this.gutter) * 2
34515 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),
34516 y : minY + (this.unitWidth + this.gutter) * 2
34524 * remove a Masonry Brick
34525 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34527 removeBrick : function(brick_id)
34533 for (var i = 0; i<this.bricks.length; i++) {
34534 if (this.bricks[i].id == brick_id) {
34535 this.bricks.splice(i,1);
34536 this.el.dom.removeChild(Roo.get(brick_id).dom);
34543 * adds a Masonry Brick
34544 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34546 addBrick : function(cfg)
34548 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34549 //this.register(cn);
34550 cn.parentId = this.id;
34551 cn.render(this.el);
34556 * register a Masonry Brick
34557 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34560 register : function(brick)
34562 this.bricks.push(brick);
34563 brick.masonryId = this.id;
34567 * clear all the Masonry Brick
34569 clearAll : function()
34572 //this.getChildContainer().dom.innerHTML = "";
34573 this.el.dom.innerHTML = '';
34576 getSelected : function()
34578 if (!this.selectedBrick) {
34582 return this.selectedBrick;
34586 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34590 * register a Masonry Layout
34591 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34594 register : function(layout)
34596 this.groups[layout.id] = layout;
34599 * fetch a Masonry Layout based on the masonry layout ID
34600 * @param {string} the masonry layout to add
34601 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34604 get: function(layout_id) {
34605 if (typeof(this.groups[layout_id]) == 'undefined') {
34608 return this.groups[layout_id] ;
34620 * http://masonry.desandro.com
34622 * The idea is to render all the bricks based on vertical width...
34624 * The original code extends 'outlayer' - we might need to use that....
34630 * @class Roo.bootstrap.LayoutMasonryAuto
34631 * @extends Roo.bootstrap.Component
34632 * Bootstrap Layout Masonry class
34635 * Create a new Element
34636 * @param {Object} config The config object
34639 Roo.bootstrap.LayoutMasonryAuto = function(config){
34640 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34643 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34646 * @cfg {Boolean} isFitWidth - resize the width..
34648 isFitWidth : false, // options..
34650 * @cfg {Boolean} isOriginLeft = left align?
34652 isOriginLeft : true,
34654 * @cfg {Boolean} isOriginTop = top align?
34656 isOriginTop : false,
34658 * @cfg {Boolean} isLayoutInstant = no animation?
34660 isLayoutInstant : false, // needed?
34662 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34664 isResizingContainer : true,
34666 * @cfg {Number} columnWidth width of the columns
34672 * @cfg {Number} maxCols maximum number of columns
34677 * @cfg {Number} padHeight padding below box..
34683 * @cfg {Boolean} isAutoInitial defalut true
34686 isAutoInitial : true,
34692 initialColumnWidth : 0,
34693 currentSize : null,
34695 colYs : null, // array.
34702 bricks: null, //CompositeElement
34703 cols : 0, // array?
34704 // element : null, // wrapped now this.el
34705 _isLayoutInited : null,
34708 getAutoCreate : function(){
34712 cls: 'blog-masonary-wrapper ' + this.cls,
34714 cls : 'mas-boxes masonary'
34721 getChildContainer: function( )
34723 if (this.boxesEl) {
34724 return this.boxesEl;
34727 this.boxesEl = this.el.select('.mas-boxes').first();
34729 return this.boxesEl;
34733 initEvents : function()
34737 if(this.isAutoInitial){
34738 Roo.log('hook children rendered');
34739 this.on('childrenrendered', function() {
34740 Roo.log('children rendered');
34747 initial : function()
34749 this.reloadItems();
34751 this.currentSize = this.el.getBox(true);
34753 /// was window resize... - let's see if this works..
34754 Roo.EventManager.onWindowResize(this.resize, this);
34756 if(!this.isAutoInitial){
34761 this.layout.defer(500,this);
34764 reloadItems: function()
34766 this.bricks = this.el.select('.masonry-brick', true);
34768 this.bricks.each(function(b) {
34769 //Roo.log(b.getSize());
34770 if (!b.attr('originalwidth')) {
34771 b.attr('originalwidth', b.getSize().width);
34776 Roo.log(this.bricks.elements.length);
34779 resize : function()
34782 var cs = this.el.getBox(true);
34784 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34785 Roo.log("no change in with or X");
34788 this.currentSize = cs;
34792 layout : function()
34795 this._resetLayout();
34796 //this._manageStamps();
34798 // don't animate first layout
34799 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34800 this.layoutItems( isInstant );
34802 // flag for initalized
34803 this._isLayoutInited = true;
34806 layoutItems : function( isInstant )
34808 //var items = this._getItemsForLayout( this.items );
34809 // original code supports filtering layout items.. we just ignore it..
34811 this._layoutItems( this.bricks , isInstant );
34813 this._postLayout();
34815 _layoutItems : function ( items , isInstant)
34817 //this.fireEvent( 'layout', this, items );
34820 if ( !items || !items.elements.length ) {
34821 // no items, emit event with empty array
34826 items.each(function(item) {
34827 Roo.log("layout item");
34829 // get x/y object from method
34830 var position = this._getItemLayoutPosition( item );
34832 position.item = item;
34833 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34834 queue.push( position );
34837 this._processLayoutQueue( queue );
34839 /** Sets position of item in DOM
34840 * @param {Element} item
34841 * @param {Number} x - horizontal position
34842 * @param {Number} y - vertical position
34843 * @param {Boolean} isInstant - disables transitions
34845 _processLayoutQueue : function( queue )
34847 for ( var i=0, len = queue.length; i < len; i++ ) {
34848 var obj = queue[i];
34849 obj.item.position('absolute');
34850 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34856 * Any logic you want to do after each layout,
34857 * i.e. size the container
34859 _postLayout : function()
34861 this.resizeContainer();
34864 resizeContainer : function()
34866 if ( !this.isResizingContainer ) {
34869 var size = this._getContainerSize();
34871 this.el.setSize(size.width,size.height);
34872 this.boxesEl.setSize(size.width,size.height);
34878 _resetLayout : function()
34880 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34881 this.colWidth = this.el.getWidth();
34882 //this.gutter = this.el.getWidth();
34884 this.measureColumns();
34890 this.colYs.push( 0 );
34896 measureColumns : function()
34898 this.getContainerWidth();
34899 // if columnWidth is 0, default to outerWidth of first item
34900 if ( !this.columnWidth ) {
34901 var firstItem = this.bricks.first();
34902 Roo.log(firstItem);
34903 this.columnWidth = this.containerWidth;
34904 if (firstItem && firstItem.attr('originalwidth') ) {
34905 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34907 // columnWidth fall back to item of first element
34908 Roo.log("set column width?");
34909 this.initialColumnWidth = this.columnWidth ;
34911 // if first elem has no width, default to size of container
34916 if (this.initialColumnWidth) {
34917 this.columnWidth = this.initialColumnWidth;
34922 // column width is fixed at the top - however if container width get's smaller we should
34925 // this bit calcs how man columns..
34927 var columnWidth = this.columnWidth += this.gutter;
34929 // calculate columns
34930 var containerWidth = this.containerWidth + this.gutter;
34932 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34933 // fix rounding errors, typically with gutters
34934 var excess = columnWidth - containerWidth % columnWidth;
34937 // if overshoot is less than a pixel, round up, otherwise floor it
34938 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34939 cols = Math[ mathMethod ]( cols );
34940 this.cols = Math.max( cols, 1 );
34941 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34943 // padding positioning..
34944 var totalColWidth = this.cols * this.columnWidth;
34945 var padavail = this.containerWidth - totalColWidth;
34946 // so for 2 columns - we need 3 'pads'
34948 var padNeeded = (1+this.cols) * this.padWidth;
34950 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34952 this.columnWidth += padExtra
34953 //this.padWidth = Math.floor(padavail / ( this.cols));
34955 // adjust colum width so that padding is fixed??
34957 // we have 3 columns ... total = width * 3
34958 // we have X left over... that should be used by
34960 //if (this.expandC) {
34968 getContainerWidth : function()
34970 /* // container is parent if fit width
34971 var container = this.isFitWidth ? this.element.parentNode : this.element;
34972 // check that this.size and size are there
34973 // IE8 triggers resize on body size change, so they might not be
34975 var size = getSize( container ); //FIXME
34976 this.containerWidth = size && size.innerWidth; //FIXME
34979 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34983 _getItemLayoutPosition : function( item ) // what is item?
34985 // we resize the item to our columnWidth..
34987 item.setWidth(this.columnWidth);
34988 item.autoBoxAdjust = false;
34990 var sz = item.getSize();
34992 // how many columns does this brick span
34993 var remainder = this.containerWidth % this.columnWidth;
34995 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34996 // round if off by 1 pixel, otherwise use ceil
34997 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34998 colSpan = Math.min( colSpan, this.cols );
35000 // normally this should be '1' as we dont' currently allow multi width columns..
35002 var colGroup = this._getColGroup( colSpan );
35003 // get the minimum Y value from the columns
35004 var minimumY = Math.min.apply( Math, colGroup );
35005 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35007 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35009 // position the brick
35011 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35012 y: this.currentSize.y + minimumY + this.padHeight
35016 // apply setHeight to necessary columns
35017 var setHeight = minimumY + sz.height + this.padHeight;
35018 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35020 var setSpan = this.cols + 1 - colGroup.length;
35021 for ( var i = 0; i < setSpan; i++ ) {
35022 this.colYs[ shortColIndex + i ] = setHeight ;
35029 * @param {Number} colSpan - number of columns the element spans
35030 * @returns {Array} colGroup
35032 _getColGroup : function( colSpan )
35034 if ( colSpan < 2 ) {
35035 // if brick spans only one column, use all the column Ys
35040 // how many different places could this brick fit horizontally
35041 var groupCount = this.cols + 1 - colSpan;
35042 // for each group potential horizontal position
35043 for ( var i = 0; i < groupCount; i++ ) {
35044 // make an array of colY values for that one group
35045 var groupColYs = this.colYs.slice( i, i + colSpan );
35046 // and get the max value of the array
35047 colGroup[i] = Math.max.apply( Math, groupColYs );
35052 _manageStamp : function( stamp )
35054 var stampSize = stamp.getSize();
35055 var offset = stamp.getBox();
35056 // get the columns that this stamp affects
35057 var firstX = this.isOriginLeft ? offset.x : offset.right;
35058 var lastX = firstX + stampSize.width;
35059 var firstCol = Math.floor( firstX / this.columnWidth );
35060 firstCol = Math.max( 0, firstCol );
35062 var lastCol = Math.floor( lastX / this.columnWidth );
35063 // lastCol should not go over if multiple of columnWidth #425
35064 lastCol -= lastX % this.columnWidth ? 0 : 1;
35065 lastCol = Math.min( this.cols - 1, lastCol );
35067 // set colYs to bottom of the stamp
35068 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35071 for ( var i = firstCol; i <= lastCol; i++ ) {
35072 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35077 _getContainerSize : function()
35079 this.maxY = Math.max.apply( Math, this.colYs );
35084 if ( this.isFitWidth ) {
35085 size.width = this._getContainerFitWidth();
35091 _getContainerFitWidth : function()
35093 var unusedCols = 0;
35094 // count unused columns
35097 if ( this.colYs[i] !== 0 ) {
35102 // fit container to columns that have been used
35103 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35106 needsResizeLayout : function()
35108 var previousWidth = this.containerWidth;
35109 this.getContainerWidth();
35110 return previousWidth !== this.containerWidth;
35125 * @class Roo.bootstrap.MasonryBrick
35126 * @extends Roo.bootstrap.Component
35127 * Bootstrap MasonryBrick class
35130 * Create a new MasonryBrick
35131 * @param {Object} config The config object
35134 Roo.bootstrap.MasonryBrick = function(config){
35136 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35138 Roo.bootstrap.MasonryBrick.register(this);
35144 * When a MasonryBrick is clcik
35145 * @param {Roo.bootstrap.MasonryBrick} this
35146 * @param {Roo.EventObject} e
35152 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35155 * @cfg {String} title
35159 * @cfg {String} html
35163 * @cfg {String} bgimage
35167 * @cfg {String} videourl
35171 * @cfg {String} cls
35175 * @cfg {String} href
35179 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35184 * @cfg {String} placetitle (center|bottom)
35189 * @cfg {Boolean} isFitContainer defalut true
35191 isFitContainer : true,
35194 * @cfg {Boolean} preventDefault defalut false
35196 preventDefault : false,
35199 * @cfg {Boolean} inverse defalut false
35201 maskInverse : false,
35203 getAutoCreate : function()
35205 if(!this.isFitContainer){
35206 return this.getSplitAutoCreate();
35209 var cls = 'masonry-brick masonry-brick-full';
35211 if(this.href.length){
35212 cls += ' masonry-brick-link';
35215 if(this.bgimage.length){
35216 cls += ' masonry-brick-image';
35219 if(this.maskInverse){
35220 cls += ' mask-inverse';
35223 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35224 cls += ' enable-mask';
35228 cls += ' masonry-' + this.size + '-brick';
35231 if(this.placetitle.length){
35233 switch (this.placetitle) {
35235 cls += ' masonry-center-title';
35238 cls += ' masonry-bottom-title';
35245 if(!this.html.length && !this.bgimage.length){
35246 cls += ' masonry-center-title';
35249 if(!this.html.length && this.bgimage.length){
35250 cls += ' masonry-bottom-title';
35255 cls += ' ' + this.cls;
35259 tag: (this.href.length) ? 'a' : 'div',
35264 cls: 'masonry-brick-mask'
35268 cls: 'masonry-brick-paragraph',
35274 if(this.href.length){
35275 cfg.href = this.href;
35278 var cn = cfg.cn[1].cn;
35280 if(this.title.length){
35283 cls: 'masonry-brick-title',
35288 if(this.html.length){
35291 cls: 'masonry-brick-text',
35296 if (!this.title.length && !this.html.length) {
35297 cfg.cn[1].cls += ' hide';
35300 if(this.bgimage.length){
35303 cls: 'masonry-brick-image-view',
35308 if(this.videourl.length){
35309 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35310 // youtube support only?
35313 cls: 'masonry-brick-image-view',
35316 allowfullscreen : true
35324 getSplitAutoCreate : function()
35326 var cls = 'masonry-brick masonry-brick-split';
35328 if(this.href.length){
35329 cls += ' masonry-brick-link';
35332 if(this.bgimage.length){
35333 cls += ' masonry-brick-image';
35337 cls += ' masonry-' + this.size + '-brick';
35340 switch (this.placetitle) {
35342 cls += ' masonry-center-title';
35345 cls += ' masonry-bottom-title';
35348 if(!this.bgimage.length){
35349 cls += ' masonry-center-title';
35352 if(this.bgimage.length){
35353 cls += ' masonry-bottom-title';
35359 cls += ' ' + this.cls;
35363 tag: (this.href.length) ? 'a' : 'div',
35368 cls: 'masonry-brick-split-head',
35372 cls: 'masonry-brick-paragraph',
35379 cls: 'masonry-brick-split-body',
35385 if(this.href.length){
35386 cfg.href = this.href;
35389 if(this.title.length){
35390 cfg.cn[0].cn[0].cn.push({
35392 cls: 'masonry-brick-title',
35397 if(this.html.length){
35398 cfg.cn[1].cn.push({
35400 cls: 'masonry-brick-text',
35405 if(this.bgimage.length){
35406 cfg.cn[0].cn.push({
35408 cls: 'masonry-brick-image-view',
35413 if(this.videourl.length){
35414 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35415 // youtube support only?
35416 cfg.cn[0].cn.cn.push({
35418 cls: 'masonry-brick-image-view',
35421 allowfullscreen : true
35428 initEvents: function()
35430 switch (this.size) {
35463 this.el.on('touchstart', this.onTouchStart, this);
35464 this.el.on('touchmove', this.onTouchMove, this);
35465 this.el.on('touchend', this.onTouchEnd, this);
35466 this.el.on('contextmenu', this.onContextMenu, this);
35468 this.el.on('mouseenter' ,this.enter, this);
35469 this.el.on('mouseleave', this.leave, this);
35470 this.el.on('click', this.onClick, this);
35473 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35474 this.parent().bricks.push(this);
35479 onClick: function(e, el)
35481 var time = this.endTimer - this.startTimer;
35482 // Roo.log(e.preventDefault());
35485 e.preventDefault();
35490 if(!this.preventDefault){
35494 e.preventDefault();
35496 if (this.activeClass != '') {
35497 this.selectBrick();
35500 this.fireEvent('click', this, e);
35503 enter: function(e, el)
35505 e.preventDefault();
35507 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35511 if(this.bgimage.length && this.html.length){
35512 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35516 leave: function(e, el)
35518 e.preventDefault();
35520 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35524 if(this.bgimage.length && this.html.length){
35525 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35529 onTouchStart: function(e, el)
35531 // e.preventDefault();
35533 this.touchmoved = false;
35535 if(!this.isFitContainer){
35539 if(!this.bgimage.length || !this.html.length){
35543 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35545 this.timer = new Date().getTime();
35549 onTouchMove: function(e, el)
35551 this.touchmoved = true;
35554 onContextMenu : function(e,el)
35556 e.preventDefault();
35557 e.stopPropagation();
35561 onTouchEnd: function(e, el)
35563 // e.preventDefault();
35565 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35572 if(!this.bgimage.length || !this.html.length){
35574 if(this.href.length){
35575 window.location.href = this.href;
35581 if(!this.isFitContainer){
35585 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35587 window.location.href = this.href;
35590 //selection on single brick only
35591 selectBrick : function() {
35593 if (!this.parentId) {
35597 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35598 var index = m.selectedBrick.indexOf(this.id);
35601 m.selectedBrick.splice(index,1);
35602 this.el.removeClass(this.activeClass);
35606 for(var i = 0; i < m.selectedBrick.length; i++) {
35607 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35608 b.el.removeClass(b.activeClass);
35611 m.selectedBrick = [];
35613 m.selectedBrick.push(this.id);
35614 this.el.addClass(this.activeClass);
35618 isSelected : function(){
35619 return this.el.hasClass(this.activeClass);
35624 Roo.apply(Roo.bootstrap.MasonryBrick, {
35627 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35629 * register a Masonry Brick
35630 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35633 register : function(brick)
35635 //this.groups[brick.id] = brick;
35636 this.groups.add(brick.id, brick);
35639 * fetch a masonry brick based on the masonry brick ID
35640 * @param {string} the masonry brick to add
35641 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35644 get: function(brick_id)
35646 // if (typeof(this.groups[brick_id]) == 'undefined') {
35649 // return this.groups[brick_id] ;
35651 if(this.groups.key(brick_id)) {
35652 return this.groups.key(brick_id);
35670 * @class Roo.bootstrap.Brick
35671 * @extends Roo.bootstrap.Component
35672 * Bootstrap Brick class
35675 * Create a new Brick
35676 * @param {Object} config The config object
35679 Roo.bootstrap.Brick = function(config){
35680 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35686 * When a Brick is click
35687 * @param {Roo.bootstrap.Brick} this
35688 * @param {Roo.EventObject} e
35694 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35697 * @cfg {String} title
35701 * @cfg {String} html
35705 * @cfg {String} bgimage
35709 * @cfg {String} cls
35713 * @cfg {String} href
35717 * @cfg {String} video
35721 * @cfg {Boolean} square
35725 getAutoCreate : function()
35727 var cls = 'roo-brick';
35729 if(this.href.length){
35730 cls += ' roo-brick-link';
35733 if(this.bgimage.length){
35734 cls += ' roo-brick-image';
35737 if(!this.html.length && !this.bgimage.length){
35738 cls += ' roo-brick-center-title';
35741 if(!this.html.length && this.bgimage.length){
35742 cls += ' roo-brick-bottom-title';
35746 cls += ' ' + this.cls;
35750 tag: (this.href.length) ? 'a' : 'div',
35755 cls: 'roo-brick-paragraph',
35761 if(this.href.length){
35762 cfg.href = this.href;
35765 var cn = cfg.cn[0].cn;
35767 if(this.title.length){
35770 cls: 'roo-brick-title',
35775 if(this.html.length){
35778 cls: 'roo-brick-text',
35785 if(this.bgimage.length){
35788 cls: 'roo-brick-image-view',
35796 initEvents: function()
35798 if(this.title.length || this.html.length){
35799 this.el.on('mouseenter' ,this.enter, this);
35800 this.el.on('mouseleave', this.leave, this);
35803 Roo.EventManager.onWindowResize(this.resize, this);
35805 if(this.bgimage.length){
35806 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35807 this.imageEl.on('load', this.onImageLoad, this);
35814 onImageLoad : function()
35819 resize : function()
35821 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35823 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35825 if(this.bgimage.length){
35826 var image = this.el.select('.roo-brick-image-view', true).first();
35828 image.setWidth(paragraph.getWidth());
35831 image.setHeight(paragraph.getWidth());
35834 this.el.setHeight(image.getHeight());
35835 paragraph.setHeight(image.getHeight());
35841 enter: function(e, el)
35843 e.preventDefault();
35845 if(this.bgimage.length){
35846 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35847 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35851 leave: function(e, el)
35853 e.preventDefault();
35855 if(this.bgimage.length){
35856 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35857 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35872 * @class Roo.bootstrap.NumberField
35873 * @extends Roo.bootstrap.Input
35874 * Bootstrap NumberField class
35880 * Create a new NumberField
35881 * @param {Object} config The config object
35884 Roo.bootstrap.NumberField = function(config){
35885 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35888 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35891 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35893 allowDecimals : true,
35895 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35897 decimalSeparator : ".",
35899 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35901 decimalPrecision : 2,
35903 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35905 allowNegative : true,
35908 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35912 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35914 minValue : Number.NEGATIVE_INFINITY,
35916 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35918 maxValue : Number.MAX_VALUE,
35920 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35922 minText : "The minimum value for this field is {0}",
35924 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35926 maxText : "The maximum value for this field is {0}",
35928 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35929 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35931 nanText : "{0} is not a valid number",
35933 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35935 thousandsDelimiter : false,
35937 * @cfg {String} valueAlign alignment of value
35939 valueAlign : "left",
35941 getAutoCreate : function()
35943 var hiddenInput = {
35947 cls: 'hidden-number-input'
35951 hiddenInput.name = this.name;
35956 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35958 this.name = hiddenInput.name;
35960 if(cfg.cn.length > 0) {
35961 cfg.cn.push(hiddenInput);
35968 initEvents : function()
35970 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35972 var allowed = "0123456789";
35974 if(this.allowDecimals){
35975 allowed += this.decimalSeparator;
35978 if(this.allowNegative){
35982 if(this.thousandsDelimiter) {
35986 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35988 var keyPress = function(e){
35990 var k = e.getKey();
35992 var c = e.getCharCode();
35995 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35996 allowed.indexOf(String.fromCharCode(c)) === -1
36002 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36006 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36011 this.el.on("keypress", keyPress, this);
36014 validateValue : function(value)
36017 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36021 var num = this.parseValue(value);
36024 this.markInvalid(String.format(this.nanText, value));
36028 if(num < this.minValue){
36029 this.markInvalid(String.format(this.minText, this.minValue));
36033 if(num > this.maxValue){
36034 this.markInvalid(String.format(this.maxText, this.maxValue));
36041 getValue : function()
36043 var v = this.hiddenEl().getValue();
36045 return this.fixPrecision(this.parseValue(v));
36048 parseValue : function(value)
36050 if(this.thousandsDelimiter) {
36052 r = new RegExp(",", "g");
36053 value = value.replace(r, "");
36056 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36057 return isNaN(value) ? '' : value;
36060 fixPrecision : function(value)
36062 if(this.thousandsDelimiter) {
36064 r = new RegExp(",", "g");
36065 value = value.replace(r, "");
36068 var nan = isNaN(value);
36070 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36071 return nan ? '' : value;
36073 return parseFloat(value).toFixed(this.decimalPrecision);
36076 setValue : function(v)
36078 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36084 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36086 this.inputEl().dom.value = (v == '') ? '' :
36087 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36089 if(!this.allowZero && v === '0') {
36090 this.hiddenEl().dom.value = '';
36091 this.inputEl().dom.value = '';
36098 decimalPrecisionFcn : function(v)
36100 return Math.floor(v);
36103 beforeBlur : function()
36105 var v = this.parseValue(this.getRawValue());
36107 if(v || v === 0 || v === ''){
36112 hiddenEl : function()
36114 return this.el.select('input.hidden-number-input',true).first();
36126 * @class Roo.bootstrap.DocumentSlider
36127 * @extends Roo.bootstrap.Component
36128 * Bootstrap DocumentSlider class
36131 * Create a new DocumentViewer
36132 * @param {Object} config The config object
36135 Roo.bootstrap.DocumentSlider = function(config){
36136 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36143 * Fire after initEvent
36144 * @param {Roo.bootstrap.DocumentSlider} this
36149 * Fire after update
36150 * @param {Roo.bootstrap.DocumentSlider} this
36156 * @param {Roo.bootstrap.DocumentSlider} this
36162 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36168 getAutoCreate : function()
36172 cls : 'roo-document-slider',
36176 cls : 'roo-document-slider-header',
36180 cls : 'roo-document-slider-header-title'
36186 cls : 'roo-document-slider-body',
36190 cls : 'roo-document-slider-prev',
36194 cls : 'fa fa-chevron-left'
36200 cls : 'roo-document-slider-thumb',
36204 cls : 'roo-document-slider-image'
36210 cls : 'roo-document-slider-next',
36214 cls : 'fa fa-chevron-right'
36226 initEvents : function()
36228 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36229 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36231 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36232 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36234 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36235 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36237 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36238 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36240 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36241 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36243 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36244 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36246 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36247 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36249 this.thumbEl.on('click', this.onClick, this);
36251 this.prevIndicator.on('click', this.prev, this);
36253 this.nextIndicator.on('click', this.next, this);
36257 initial : function()
36259 if(this.files.length){
36260 this.indicator = 1;
36264 this.fireEvent('initial', this);
36267 update : function()
36269 this.imageEl.attr('src', this.files[this.indicator - 1]);
36271 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36273 this.prevIndicator.show();
36275 if(this.indicator == 1){
36276 this.prevIndicator.hide();
36279 this.nextIndicator.show();
36281 if(this.indicator == this.files.length){
36282 this.nextIndicator.hide();
36285 this.thumbEl.scrollTo('top');
36287 this.fireEvent('update', this);
36290 onClick : function(e)
36292 e.preventDefault();
36294 this.fireEvent('click', this);
36299 e.preventDefault();
36301 this.indicator = Math.max(1, this.indicator - 1);
36308 e.preventDefault();
36310 this.indicator = Math.min(this.files.length, this.indicator + 1);
36324 * @class Roo.bootstrap.RadioSet
36325 * @extends Roo.bootstrap.Input
36326 * Bootstrap RadioSet class
36327 * @cfg {String} indicatorpos (left|right) default left
36328 * @cfg {Boolean} inline (true|false) inline the element (default true)
36329 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36331 * Create a new RadioSet
36332 * @param {Object} config The config object
36335 Roo.bootstrap.RadioSet = function(config){
36337 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36341 Roo.bootstrap.RadioSet.register(this);
36346 * Fires when the element is checked or unchecked.
36347 * @param {Roo.bootstrap.RadioSet} this This radio
36348 * @param {Roo.bootstrap.Radio} item The checked item
36353 * Fires when the element is click.
36354 * @param {Roo.bootstrap.RadioSet} this This radio set
36355 * @param {Roo.bootstrap.Radio} item The checked item
36356 * @param {Roo.EventObject} e The event object
36363 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36371 indicatorpos : 'left',
36373 getAutoCreate : function()
36377 cls : 'roo-radio-set-label',
36381 html : this.fieldLabel
36385 if (Roo.bootstrap.version == 3) {
36388 if(this.indicatorpos == 'left'){
36391 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36392 tooltip : 'This field is required'
36397 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36398 tooltip : 'This field is required'
36404 cls : 'roo-radio-set-items'
36407 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36409 if (align === 'left' && this.fieldLabel.length) {
36412 cls : "roo-radio-set-right",
36418 if(this.labelWidth > 12){
36419 label.style = "width: " + this.labelWidth + 'px';
36422 if(this.labelWidth < 13 && this.labelmd == 0){
36423 this.labelmd = this.labelWidth;
36426 if(this.labellg > 0){
36427 label.cls += ' col-lg-' + this.labellg;
36428 items.cls += ' col-lg-' + (12 - this.labellg);
36431 if(this.labelmd > 0){
36432 label.cls += ' col-md-' + this.labelmd;
36433 items.cls += ' col-md-' + (12 - this.labelmd);
36436 if(this.labelsm > 0){
36437 label.cls += ' col-sm-' + this.labelsm;
36438 items.cls += ' col-sm-' + (12 - this.labelsm);
36441 if(this.labelxs > 0){
36442 label.cls += ' col-xs-' + this.labelxs;
36443 items.cls += ' col-xs-' + (12 - this.labelxs);
36449 cls : 'roo-radio-set',
36453 cls : 'roo-radio-set-input',
36456 value : this.value ? this.value : ''
36463 if(this.weight.length){
36464 cfg.cls += ' roo-radio-' + this.weight;
36468 cfg.cls += ' roo-radio-set-inline';
36472 ['xs','sm','md','lg'].map(function(size){
36473 if (settings[size]) {
36474 cfg.cls += ' col-' + size + '-' + settings[size];
36482 initEvents : function()
36484 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36485 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36487 if(!this.fieldLabel.length){
36488 this.labelEl.hide();
36491 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36492 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36494 this.indicator = this.indicatorEl();
36496 if(this.indicator){
36497 this.indicator.addClass('invisible');
36500 this.originalValue = this.getValue();
36504 inputEl: function ()
36506 return this.el.select('.roo-radio-set-input', true).first();
36509 getChildContainer : function()
36511 return this.itemsEl;
36514 register : function(item)
36516 this.radioes.push(item);
36520 validate : function()
36522 if(this.getVisibilityEl().hasClass('hidden')){
36528 Roo.each(this.radioes, function(i){
36537 if(this.allowBlank) {
36541 if(this.disabled || valid){
36546 this.markInvalid();
36551 markValid : function()
36553 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36554 this.indicatorEl().removeClass('visible');
36555 this.indicatorEl().addClass('invisible');
36559 if (Roo.bootstrap.version == 3) {
36560 this.el.removeClass([this.invalidClass, this.validClass]);
36561 this.el.addClass(this.validClass);
36563 this.el.removeClass(['is-invalid','is-valid']);
36564 this.el.addClass(['is-valid']);
36566 this.fireEvent('valid', this);
36569 markInvalid : function(msg)
36571 if(this.allowBlank || this.disabled){
36575 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36576 this.indicatorEl().removeClass('invisible');
36577 this.indicatorEl().addClass('visible');
36579 if (Roo.bootstrap.version == 3) {
36580 this.el.removeClass([this.invalidClass, this.validClass]);
36581 this.el.addClass(this.invalidClass);
36583 this.el.removeClass(['is-invalid','is-valid']);
36584 this.el.addClass(['is-invalid']);
36587 this.fireEvent('invalid', this, msg);
36591 setValue : function(v, suppressEvent)
36593 if(this.value === v){
36600 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36603 Roo.each(this.radioes, function(i){
36605 i.el.removeClass('checked');
36608 Roo.each(this.radioes, function(i){
36610 if(i.value === v || i.value.toString() === v.toString()){
36612 i.el.addClass('checked');
36614 if(suppressEvent !== true){
36615 this.fireEvent('check', this, i);
36626 clearInvalid : function(){
36628 if(!this.el || this.preventMark){
36632 this.el.removeClass([this.invalidClass]);
36634 this.fireEvent('valid', this);
36639 Roo.apply(Roo.bootstrap.RadioSet, {
36643 register : function(set)
36645 this.groups[set.name] = set;
36648 get: function(name)
36650 if (typeof(this.groups[name]) == 'undefined') {
36654 return this.groups[name] ;
36660 * Ext JS Library 1.1.1
36661 * Copyright(c) 2006-2007, Ext JS, LLC.
36663 * Originally Released Under LGPL - original licence link has changed is not relivant.
36666 * <script type="text/javascript">
36671 * @class Roo.bootstrap.SplitBar
36672 * @extends Roo.util.Observable
36673 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36677 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36678 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36679 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36680 split.minSize = 100;
36681 split.maxSize = 600;
36682 split.animate = true;
36683 split.on('moved', splitterMoved);
36686 * Create a new SplitBar
36687 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36688 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36689 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36690 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36691 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36692 position of the SplitBar).
36694 Roo.bootstrap.SplitBar = function(cfg){
36699 // dragElement : elm
36700 // resizingElement: el,
36702 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36703 // placement : Roo.bootstrap.SplitBar.LEFT ,
36704 // existingProxy ???
36707 this.el = Roo.get(cfg.dragElement, true);
36708 this.el.dom.unselectable = "on";
36710 this.resizingEl = Roo.get(cfg.resizingElement, true);
36714 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36715 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36718 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36721 * The minimum size of the resizing element. (Defaults to 0)
36727 * The maximum size of the resizing element. (Defaults to 2000)
36730 this.maxSize = 2000;
36733 * Whether to animate the transition to the new size
36736 this.animate = false;
36739 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36742 this.useShim = false;
36747 if(!cfg.existingProxy){
36749 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36751 this.proxy = Roo.get(cfg.existingProxy).dom;
36754 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36757 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36760 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36763 this.dragSpecs = {};
36766 * @private The adapter to use to positon and resize elements
36768 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36769 this.adapter.init(this);
36771 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36773 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36774 this.el.addClass("roo-splitbar-h");
36777 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36778 this.el.addClass("roo-splitbar-v");
36784 * Fires when the splitter is moved (alias for {@link #event-moved})
36785 * @param {Roo.bootstrap.SplitBar} this
36786 * @param {Number} newSize the new width or height
36791 * Fires when the splitter is moved
36792 * @param {Roo.bootstrap.SplitBar} this
36793 * @param {Number} newSize the new width or height
36797 * @event beforeresize
36798 * Fires before the splitter is dragged
36799 * @param {Roo.bootstrap.SplitBar} this
36801 "beforeresize" : true,
36803 "beforeapply" : true
36806 Roo.util.Observable.call(this);
36809 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36810 onStartProxyDrag : function(x, y){
36811 this.fireEvent("beforeresize", this);
36813 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36815 o.enableDisplayMode("block");
36816 // all splitbars share the same overlay
36817 Roo.bootstrap.SplitBar.prototype.overlay = o;
36819 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36820 this.overlay.show();
36821 Roo.get(this.proxy).setDisplayed("block");
36822 var size = this.adapter.getElementSize(this);
36823 this.activeMinSize = this.getMinimumSize();;
36824 this.activeMaxSize = this.getMaximumSize();;
36825 var c1 = size - this.activeMinSize;
36826 var c2 = Math.max(this.activeMaxSize - size, 0);
36827 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36828 this.dd.resetConstraints();
36829 this.dd.setXConstraint(
36830 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36831 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36833 this.dd.setYConstraint(0, 0);
36835 this.dd.resetConstraints();
36836 this.dd.setXConstraint(0, 0);
36837 this.dd.setYConstraint(
36838 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36839 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36842 this.dragSpecs.startSize = size;
36843 this.dragSpecs.startPoint = [x, y];
36844 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36848 * @private Called after the drag operation by the DDProxy
36850 onEndProxyDrag : function(e){
36851 Roo.get(this.proxy).setDisplayed(false);
36852 var endPoint = Roo.lib.Event.getXY(e);
36854 this.overlay.hide();
36857 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36858 newSize = this.dragSpecs.startSize +
36859 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36860 endPoint[0] - this.dragSpecs.startPoint[0] :
36861 this.dragSpecs.startPoint[0] - endPoint[0]
36864 newSize = this.dragSpecs.startSize +
36865 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36866 endPoint[1] - this.dragSpecs.startPoint[1] :
36867 this.dragSpecs.startPoint[1] - endPoint[1]
36870 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36871 if(newSize != this.dragSpecs.startSize){
36872 if(this.fireEvent('beforeapply', this, newSize) !== false){
36873 this.adapter.setElementSize(this, newSize);
36874 this.fireEvent("moved", this, newSize);
36875 this.fireEvent("resize", this, newSize);
36881 * Get the adapter this SplitBar uses
36882 * @return The adapter object
36884 getAdapter : function(){
36885 return this.adapter;
36889 * Set the adapter this SplitBar uses
36890 * @param {Object} adapter A SplitBar adapter object
36892 setAdapter : function(adapter){
36893 this.adapter = adapter;
36894 this.adapter.init(this);
36898 * Gets the minimum size for the resizing element
36899 * @return {Number} The minimum size
36901 getMinimumSize : function(){
36902 return this.minSize;
36906 * Sets the minimum size for the resizing element
36907 * @param {Number} minSize The minimum size
36909 setMinimumSize : function(minSize){
36910 this.minSize = minSize;
36914 * Gets the maximum size for the resizing element
36915 * @return {Number} The maximum size
36917 getMaximumSize : function(){
36918 return this.maxSize;
36922 * Sets the maximum size for the resizing element
36923 * @param {Number} maxSize The maximum size
36925 setMaximumSize : function(maxSize){
36926 this.maxSize = maxSize;
36930 * Sets the initialize size for the resizing element
36931 * @param {Number} size The initial size
36933 setCurrentSize : function(size){
36934 var oldAnimate = this.animate;
36935 this.animate = false;
36936 this.adapter.setElementSize(this, size);
36937 this.animate = oldAnimate;
36941 * Destroy this splitbar.
36942 * @param {Boolean} removeEl True to remove the element
36944 destroy : function(removeEl){
36946 this.shim.remove();
36949 this.proxy.parentNode.removeChild(this.proxy);
36957 * @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.
36959 Roo.bootstrap.SplitBar.createProxy = function(dir){
36960 var proxy = new Roo.Element(document.createElement("div"));
36961 proxy.unselectable();
36962 var cls = 'roo-splitbar-proxy';
36963 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36964 document.body.appendChild(proxy.dom);
36969 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36970 * Default Adapter. It assumes the splitter and resizing element are not positioned
36971 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36973 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36976 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36977 // do nothing for now
36978 init : function(s){
36982 * Called before drag operations to get the current size of the resizing element.
36983 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36985 getElementSize : function(s){
36986 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36987 return s.resizingEl.getWidth();
36989 return s.resizingEl.getHeight();
36994 * Called after drag operations to set the size of the resizing element.
36995 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36996 * @param {Number} newSize The new size to set
36997 * @param {Function} onComplete A function to be invoked when resizing is complete
36999 setElementSize : function(s, newSize, onComplete){
37000 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37002 s.resizingEl.setWidth(newSize);
37004 onComplete(s, newSize);
37007 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37012 s.resizingEl.setHeight(newSize);
37014 onComplete(s, newSize);
37017 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37024 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37025 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37026 * Adapter that moves the splitter element to align with the resized sizing element.
37027 * Used with an absolute positioned SplitBar.
37028 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37029 * document.body, make sure you assign an id to the body element.
37031 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37032 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37033 this.container = Roo.get(container);
37036 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37037 init : function(s){
37038 this.basic.init(s);
37041 getElementSize : function(s){
37042 return this.basic.getElementSize(s);
37045 setElementSize : function(s, newSize, onComplete){
37046 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37049 moveSplitter : function(s){
37050 var yes = Roo.bootstrap.SplitBar;
37051 switch(s.placement){
37053 s.el.setX(s.resizingEl.getRight());
37056 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37059 s.el.setY(s.resizingEl.getBottom());
37062 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37069 * Orientation constant - Create a vertical SplitBar
37073 Roo.bootstrap.SplitBar.VERTICAL = 1;
37076 * Orientation constant - Create a horizontal SplitBar
37080 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37083 * Placement constant - The resizing element is to the left of the splitter element
37087 Roo.bootstrap.SplitBar.LEFT = 1;
37090 * Placement constant - The resizing element is to the right of the splitter element
37094 Roo.bootstrap.SplitBar.RIGHT = 2;
37097 * Placement constant - The resizing element is positioned above the splitter element
37101 Roo.bootstrap.SplitBar.TOP = 3;
37104 * Placement constant - The resizing element is positioned under splitter element
37108 Roo.bootstrap.SplitBar.BOTTOM = 4;
37109 Roo.namespace("Roo.bootstrap.layout");/*
37111 * Ext JS Library 1.1.1
37112 * Copyright(c) 2006-2007, Ext JS, LLC.
37114 * Originally Released Under LGPL - original licence link has changed is not relivant.
37117 * <script type="text/javascript">
37121 * @class Roo.bootstrap.layout.Manager
37122 * @extends Roo.bootstrap.Component
37123 * Base class for layout managers.
37125 Roo.bootstrap.layout.Manager = function(config)
37127 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37133 /** false to disable window resize monitoring @type Boolean */
37134 this.monitorWindowResize = true;
37139 * Fires when a layout is performed.
37140 * @param {Roo.LayoutManager} this
37144 * @event regionresized
37145 * Fires when the user resizes a region.
37146 * @param {Roo.LayoutRegion} region The resized region
37147 * @param {Number} newSize The new size (width for east/west, height for north/south)
37149 "regionresized" : true,
37151 * @event regioncollapsed
37152 * Fires when a region is collapsed.
37153 * @param {Roo.LayoutRegion} region The collapsed region
37155 "regioncollapsed" : true,
37157 * @event regionexpanded
37158 * Fires when a region is expanded.
37159 * @param {Roo.LayoutRegion} region The expanded region
37161 "regionexpanded" : true
37163 this.updating = false;
37166 this.el = Roo.get(config.el);
37172 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37177 monitorWindowResize : true,
37183 onRender : function(ct, position)
37186 this.el = Roo.get(ct);
37189 //this.fireEvent('render',this);
37193 initEvents: function()
37197 // ie scrollbar fix
37198 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37199 document.body.scroll = "no";
37200 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37201 this.el.position('relative');
37203 this.id = this.el.id;
37204 this.el.addClass("roo-layout-container");
37205 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37206 if(this.el.dom != document.body ) {
37207 this.el.on('resize', this.layout,this);
37208 this.el.on('show', this.layout,this);
37214 * Returns true if this layout is currently being updated
37215 * @return {Boolean}
37217 isUpdating : function(){
37218 return this.updating;
37222 * Suspend the LayoutManager from doing auto-layouts while
37223 * making multiple add or remove calls
37225 beginUpdate : function(){
37226 this.updating = true;
37230 * Restore auto-layouts and optionally disable the manager from performing a layout
37231 * @param {Boolean} noLayout true to disable a layout update
37233 endUpdate : function(noLayout){
37234 this.updating = false;
37240 layout: function(){
37244 onRegionResized : function(region, newSize){
37245 this.fireEvent("regionresized", region, newSize);
37249 onRegionCollapsed : function(region){
37250 this.fireEvent("regioncollapsed", region);
37253 onRegionExpanded : function(region){
37254 this.fireEvent("regionexpanded", region);
37258 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37259 * performs box-model adjustments.
37260 * @return {Object} The size as an object {width: (the width), height: (the height)}
37262 getViewSize : function()
37265 if(this.el.dom != document.body){
37266 size = this.el.getSize();
37268 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37270 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37271 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37276 * Returns the Element this layout is bound to.
37277 * @return {Roo.Element}
37279 getEl : function(){
37284 * Returns the specified region.
37285 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37286 * @return {Roo.LayoutRegion}
37288 getRegion : function(target){
37289 return this.regions[target.toLowerCase()];
37292 onWindowResize : function(){
37293 if(this.monitorWindowResize){
37300 * Ext JS Library 1.1.1
37301 * Copyright(c) 2006-2007, Ext JS, LLC.
37303 * Originally Released Under LGPL - original licence link has changed is not relivant.
37306 * <script type="text/javascript">
37309 * @class Roo.bootstrap.layout.Border
37310 * @extends Roo.bootstrap.layout.Manager
37311 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37312 * please see: examples/bootstrap/nested.html<br><br>
37314 <b>The container the layout is rendered into can be either the body element or any other element.
37315 If it is not the body element, the container needs to either be an absolute positioned element,
37316 or you will need to add "position:relative" to the css of the container. You will also need to specify
37317 the container size if it is not the body element.</b>
37320 * Create a new Border
37321 * @param {Object} config Configuration options
37323 Roo.bootstrap.layout.Border = function(config){
37324 config = config || {};
37325 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37329 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37330 if(config[region]){
37331 config[region].region = region;
37332 this.addRegion(config[region]);
37338 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37340 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37342 parent : false, // this might point to a 'nest' or a ???
37345 * Creates and adds a new region if it doesn't already exist.
37346 * @param {String} target The target region key (north, south, east, west or center).
37347 * @param {Object} config The regions config object
37348 * @return {BorderLayoutRegion} The new region
37350 addRegion : function(config)
37352 if(!this.regions[config.region]){
37353 var r = this.factory(config);
37354 this.bindRegion(r);
37356 return this.regions[config.region];
37360 bindRegion : function(r){
37361 this.regions[r.config.region] = r;
37363 r.on("visibilitychange", this.layout, this);
37364 r.on("paneladded", this.layout, this);
37365 r.on("panelremoved", this.layout, this);
37366 r.on("invalidated", this.layout, this);
37367 r.on("resized", this.onRegionResized, this);
37368 r.on("collapsed", this.onRegionCollapsed, this);
37369 r.on("expanded", this.onRegionExpanded, this);
37373 * Performs a layout update.
37375 layout : function()
37377 if(this.updating) {
37381 // render all the rebions if they have not been done alreayd?
37382 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37383 if(this.regions[region] && !this.regions[region].bodyEl){
37384 this.regions[region].onRender(this.el)
37388 var size = this.getViewSize();
37389 var w = size.width;
37390 var h = size.height;
37395 //var x = 0, y = 0;
37397 var rs = this.regions;
37398 var north = rs["north"];
37399 var south = rs["south"];
37400 var west = rs["west"];
37401 var east = rs["east"];
37402 var center = rs["center"];
37403 //if(this.hideOnLayout){ // not supported anymore
37404 //c.el.setStyle("display", "none");
37406 if(north && north.isVisible()){
37407 var b = north.getBox();
37408 var m = north.getMargins();
37409 b.width = w - (m.left+m.right);
37412 centerY = b.height + b.y + m.bottom;
37413 centerH -= centerY;
37414 north.updateBox(this.safeBox(b));
37416 if(south && south.isVisible()){
37417 var b = south.getBox();
37418 var m = south.getMargins();
37419 b.width = w - (m.left+m.right);
37421 var totalHeight = (b.height + m.top + m.bottom);
37422 b.y = h - totalHeight + m.top;
37423 centerH -= totalHeight;
37424 south.updateBox(this.safeBox(b));
37426 if(west && west.isVisible()){
37427 var b = west.getBox();
37428 var m = west.getMargins();
37429 b.height = centerH - (m.top+m.bottom);
37431 b.y = centerY + m.top;
37432 var totalWidth = (b.width + m.left + m.right);
37433 centerX += totalWidth;
37434 centerW -= totalWidth;
37435 west.updateBox(this.safeBox(b));
37437 if(east && east.isVisible()){
37438 var b = east.getBox();
37439 var m = east.getMargins();
37440 b.height = centerH - (m.top+m.bottom);
37441 var totalWidth = (b.width + m.left + m.right);
37442 b.x = w - totalWidth + m.left;
37443 b.y = centerY + m.top;
37444 centerW -= totalWidth;
37445 east.updateBox(this.safeBox(b));
37448 var m = center.getMargins();
37450 x: centerX + m.left,
37451 y: centerY + m.top,
37452 width: centerW - (m.left+m.right),
37453 height: centerH - (m.top+m.bottom)
37455 //if(this.hideOnLayout){
37456 //center.el.setStyle("display", "block");
37458 center.updateBox(this.safeBox(centerBox));
37461 this.fireEvent("layout", this);
37465 safeBox : function(box){
37466 box.width = Math.max(0, box.width);
37467 box.height = Math.max(0, box.height);
37472 * Adds a ContentPanel (or subclass) to this layout.
37473 * @param {String} target The target region key (north, south, east, west or center).
37474 * @param {Roo.ContentPanel} panel The panel to add
37475 * @return {Roo.ContentPanel} The added panel
37477 add : function(target, panel){
37479 target = target.toLowerCase();
37480 return this.regions[target].add(panel);
37484 * Remove a ContentPanel (or subclass) to this layout.
37485 * @param {String} target The target region key (north, south, east, west or center).
37486 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37487 * @return {Roo.ContentPanel} The removed panel
37489 remove : function(target, panel){
37490 target = target.toLowerCase();
37491 return this.regions[target].remove(panel);
37495 * Searches all regions for a panel with the specified id
37496 * @param {String} panelId
37497 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37499 findPanel : function(panelId){
37500 var rs = this.regions;
37501 for(var target in rs){
37502 if(typeof rs[target] != "function"){
37503 var p = rs[target].getPanel(panelId);
37513 * Searches all regions for a panel with the specified id and activates (shows) it.
37514 * @param {String/ContentPanel} panelId The panels id or the panel itself
37515 * @return {Roo.ContentPanel} The shown panel or null
37517 showPanel : function(panelId) {
37518 var rs = this.regions;
37519 for(var target in rs){
37520 var r = rs[target];
37521 if(typeof r != "function"){
37522 if(r.hasPanel(panelId)){
37523 return r.showPanel(panelId);
37531 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37532 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37535 restoreState : function(provider){
37537 provider = Roo.state.Manager;
37539 var sm = new Roo.LayoutStateManager();
37540 sm.init(this, provider);
37546 * Adds a xtype elements to the layout.
37550 xtype : 'ContentPanel',
37557 xtype : 'NestedLayoutPanel',
37563 items : [ ... list of content panels or nested layout panels.. ]
37567 * @param {Object} cfg Xtype definition of item to add.
37569 addxtype : function(cfg)
37571 // basically accepts a pannel...
37572 // can accept a layout region..!?!?
37573 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37576 // theory? children can only be panels??
37578 //if (!cfg.xtype.match(/Panel$/)) {
37583 if (typeof(cfg.region) == 'undefined') {
37584 Roo.log("Failed to add Panel, region was not set");
37588 var region = cfg.region;
37594 xitems = cfg.items;
37599 if ( region == 'center') {
37600 Roo.log("Center: " + cfg.title);
37606 case 'Content': // ContentPanel (el, cfg)
37607 case 'Scroll': // ContentPanel (el, cfg)
37609 cfg.autoCreate = cfg.autoCreate || true;
37610 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37612 // var el = this.el.createChild();
37613 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37616 this.add(region, ret);
37620 case 'TreePanel': // our new panel!
37621 cfg.el = this.el.createChild();
37622 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37623 this.add(region, ret);
37628 // create a new Layout (which is a Border Layout...
37630 var clayout = cfg.layout;
37631 clayout.el = this.el.createChild();
37632 clayout.items = clayout.items || [];
37636 // replace this exitems with the clayout ones..
37637 xitems = clayout.items;
37639 // force background off if it's in center...
37640 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37641 cfg.background = false;
37643 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37646 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37647 //console.log('adding nested layout panel ' + cfg.toSource());
37648 this.add(region, ret);
37649 nb = {}; /// find first...
37654 // needs grid and region
37656 //var el = this.getRegion(region).el.createChild();
37658 *var el = this.el.createChild();
37659 // create the grid first...
37660 cfg.grid.container = el;
37661 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37664 if (region == 'center' && this.active ) {
37665 cfg.background = false;
37668 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37670 this.add(region, ret);
37672 if (cfg.background) {
37673 // render grid on panel activation (if panel background)
37674 ret.on('activate', function(gp) {
37675 if (!gp.grid.rendered) {
37676 // gp.grid.render(el);
37680 // cfg.grid.render(el);
37686 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37687 // it was the old xcomponent building that caused this before.
37688 // espeically if border is the top element in the tree.
37698 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37700 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37701 this.add(region, ret);
37705 throw "Can not add '" + cfg.xtype + "' to Border";
37711 this.beginUpdate();
37715 Roo.each(xitems, function(i) {
37716 region = nb && i.region ? i.region : false;
37718 var add = ret.addxtype(i);
37721 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37722 if (!i.background) {
37723 abn[region] = nb[region] ;
37730 // make the last non-background panel active..
37731 //if (nb) { Roo.log(abn); }
37734 for(var r in abn) {
37735 region = this.getRegion(r);
37737 // tried using nb[r], but it does not work..
37739 region.showPanel(abn[r]);
37750 factory : function(cfg)
37753 var validRegions = Roo.bootstrap.layout.Border.regions;
37755 var target = cfg.region;
37758 var r = Roo.bootstrap.layout;
37762 return new r.North(cfg);
37764 return new r.South(cfg);
37766 return new r.East(cfg);
37768 return new r.West(cfg);
37770 return new r.Center(cfg);
37772 throw 'Layout region "'+target+'" not supported.';
37779 * Ext JS Library 1.1.1
37780 * Copyright(c) 2006-2007, Ext JS, LLC.
37782 * Originally Released Under LGPL - original licence link has changed is not relivant.
37785 * <script type="text/javascript">
37789 * @class Roo.bootstrap.layout.Basic
37790 * @extends Roo.util.Observable
37791 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37792 * and does not have a titlebar, tabs or any other features. All it does is size and position
37793 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37794 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37795 * @cfg {string} region the region that it inhabits..
37796 * @cfg {bool} skipConfig skip config?
37800 Roo.bootstrap.layout.Basic = function(config){
37802 this.mgr = config.mgr;
37804 this.position = config.region;
37806 var skipConfig = config.skipConfig;
37810 * @scope Roo.BasicLayoutRegion
37814 * @event beforeremove
37815 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37816 * @param {Roo.LayoutRegion} this
37817 * @param {Roo.ContentPanel} panel The panel
37818 * @param {Object} e The cancel event object
37820 "beforeremove" : true,
37822 * @event invalidated
37823 * Fires when the layout for this region is changed.
37824 * @param {Roo.LayoutRegion} this
37826 "invalidated" : true,
37828 * @event visibilitychange
37829 * Fires when this region is shown or hidden
37830 * @param {Roo.LayoutRegion} this
37831 * @param {Boolean} visibility true or false
37833 "visibilitychange" : true,
37835 * @event paneladded
37836 * Fires when a panel is added.
37837 * @param {Roo.LayoutRegion} this
37838 * @param {Roo.ContentPanel} panel The panel
37840 "paneladded" : true,
37842 * @event panelremoved
37843 * Fires when a panel is removed.
37844 * @param {Roo.LayoutRegion} this
37845 * @param {Roo.ContentPanel} panel The panel
37847 "panelremoved" : true,
37849 * @event beforecollapse
37850 * Fires when this region before collapse.
37851 * @param {Roo.LayoutRegion} this
37853 "beforecollapse" : true,
37856 * Fires when this region is collapsed.
37857 * @param {Roo.LayoutRegion} this
37859 "collapsed" : true,
37862 * Fires when this region is expanded.
37863 * @param {Roo.LayoutRegion} this
37868 * Fires when this region is slid into view.
37869 * @param {Roo.LayoutRegion} this
37871 "slideshow" : true,
37874 * Fires when this region slides out of view.
37875 * @param {Roo.LayoutRegion} this
37877 "slidehide" : true,
37879 * @event panelactivated
37880 * Fires when a panel is activated.
37881 * @param {Roo.LayoutRegion} this
37882 * @param {Roo.ContentPanel} panel The activated panel
37884 "panelactivated" : true,
37887 * Fires when the user resizes this region.
37888 * @param {Roo.LayoutRegion} this
37889 * @param {Number} newSize The new size (width for east/west, height for north/south)
37893 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37894 this.panels = new Roo.util.MixedCollection();
37895 this.panels.getKey = this.getPanelId.createDelegate(this);
37897 this.activePanel = null;
37898 // ensure listeners are added...
37900 if (config.listeners || config.events) {
37901 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37902 listeners : config.listeners || {},
37903 events : config.events || {}
37907 if(skipConfig !== true){
37908 this.applyConfig(config);
37912 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37914 getPanelId : function(p){
37918 applyConfig : function(config){
37919 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37920 this.config = config;
37925 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37926 * the width, for horizontal (north, south) the height.
37927 * @param {Number} newSize The new width or height
37929 resizeTo : function(newSize){
37930 var el = this.el ? this.el :
37931 (this.activePanel ? this.activePanel.getEl() : null);
37933 switch(this.position){
37936 el.setWidth(newSize);
37937 this.fireEvent("resized", this, newSize);
37941 el.setHeight(newSize);
37942 this.fireEvent("resized", this, newSize);
37948 getBox : function(){
37949 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37952 getMargins : function(){
37953 return this.margins;
37956 updateBox : function(box){
37958 var el = this.activePanel.getEl();
37959 el.dom.style.left = box.x + "px";
37960 el.dom.style.top = box.y + "px";
37961 this.activePanel.setSize(box.width, box.height);
37965 * Returns the container element for this region.
37966 * @return {Roo.Element}
37968 getEl : function(){
37969 return this.activePanel;
37973 * Returns true if this region is currently visible.
37974 * @return {Boolean}
37976 isVisible : function(){
37977 return this.activePanel ? true : false;
37980 setActivePanel : function(panel){
37981 panel = this.getPanel(panel);
37982 if(this.activePanel && this.activePanel != panel){
37983 this.activePanel.setActiveState(false);
37984 this.activePanel.getEl().setLeftTop(-10000,-10000);
37986 this.activePanel = panel;
37987 panel.setActiveState(true);
37989 panel.setSize(this.box.width, this.box.height);
37991 this.fireEvent("panelactivated", this, panel);
37992 this.fireEvent("invalidated");
37996 * Show the specified panel.
37997 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37998 * @return {Roo.ContentPanel} The shown panel or null
38000 showPanel : function(panel){
38001 panel = this.getPanel(panel);
38003 this.setActivePanel(panel);
38009 * Get the active panel for this region.
38010 * @return {Roo.ContentPanel} The active panel or null
38012 getActivePanel : function(){
38013 return this.activePanel;
38017 * Add the passed ContentPanel(s)
38018 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38019 * @return {Roo.ContentPanel} The panel added (if only one was added)
38021 add : function(panel){
38022 if(arguments.length > 1){
38023 for(var i = 0, len = arguments.length; i < len; i++) {
38024 this.add(arguments[i]);
38028 if(this.hasPanel(panel)){
38029 this.showPanel(panel);
38032 var el = panel.getEl();
38033 if(el.dom.parentNode != this.mgr.el.dom){
38034 this.mgr.el.dom.appendChild(el.dom);
38036 if(panel.setRegion){
38037 panel.setRegion(this);
38039 this.panels.add(panel);
38040 el.setStyle("position", "absolute");
38041 if(!panel.background){
38042 this.setActivePanel(panel);
38043 if(this.config.initialSize && this.panels.getCount()==1){
38044 this.resizeTo(this.config.initialSize);
38047 this.fireEvent("paneladded", this, panel);
38052 * Returns true if the panel is in this region.
38053 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38054 * @return {Boolean}
38056 hasPanel : function(panel){
38057 if(typeof panel == "object"){ // must be panel obj
38058 panel = panel.getId();
38060 return this.getPanel(panel) ? true : false;
38064 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38065 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38066 * @param {Boolean} preservePanel Overrides the config preservePanel option
38067 * @return {Roo.ContentPanel} The panel that was removed
38069 remove : function(panel, preservePanel){
38070 panel = this.getPanel(panel);
38075 this.fireEvent("beforeremove", this, panel, e);
38076 if(e.cancel === true){
38079 var panelId = panel.getId();
38080 this.panels.removeKey(panelId);
38085 * Returns the panel specified or null if it's not in this region.
38086 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38087 * @return {Roo.ContentPanel}
38089 getPanel : function(id){
38090 if(typeof id == "object"){ // must be panel obj
38093 return this.panels.get(id);
38097 * Returns this regions position (north/south/east/west/center).
38100 getPosition: function(){
38101 return this.position;
38105 * Ext JS Library 1.1.1
38106 * Copyright(c) 2006-2007, Ext JS, LLC.
38108 * Originally Released Under LGPL - original licence link has changed is not relivant.
38111 * <script type="text/javascript">
38115 * @class Roo.bootstrap.layout.Region
38116 * @extends Roo.bootstrap.layout.Basic
38117 * This class represents a region in a layout manager.
38119 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38120 * @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})
38121 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38122 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38123 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38124 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38125 * @cfg {String} title The title for the region (overrides panel titles)
38126 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38127 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38128 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38129 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38130 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38131 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38132 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38133 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38134 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38135 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38137 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38138 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38139 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38140 * @cfg {Number} width For East/West panels
38141 * @cfg {Number} height For North/South panels
38142 * @cfg {Boolean} split To show the splitter
38143 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38145 * @cfg {string} cls Extra CSS classes to add to region
38147 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38148 * @cfg {string} region the region that it inhabits..
38151 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38152 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38154 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38155 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38156 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38158 Roo.bootstrap.layout.Region = function(config)
38160 this.applyConfig(config);
38162 var mgr = config.mgr;
38163 var pos = config.region;
38164 config.skipConfig = true;
38165 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38168 this.onRender(mgr.el);
38171 this.visible = true;
38172 this.collapsed = false;
38173 this.unrendered_panels = [];
38176 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38178 position: '', // set by wrapper (eg. north/south etc..)
38179 unrendered_panels : null, // unrendered panels.
38181 tabPosition : false,
38183 mgr: false, // points to 'Border'
38186 createBody : function(){
38187 /** This region's body element
38188 * @type Roo.Element */
38189 this.bodyEl = this.el.createChild({
38191 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38195 onRender: function(ctr, pos)
38197 var dh = Roo.DomHelper;
38198 /** This region's container element
38199 * @type Roo.Element */
38200 this.el = dh.append(ctr.dom, {
38202 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38204 /** This region's title element
38205 * @type Roo.Element */
38207 this.titleEl = dh.append(this.el.dom, {
38209 unselectable: "on",
38210 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38212 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38213 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38217 this.titleEl.enableDisplayMode();
38218 /** This region's title text element
38219 * @type HTMLElement */
38220 this.titleTextEl = this.titleEl.dom.firstChild;
38221 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38223 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38224 this.closeBtn.enableDisplayMode();
38225 this.closeBtn.on("click", this.closeClicked, this);
38226 this.closeBtn.hide();
38228 this.createBody(this.config);
38229 if(this.config.hideWhenEmpty){
38231 this.on("paneladded", this.validateVisibility, this);
38232 this.on("panelremoved", this.validateVisibility, this);
38234 if(this.autoScroll){
38235 this.bodyEl.setStyle("overflow", "auto");
38237 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38239 //if(c.titlebar !== false){
38240 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38241 this.titleEl.hide();
38243 this.titleEl.show();
38244 if(this.config.title){
38245 this.titleTextEl.innerHTML = this.config.title;
38249 if(this.config.collapsed){
38250 this.collapse(true);
38252 if(this.config.hidden){
38256 if (this.unrendered_panels && this.unrendered_panels.length) {
38257 for (var i =0;i< this.unrendered_panels.length; i++) {
38258 this.add(this.unrendered_panels[i]);
38260 this.unrendered_panels = null;
38266 applyConfig : function(c)
38269 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38270 var dh = Roo.DomHelper;
38271 if(c.titlebar !== false){
38272 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38273 this.collapseBtn.on("click", this.collapse, this);
38274 this.collapseBtn.enableDisplayMode();
38276 if(c.showPin === true || this.showPin){
38277 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38278 this.stickBtn.enableDisplayMode();
38279 this.stickBtn.on("click", this.expand, this);
38280 this.stickBtn.hide();
38285 /** This region's collapsed element
38286 * @type Roo.Element */
38289 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38290 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38293 if(c.floatable !== false){
38294 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38295 this.collapsedEl.on("click", this.collapseClick, this);
38298 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38299 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38300 id: "message", unselectable: "on", style:{"float":"left"}});
38301 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38303 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38304 this.expandBtn.on("click", this.expand, this);
38308 if(this.collapseBtn){
38309 this.collapseBtn.setVisible(c.collapsible == true);
38312 this.cmargins = c.cmargins || this.cmargins ||
38313 (this.position == "west" || this.position == "east" ?
38314 {top: 0, left: 2, right:2, bottom: 0} :
38315 {top: 2, left: 0, right:0, bottom: 2});
38317 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38320 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38322 this.autoScroll = c.autoScroll || false;
38327 this.duration = c.duration || .30;
38328 this.slideDuration = c.slideDuration || .45;
38333 * Returns true if this region is currently visible.
38334 * @return {Boolean}
38336 isVisible : function(){
38337 return this.visible;
38341 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38342 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38344 //setCollapsedTitle : function(title){
38345 // title = title || " ";
38346 // if(this.collapsedTitleTextEl){
38347 // this.collapsedTitleTextEl.innerHTML = title;
38351 getBox : function(){
38353 // if(!this.collapsed){
38354 b = this.el.getBox(false, true);
38356 // b = this.collapsedEl.getBox(false, true);
38361 getMargins : function(){
38362 return this.margins;
38363 //return this.collapsed ? this.cmargins : this.margins;
38366 highlight : function(){
38367 this.el.addClass("x-layout-panel-dragover");
38370 unhighlight : function(){
38371 this.el.removeClass("x-layout-panel-dragover");
38374 updateBox : function(box)
38376 if (!this.bodyEl) {
38377 return; // not rendered yet..
38381 if(!this.collapsed){
38382 this.el.dom.style.left = box.x + "px";
38383 this.el.dom.style.top = box.y + "px";
38384 this.updateBody(box.width, box.height);
38386 this.collapsedEl.dom.style.left = box.x + "px";
38387 this.collapsedEl.dom.style.top = box.y + "px";
38388 this.collapsedEl.setSize(box.width, box.height);
38391 this.tabs.autoSizeTabs();
38395 updateBody : function(w, h)
38398 this.el.setWidth(w);
38399 w -= this.el.getBorderWidth("rl");
38400 if(this.config.adjustments){
38401 w += this.config.adjustments[0];
38404 if(h !== null && h > 0){
38405 this.el.setHeight(h);
38406 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38407 h -= this.el.getBorderWidth("tb");
38408 if(this.config.adjustments){
38409 h += this.config.adjustments[1];
38411 this.bodyEl.setHeight(h);
38413 h = this.tabs.syncHeight(h);
38416 if(this.panelSize){
38417 w = w !== null ? w : this.panelSize.width;
38418 h = h !== null ? h : this.panelSize.height;
38420 if(this.activePanel){
38421 var el = this.activePanel.getEl();
38422 w = w !== null ? w : el.getWidth();
38423 h = h !== null ? h : el.getHeight();
38424 this.panelSize = {width: w, height: h};
38425 this.activePanel.setSize(w, h);
38427 if(Roo.isIE && this.tabs){
38428 this.tabs.el.repaint();
38433 * Returns the container element for this region.
38434 * @return {Roo.Element}
38436 getEl : function(){
38441 * Hides this region.
38444 //if(!this.collapsed){
38445 this.el.dom.style.left = "-2000px";
38448 // this.collapsedEl.dom.style.left = "-2000px";
38449 // this.collapsedEl.hide();
38451 this.visible = false;
38452 this.fireEvent("visibilitychange", this, false);
38456 * Shows this region if it was previously hidden.
38459 //if(!this.collapsed){
38462 // this.collapsedEl.show();
38464 this.visible = true;
38465 this.fireEvent("visibilitychange", this, true);
38468 closeClicked : function(){
38469 if(this.activePanel){
38470 this.remove(this.activePanel);
38474 collapseClick : function(e){
38476 e.stopPropagation();
38479 e.stopPropagation();
38485 * Collapses this region.
38486 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38489 collapse : function(skipAnim, skipCheck = false){
38490 if(this.collapsed) {
38494 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38496 this.collapsed = true;
38498 this.split.el.hide();
38500 if(this.config.animate && skipAnim !== true){
38501 this.fireEvent("invalidated", this);
38502 this.animateCollapse();
38504 this.el.setLocation(-20000,-20000);
38506 this.collapsedEl.show();
38507 this.fireEvent("collapsed", this);
38508 this.fireEvent("invalidated", this);
38514 animateCollapse : function(){
38519 * Expands this region if it was previously collapsed.
38520 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38521 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38524 expand : function(e, skipAnim){
38526 e.stopPropagation();
38528 if(!this.collapsed || this.el.hasActiveFx()) {
38532 this.afterSlideIn();
38535 this.collapsed = false;
38536 if(this.config.animate && skipAnim !== true){
38537 this.animateExpand();
38541 this.split.el.show();
38543 this.collapsedEl.setLocation(-2000,-2000);
38544 this.collapsedEl.hide();
38545 this.fireEvent("invalidated", this);
38546 this.fireEvent("expanded", this);
38550 animateExpand : function(){
38554 initTabs : function()
38556 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38558 var ts = new Roo.bootstrap.panel.Tabs({
38559 el: this.bodyEl.dom,
38561 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38562 disableTooltips: this.config.disableTabTips,
38563 toolbar : this.config.toolbar
38566 if(this.config.hideTabs){
38567 ts.stripWrap.setDisplayed(false);
38570 ts.resizeTabs = this.config.resizeTabs === true;
38571 ts.minTabWidth = this.config.minTabWidth || 40;
38572 ts.maxTabWidth = this.config.maxTabWidth || 250;
38573 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38574 ts.monitorResize = false;
38575 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38576 ts.bodyEl.addClass('roo-layout-tabs-body');
38577 this.panels.each(this.initPanelAsTab, this);
38580 initPanelAsTab : function(panel){
38581 var ti = this.tabs.addTab(
38585 this.config.closeOnTab && panel.isClosable(),
38588 if(panel.tabTip !== undefined){
38589 ti.setTooltip(panel.tabTip);
38591 ti.on("activate", function(){
38592 this.setActivePanel(panel);
38595 if(this.config.closeOnTab){
38596 ti.on("beforeclose", function(t, e){
38598 this.remove(panel);
38602 panel.tabItem = ti;
38607 updatePanelTitle : function(panel, title)
38609 if(this.activePanel == panel){
38610 this.updateTitle(title);
38613 var ti = this.tabs.getTab(panel.getEl().id);
38615 if(panel.tabTip !== undefined){
38616 ti.setTooltip(panel.tabTip);
38621 updateTitle : function(title){
38622 if(this.titleTextEl && !this.config.title){
38623 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38627 setActivePanel : function(panel)
38629 panel = this.getPanel(panel);
38630 if(this.activePanel && this.activePanel != panel){
38631 if(this.activePanel.setActiveState(false) === false){
38635 this.activePanel = panel;
38636 panel.setActiveState(true);
38637 if(this.panelSize){
38638 panel.setSize(this.panelSize.width, this.panelSize.height);
38641 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38643 this.updateTitle(panel.getTitle());
38645 this.fireEvent("invalidated", this);
38647 this.fireEvent("panelactivated", this, panel);
38651 * Shows the specified panel.
38652 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38653 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38655 showPanel : function(panel)
38657 panel = this.getPanel(panel);
38660 var tab = this.tabs.getTab(panel.getEl().id);
38661 if(tab.isHidden()){
38662 this.tabs.unhideTab(tab.id);
38666 this.setActivePanel(panel);
38673 * Get the active panel for this region.
38674 * @return {Roo.ContentPanel} The active panel or null
38676 getActivePanel : function(){
38677 return this.activePanel;
38680 validateVisibility : function(){
38681 if(this.panels.getCount() < 1){
38682 this.updateTitle(" ");
38683 this.closeBtn.hide();
38686 if(!this.isVisible()){
38693 * Adds the passed ContentPanel(s) to this region.
38694 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38695 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38697 add : function(panel)
38699 if(arguments.length > 1){
38700 for(var i = 0, len = arguments.length; i < len; i++) {
38701 this.add(arguments[i]);
38706 // if we have not been rendered yet, then we can not really do much of this..
38707 if (!this.bodyEl) {
38708 this.unrendered_panels.push(panel);
38715 if(this.hasPanel(panel)){
38716 this.showPanel(panel);
38719 panel.setRegion(this);
38720 this.panels.add(panel);
38721 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38722 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38723 // and hide them... ???
38724 this.bodyEl.dom.appendChild(panel.getEl().dom);
38725 if(panel.background !== true){
38726 this.setActivePanel(panel);
38728 this.fireEvent("paneladded", this, panel);
38735 this.initPanelAsTab(panel);
38739 if(panel.background !== true){
38740 this.tabs.activate(panel.getEl().id);
38742 this.fireEvent("paneladded", this, panel);
38747 * Hides the tab for the specified panel.
38748 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38750 hidePanel : function(panel){
38751 if(this.tabs && (panel = this.getPanel(panel))){
38752 this.tabs.hideTab(panel.getEl().id);
38757 * Unhides the tab for a previously hidden panel.
38758 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38760 unhidePanel : function(panel){
38761 if(this.tabs && (panel = this.getPanel(panel))){
38762 this.tabs.unhideTab(panel.getEl().id);
38766 clearPanels : function(){
38767 while(this.panels.getCount() > 0){
38768 this.remove(this.panels.first());
38773 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38774 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38775 * @param {Boolean} preservePanel Overrides the config preservePanel option
38776 * @return {Roo.ContentPanel} The panel that was removed
38778 remove : function(panel, preservePanel)
38780 panel = this.getPanel(panel);
38785 this.fireEvent("beforeremove", this, panel, e);
38786 if(e.cancel === true){
38789 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38790 var panelId = panel.getId();
38791 this.panels.removeKey(panelId);
38793 document.body.appendChild(panel.getEl().dom);
38796 this.tabs.removeTab(panel.getEl().id);
38797 }else if (!preservePanel){
38798 this.bodyEl.dom.removeChild(panel.getEl().dom);
38800 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38801 var p = this.panels.first();
38802 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38803 tempEl.appendChild(p.getEl().dom);
38804 this.bodyEl.update("");
38805 this.bodyEl.dom.appendChild(p.getEl().dom);
38807 this.updateTitle(p.getTitle());
38809 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38810 this.setActivePanel(p);
38812 panel.setRegion(null);
38813 if(this.activePanel == panel){
38814 this.activePanel = null;
38816 if(this.config.autoDestroy !== false && preservePanel !== true){
38817 try{panel.destroy();}catch(e){}
38819 this.fireEvent("panelremoved", this, panel);
38824 * Returns the TabPanel component used by this region
38825 * @return {Roo.TabPanel}
38827 getTabs : function(){
38831 createTool : function(parentEl, className){
38832 var btn = Roo.DomHelper.append(parentEl, {
38834 cls: "x-layout-tools-button",
38837 cls: "roo-layout-tools-button-inner " + className,
38841 btn.addClassOnOver("roo-layout-tools-button-over");
38846 * Ext JS Library 1.1.1
38847 * Copyright(c) 2006-2007, Ext JS, LLC.
38849 * Originally Released Under LGPL - original licence link has changed is not relivant.
38852 * <script type="text/javascript">
38858 * @class Roo.SplitLayoutRegion
38859 * @extends Roo.LayoutRegion
38860 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38862 Roo.bootstrap.layout.Split = function(config){
38863 this.cursor = config.cursor;
38864 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38867 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38869 splitTip : "Drag to resize.",
38870 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38871 useSplitTips : false,
38873 applyConfig : function(config){
38874 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38877 onRender : function(ctr,pos) {
38879 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38880 if(!this.config.split){
38885 var splitEl = Roo.DomHelper.append(ctr.dom, {
38887 id: this.el.id + "-split",
38888 cls: "roo-layout-split roo-layout-split-"+this.position,
38891 /** The SplitBar for this region
38892 * @type Roo.SplitBar */
38893 // does not exist yet...
38894 Roo.log([this.position, this.orientation]);
38896 this.split = new Roo.bootstrap.SplitBar({
38897 dragElement : splitEl,
38898 resizingElement: this.el,
38899 orientation : this.orientation
38902 this.split.on("moved", this.onSplitMove, this);
38903 this.split.useShim = this.config.useShim === true;
38904 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38905 if(this.useSplitTips){
38906 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38908 //if(config.collapsible){
38909 // this.split.el.on("dblclick", this.collapse, this);
38912 if(typeof this.config.minSize != "undefined"){
38913 this.split.minSize = this.config.minSize;
38915 if(typeof this.config.maxSize != "undefined"){
38916 this.split.maxSize = this.config.maxSize;
38918 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38919 this.hideSplitter();
38924 getHMaxSize : function(){
38925 var cmax = this.config.maxSize || 10000;
38926 var center = this.mgr.getRegion("center");
38927 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38930 getVMaxSize : function(){
38931 var cmax = this.config.maxSize || 10000;
38932 var center = this.mgr.getRegion("center");
38933 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38936 onSplitMove : function(split, newSize){
38937 this.fireEvent("resized", this, newSize);
38941 * Returns the {@link Roo.SplitBar} for this region.
38942 * @return {Roo.SplitBar}
38944 getSplitBar : function(){
38949 this.hideSplitter();
38950 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38953 hideSplitter : function(){
38955 this.split.el.setLocation(-2000,-2000);
38956 this.split.el.hide();
38962 this.split.el.show();
38964 Roo.bootstrap.layout.Split.superclass.show.call(this);
38967 beforeSlide: function(){
38968 if(Roo.isGecko){// firefox overflow auto bug workaround
38969 this.bodyEl.clip();
38971 this.tabs.bodyEl.clip();
38973 if(this.activePanel){
38974 this.activePanel.getEl().clip();
38976 if(this.activePanel.beforeSlide){
38977 this.activePanel.beforeSlide();
38983 afterSlide : function(){
38984 if(Roo.isGecko){// firefox overflow auto bug workaround
38985 this.bodyEl.unclip();
38987 this.tabs.bodyEl.unclip();
38989 if(this.activePanel){
38990 this.activePanel.getEl().unclip();
38991 if(this.activePanel.afterSlide){
38992 this.activePanel.afterSlide();
38998 initAutoHide : function(){
38999 if(this.autoHide !== false){
39000 if(!this.autoHideHd){
39001 var st = new Roo.util.DelayedTask(this.slideIn, this);
39002 this.autoHideHd = {
39003 "mouseout": function(e){
39004 if(!e.within(this.el, true)){
39008 "mouseover" : function(e){
39014 this.el.on(this.autoHideHd);
39018 clearAutoHide : function(){
39019 if(this.autoHide !== false){
39020 this.el.un("mouseout", this.autoHideHd.mouseout);
39021 this.el.un("mouseover", this.autoHideHd.mouseover);
39025 clearMonitor : function(){
39026 Roo.get(document).un("click", this.slideInIf, this);
39029 // these names are backwards but not changed for compat
39030 slideOut : function(){
39031 if(this.isSlid || this.el.hasActiveFx()){
39034 this.isSlid = true;
39035 if(this.collapseBtn){
39036 this.collapseBtn.hide();
39038 this.closeBtnState = this.closeBtn.getStyle('display');
39039 this.closeBtn.hide();
39041 this.stickBtn.show();
39044 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39045 this.beforeSlide();
39046 this.el.setStyle("z-index", 10001);
39047 this.el.slideIn(this.getSlideAnchor(), {
39048 callback: function(){
39050 this.initAutoHide();
39051 Roo.get(document).on("click", this.slideInIf, this);
39052 this.fireEvent("slideshow", this);
39059 afterSlideIn : function(){
39060 this.clearAutoHide();
39061 this.isSlid = false;
39062 this.clearMonitor();
39063 this.el.setStyle("z-index", "");
39064 if(this.collapseBtn){
39065 this.collapseBtn.show();
39067 this.closeBtn.setStyle('display', this.closeBtnState);
39069 this.stickBtn.hide();
39071 this.fireEvent("slidehide", this);
39074 slideIn : function(cb){
39075 if(!this.isSlid || this.el.hasActiveFx()){
39079 this.isSlid = false;
39080 this.beforeSlide();
39081 this.el.slideOut(this.getSlideAnchor(), {
39082 callback: function(){
39083 this.el.setLeftTop(-10000, -10000);
39085 this.afterSlideIn();
39093 slideInIf : function(e){
39094 if(!e.within(this.el)){
39099 animateCollapse : function(){
39100 this.beforeSlide();
39101 this.el.setStyle("z-index", 20000);
39102 var anchor = this.getSlideAnchor();
39103 this.el.slideOut(anchor, {
39104 callback : function(){
39105 this.el.setStyle("z-index", "");
39106 this.collapsedEl.slideIn(anchor, {duration:.3});
39108 this.el.setLocation(-10000,-10000);
39110 this.fireEvent("collapsed", this);
39117 animateExpand : function(){
39118 this.beforeSlide();
39119 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39120 this.el.setStyle("z-index", 20000);
39121 this.collapsedEl.hide({
39124 this.el.slideIn(this.getSlideAnchor(), {
39125 callback : function(){
39126 this.el.setStyle("z-index", "");
39129 this.split.el.show();
39131 this.fireEvent("invalidated", this);
39132 this.fireEvent("expanded", this);
39160 getAnchor : function(){
39161 return this.anchors[this.position];
39164 getCollapseAnchor : function(){
39165 return this.canchors[this.position];
39168 getSlideAnchor : function(){
39169 return this.sanchors[this.position];
39172 getAlignAdj : function(){
39173 var cm = this.cmargins;
39174 switch(this.position){
39190 getExpandAdj : function(){
39191 var c = this.collapsedEl, cm = this.cmargins;
39192 switch(this.position){
39194 return [-(cm.right+c.getWidth()+cm.left), 0];
39197 return [cm.right+c.getWidth()+cm.left, 0];
39200 return [0, -(cm.top+cm.bottom+c.getHeight())];
39203 return [0, cm.top+cm.bottom+c.getHeight()];
39209 * Ext JS Library 1.1.1
39210 * Copyright(c) 2006-2007, Ext JS, LLC.
39212 * Originally Released Under LGPL - original licence link has changed is not relivant.
39215 * <script type="text/javascript">
39218 * These classes are private internal classes
39220 Roo.bootstrap.layout.Center = function(config){
39221 config.region = "center";
39222 Roo.bootstrap.layout.Region.call(this, config);
39223 this.visible = true;
39224 this.minWidth = config.minWidth || 20;
39225 this.minHeight = config.minHeight || 20;
39228 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39230 // center panel can't be hidden
39234 // center panel can't be hidden
39237 getMinWidth: function(){
39238 return this.minWidth;
39241 getMinHeight: function(){
39242 return this.minHeight;
39256 Roo.bootstrap.layout.North = function(config)
39258 config.region = 'north';
39259 config.cursor = 'n-resize';
39261 Roo.bootstrap.layout.Split.call(this, config);
39265 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39266 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39267 this.split.el.addClass("roo-layout-split-v");
39269 var size = config.initialSize || config.height;
39270 if(typeof size != "undefined"){
39271 this.el.setHeight(size);
39274 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39276 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39280 getBox : function(){
39281 if(this.collapsed){
39282 return this.collapsedEl.getBox();
39284 var box = this.el.getBox();
39286 box.height += this.split.el.getHeight();
39291 updateBox : function(box){
39292 if(this.split && !this.collapsed){
39293 box.height -= this.split.el.getHeight();
39294 this.split.el.setLeft(box.x);
39295 this.split.el.setTop(box.y+box.height);
39296 this.split.el.setWidth(box.width);
39298 if(this.collapsed){
39299 this.updateBody(box.width, null);
39301 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39309 Roo.bootstrap.layout.South = function(config){
39310 config.region = 'south';
39311 config.cursor = 's-resize';
39312 Roo.bootstrap.layout.Split.call(this, config);
39314 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39315 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39316 this.split.el.addClass("roo-layout-split-v");
39318 var size = config.initialSize || config.height;
39319 if(typeof size != "undefined"){
39320 this.el.setHeight(size);
39324 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39325 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39326 getBox : function(){
39327 if(this.collapsed){
39328 return this.collapsedEl.getBox();
39330 var box = this.el.getBox();
39332 var sh = this.split.el.getHeight();
39339 updateBox : function(box){
39340 if(this.split && !this.collapsed){
39341 var sh = this.split.el.getHeight();
39344 this.split.el.setLeft(box.x);
39345 this.split.el.setTop(box.y-sh);
39346 this.split.el.setWidth(box.width);
39348 if(this.collapsed){
39349 this.updateBody(box.width, null);
39351 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39355 Roo.bootstrap.layout.East = function(config){
39356 config.region = "east";
39357 config.cursor = "e-resize";
39358 Roo.bootstrap.layout.Split.call(this, config);
39360 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39361 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39362 this.split.el.addClass("roo-layout-split-h");
39364 var size = config.initialSize || config.width;
39365 if(typeof size != "undefined"){
39366 this.el.setWidth(size);
39369 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39370 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39371 getBox : function(){
39372 if(this.collapsed){
39373 return this.collapsedEl.getBox();
39375 var box = this.el.getBox();
39377 var sw = this.split.el.getWidth();
39384 updateBox : function(box){
39385 if(this.split && !this.collapsed){
39386 var sw = this.split.el.getWidth();
39388 this.split.el.setLeft(box.x);
39389 this.split.el.setTop(box.y);
39390 this.split.el.setHeight(box.height);
39393 if(this.collapsed){
39394 this.updateBody(null, box.height);
39396 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39400 Roo.bootstrap.layout.West = function(config){
39401 config.region = "west";
39402 config.cursor = "w-resize";
39404 Roo.bootstrap.layout.Split.call(this, config);
39406 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39407 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39408 this.split.el.addClass("roo-layout-split-h");
39412 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39413 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39415 onRender: function(ctr, pos)
39417 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39418 var size = this.config.initialSize || this.config.width;
39419 if(typeof size != "undefined"){
39420 this.el.setWidth(size);
39424 getBox : function(){
39425 if(this.collapsed){
39426 return this.collapsedEl.getBox();
39428 var box = this.el.getBox();
39430 box.width += this.split.el.getWidth();
39435 updateBox : function(box){
39436 if(this.split && !this.collapsed){
39437 var sw = this.split.el.getWidth();
39439 this.split.el.setLeft(box.x+box.width);
39440 this.split.el.setTop(box.y);
39441 this.split.el.setHeight(box.height);
39443 if(this.collapsed){
39444 this.updateBody(null, box.height);
39446 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39448 });Roo.namespace("Roo.bootstrap.panel");/*
39450 * Ext JS Library 1.1.1
39451 * Copyright(c) 2006-2007, Ext JS, LLC.
39453 * Originally Released Under LGPL - original licence link has changed is not relivant.
39456 * <script type="text/javascript">
39459 * @class Roo.ContentPanel
39460 * @extends Roo.util.Observable
39461 * A basic ContentPanel element.
39462 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39463 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39464 * @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
39465 * @cfg {Boolean} closable True if the panel can be closed/removed
39466 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39467 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39468 * @cfg {Toolbar} toolbar A toolbar for this panel
39469 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39470 * @cfg {String} title The title for this panel
39471 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39472 * @cfg {String} url Calls {@link #setUrl} with this value
39473 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39474 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39475 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39476 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39477 * @cfg {Boolean} badges render the badges
39478 * @cfg {String} cls extra classes to use
39479 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39482 * Create a new ContentPanel.
39483 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39484 * @param {String/Object} config A string to set only the title or a config object
39485 * @param {String} content (optional) Set the HTML content for this panel
39486 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39488 Roo.bootstrap.panel.Content = function( config){
39490 this.tpl = config.tpl || false;
39492 var el = config.el;
39493 var content = config.content;
39495 if(config.autoCreate){ // xtype is available if this is called from factory
39498 this.el = Roo.get(el);
39499 if(!this.el && config && config.autoCreate){
39500 if(typeof config.autoCreate == "object"){
39501 if(!config.autoCreate.id){
39502 config.autoCreate.id = config.id||el;
39504 this.el = Roo.DomHelper.append(document.body,
39505 config.autoCreate, true);
39509 cls: (config.cls || '') +
39510 (config.background ? ' bg-' + config.background : '') +
39511 " roo-layout-inactive-content",
39515 elcfg.html = config.html;
39519 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39522 this.closable = false;
39523 this.loaded = false;
39524 this.active = false;
39527 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39529 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39531 this.wrapEl = this.el; //this.el.wrap();
39533 if (config.toolbar.items) {
39534 ti = config.toolbar.items ;
39535 delete config.toolbar.items ;
39539 this.toolbar.render(this.wrapEl, 'before');
39540 for(var i =0;i < ti.length;i++) {
39541 // Roo.log(['add child', items[i]]);
39542 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39544 this.toolbar.items = nitems;
39545 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39546 delete config.toolbar;
39550 // xtype created footer. - not sure if will work as we normally have to render first..
39551 if (this.footer && !this.footer.el && this.footer.xtype) {
39552 if (!this.wrapEl) {
39553 this.wrapEl = this.el.wrap();
39556 this.footer.container = this.wrapEl.createChild();
39558 this.footer = Roo.factory(this.footer, Roo);
39563 if(typeof config == "string"){
39564 this.title = config;
39566 Roo.apply(this, config);
39570 this.resizeEl = Roo.get(this.resizeEl, true);
39572 this.resizeEl = this.el;
39574 // handle view.xtype
39582 * Fires when this panel is activated.
39583 * @param {Roo.ContentPanel} this
39587 * @event deactivate
39588 * Fires when this panel is activated.
39589 * @param {Roo.ContentPanel} this
39591 "deactivate" : true,
39595 * Fires when this panel is resized if fitToFrame is true.
39596 * @param {Roo.ContentPanel} this
39597 * @param {Number} width The width after any component adjustments
39598 * @param {Number} height The height after any component adjustments
39604 * Fires when this tab is created
39605 * @param {Roo.ContentPanel} this
39616 if(this.autoScroll){
39617 this.resizeEl.setStyle("overflow", "auto");
39619 // fix randome scrolling
39620 //this.el.on('scroll', function() {
39621 // Roo.log('fix random scolling');
39622 // this.scrollTo('top',0);
39625 content = content || this.content;
39627 this.setContent(content);
39629 if(config && config.url){
39630 this.setUrl(this.url, this.params, this.loadOnce);
39635 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39637 if (this.view && typeof(this.view.xtype) != 'undefined') {
39638 this.view.el = this.el.appendChild(document.createElement("div"));
39639 this.view = Roo.factory(this.view);
39640 this.view.render && this.view.render(false, '');
39644 this.fireEvent('render', this);
39647 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39654 setRegion : function(region){
39655 this.region = region;
39656 this.setActiveClass(region && !this.background);
39660 setActiveClass: function(state)
39663 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39664 this.el.setStyle('position','relative');
39666 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39667 this.el.setStyle('position', 'absolute');
39672 * Returns the toolbar for this Panel if one was configured.
39673 * @return {Roo.Toolbar}
39675 getToolbar : function(){
39676 return this.toolbar;
39679 setActiveState : function(active)
39681 this.active = active;
39682 this.setActiveClass(active);
39684 if(this.fireEvent("deactivate", this) === false){
39689 this.fireEvent("activate", this);
39693 * Updates this panel's element
39694 * @param {String} content The new content
39695 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39697 setContent : function(content, loadScripts){
39698 this.el.update(content, loadScripts);
39701 ignoreResize : function(w, h){
39702 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39705 this.lastSize = {width: w, height: h};
39710 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39711 * @return {Roo.UpdateManager} The UpdateManager
39713 getUpdateManager : function(){
39714 return this.el.getUpdateManager();
39717 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39718 * @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:
39721 url: "your-url.php",
39722 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39723 callback: yourFunction,
39724 scope: yourObject, //(optional scope)
39727 text: "Loading...",
39732 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39733 * 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.
39734 * @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}
39735 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39736 * @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.
39737 * @return {Roo.ContentPanel} this
39740 var um = this.el.getUpdateManager();
39741 um.update.apply(um, arguments);
39747 * 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.
39748 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39749 * @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)
39750 * @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)
39751 * @return {Roo.UpdateManager} The UpdateManager
39753 setUrl : function(url, params, loadOnce){
39754 if(this.refreshDelegate){
39755 this.removeListener("activate", this.refreshDelegate);
39757 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39758 this.on("activate", this.refreshDelegate);
39759 return this.el.getUpdateManager();
39762 _handleRefresh : function(url, params, loadOnce){
39763 if(!loadOnce || !this.loaded){
39764 var updater = this.el.getUpdateManager();
39765 updater.update(url, params, this._setLoaded.createDelegate(this));
39769 _setLoaded : function(){
39770 this.loaded = true;
39774 * Returns this panel's id
39777 getId : function(){
39782 * Returns this panel's element - used by regiosn to add.
39783 * @return {Roo.Element}
39785 getEl : function(){
39786 return this.wrapEl || this.el;
39791 adjustForComponents : function(width, height)
39793 //Roo.log('adjustForComponents ');
39794 if(this.resizeEl != this.el){
39795 width -= this.el.getFrameWidth('lr');
39796 height -= this.el.getFrameWidth('tb');
39799 var te = this.toolbar.getEl();
39800 te.setWidth(width);
39801 height -= te.getHeight();
39804 var te = this.footer.getEl();
39805 te.setWidth(width);
39806 height -= te.getHeight();
39810 if(this.adjustments){
39811 width += this.adjustments[0];
39812 height += this.adjustments[1];
39814 return {"width": width, "height": height};
39817 setSize : function(width, height){
39818 if(this.fitToFrame && !this.ignoreResize(width, height)){
39819 if(this.fitContainer && this.resizeEl != this.el){
39820 this.el.setSize(width, height);
39822 var size = this.adjustForComponents(width, height);
39823 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39824 this.fireEvent('resize', this, size.width, size.height);
39829 * Returns this panel's title
39832 getTitle : function(){
39834 if (typeof(this.title) != 'object') {
39839 for (var k in this.title) {
39840 if (!this.title.hasOwnProperty(k)) {
39844 if (k.indexOf('-') >= 0) {
39845 var s = k.split('-');
39846 for (var i = 0; i<s.length; i++) {
39847 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39850 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39857 * Set this panel's title
39858 * @param {String} title
39860 setTitle : function(title){
39861 this.title = title;
39863 this.region.updatePanelTitle(this, title);
39868 * Returns true is this panel was configured to be closable
39869 * @return {Boolean}
39871 isClosable : function(){
39872 return this.closable;
39875 beforeSlide : function(){
39877 this.resizeEl.clip();
39880 afterSlide : function(){
39882 this.resizeEl.unclip();
39886 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39887 * Will fail silently if the {@link #setUrl} method has not been called.
39888 * This does not activate the panel, just updates its content.
39890 refresh : function(){
39891 if(this.refreshDelegate){
39892 this.loaded = false;
39893 this.refreshDelegate();
39898 * Destroys this panel
39900 destroy : function(){
39901 this.el.removeAllListeners();
39902 var tempEl = document.createElement("span");
39903 tempEl.appendChild(this.el.dom);
39904 tempEl.innerHTML = "";
39910 * form - if the content panel contains a form - this is a reference to it.
39911 * @type {Roo.form.Form}
39915 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39916 * This contains a reference to it.
39922 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39932 * @param {Object} cfg Xtype definition of item to add.
39936 getChildContainer: function () {
39937 return this.getEl();
39942 var ret = new Roo.factory(cfg);
39947 if (cfg.xtype.match(/^Form$/)) {
39950 //if (this.footer) {
39951 // el = this.footer.container.insertSibling(false, 'before');
39953 el = this.el.createChild();
39956 this.form = new Roo.form.Form(cfg);
39959 if ( this.form.allItems.length) {
39960 this.form.render(el.dom);
39964 // should only have one of theses..
39965 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39966 // views.. should not be just added - used named prop 'view''
39968 cfg.el = this.el.appendChild(document.createElement("div"));
39971 var ret = new Roo.factory(cfg);
39973 ret.render && ret.render(false, ''); // render blank..
39983 * @class Roo.bootstrap.panel.Grid
39984 * @extends Roo.bootstrap.panel.Content
39986 * Create a new GridPanel.
39987 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39988 * @param {Object} config A the config object
39994 Roo.bootstrap.panel.Grid = function(config)
39998 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39999 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40001 config.el = this.wrapper;
40002 //this.el = this.wrapper;
40004 if (config.container) {
40005 // ctor'ed from a Border/panel.grid
40008 this.wrapper.setStyle("overflow", "hidden");
40009 this.wrapper.addClass('roo-grid-container');
40014 if(config.toolbar){
40015 var tool_el = this.wrapper.createChild();
40016 this.toolbar = Roo.factory(config.toolbar);
40018 if (config.toolbar.items) {
40019 ti = config.toolbar.items ;
40020 delete config.toolbar.items ;
40024 this.toolbar.render(tool_el);
40025 for(var i =0;i < ti.length;i++) {
40026 // Roo.log(['add child', items[i]]);
40027 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40029 this.toolbar.items = nitems;
40031 delete config.toolbar;
40034 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40035 config.grid.scrollBody = true;;
40036 config.grid.monitorWindowResize = false; // turn off autosizing
40037 config.grid.autoHeight = false;
40038 config.grid.autoWidth = false;
40040 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40042 if (config.background) {
40043 // render grid on panel activation (if panel background)
40044 this.on('activate', function(gp) {
40045 if (!gp.grid.rendered) {
40046 gp.grid.render(this.wrapper);
40047 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40052 this.grid.render(this.wrapper);
40053 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40056 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40057 // ??? needed ??? config.el = this.wrapper;
40062 // xtype created footer. - not sure if will work as we normally have to render first..
40063 if (this.footer && !this.footer.el && this.footer.xtype) {
40065 var ctr = this.grid.getView().getFooterPanel(true);
40066 this.footer.dataSource = this.grid.dataSource;
40067 this.footer = Roo.factory(this.footer, Roo);
40068 this.footer.render(ctr);
40078 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40079 getId : function(){
40080 return this.grid.id;
40084 * Returns the grid for this panel
40085 * @return {Roo.bootstrap.Table}
40087 getGrid : function(){
40091 setSize : function(width, height){
40092 if(!this.ignoreResize(width, height)){
40093 var grid = this.grid;
40094 var size = this.adjustForComponents(width, height);
40095 // tfoot is not a footer?
40098 var gridel = grid.getGridEl();
40099 gridel.setSize(size.width, size.height);
40101 var tbd = grid.getGridEl().select('tbody', true).first();
40102 var thd = grid.getGridEl().select('thead',true).first();
40103 var tbf= grid.getGridEl().select('tfoot', true).first();
40106 size.height -= thd.getHeight();
40109 size.height -= thd.getHeight();
40112 tbd.setSize(size.width, size.height );
40113 // this is for the account management tab -seems to work there.
40114 var thd = grid.getGridEl().select('thead',true).first();
40116 // tbd.setSize(size.width, size.height - thd.getHeight());
40125 beforeSlide : function(){
40126 this.grid.getView().scroller.clip();
40129 afterSlide : function(){
40130 this.grid.getView().scroller.unclip();
40133 destroy : function(){
40134 this.grid.destroy();
40136 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40141 * @class Roo.bootstrap.panel.Nest
40142 * @extends Roo.bootstrap.panel.Content
40144 * Create a new Panel, that can contain a layout.Border.
40147 * @param {Roo.BorderLayout} layout The layout for this panel
40148 * @param {String/Object} config A string to set only the title or a config object
40150 Roo.bootstrap.panel.Nest = function(config)
40152 // construct with only one argument..
40153 /* FIXME - implement nicer consturctors
40154 if (layout.layout) {
40156 layout = config.layout;
40157 delete config.layout;
40159 if (layout.xtype && !layout.getEl) {
40160 // then layout needs constructing..
40161 layout = Roo.factory(layout, Roo);
40165 config.el = config.layout.getEl();
40167 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40169 config.layout.monitorWindowResize = false; // turn off autosizing
40170 this.layout = config.layout;
40171 this.layout.getEl().addClass("roo-layout-nested-layout");
40172 this.layout.parent = this;
40179 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40181 setSize : function(width, height){
40182 if(!this.ignoreResize(width, height)){
40183 var size = this.adjustForComponents(width, height);
40184 var el = this.layout.getEl();
40185 if (size.height < 1) {
40186 el.setWidth(size.width);
40188 el.setSize(size.width, size.height);
40190 var touch = el.dom.offsetWidth;
40191 this.layout.layout();
40192 // ie requires a double layout on the first pass
40193 if(Roo.isIE && !this.initialized){
40194 this.initialized = true;
40195 this.layout.layout();
40200 // activate all subpanels if not currently active..
40202 setActiveState : function(active){
40203 this.active = active;
40204 this.setActiveClass(active);
40207 this.fireEvent("deactivate", this);
40211 this.fireEvent("activate", this);
40212 // not sure if this should happen before or after..
40213 if (!this.layout) {
40214 return; // should not happen..
40217 for (var r in this.layout.regions) {
40218 reg = this.layout.getRegion(r);
40219 if (reg.getActivePanel()) {
40220 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40221 reg.setActivePanel(reg.getActivePanel());
40224 if (!reg.panels.length) {
40227 reg.showPanel(reg.getPanel(0));
40236 * Returns the nested BorderLayout for this panel
40237 * @return {Roo.BorderLayout}
40239 getLayout : function(){
40240 return this.layout;
40244 * Adds a xtype elements to the layout of the nested panel
40248 xtype : 'ContentPanel',
40255 xtype : 'NestedLayoutPanel',
40261 items : [ ... list of content panels or nested layout panels.. ]
40265 * @param {Object} cfg Xtype definition of item to add.
40267 addxtype : function(cfg) {
40268 return this.layout.addxtype(cfg);
40273 * Ext JS Library 1.1.1
40274 * Copyright(c) 2006-2007, Ext JS, LLC.
40276 * Originally Released Under LGPL - original licence link has changed is not relivant.
40279 * <script type="text/javascript">
40282 * @class Roo.TabPanel
40283 * @extends Roo.util.Observable
40284 * A lightweight tab container.
40288 // basic tabs 1, built from existing content
40289 var tabs = new Roo.TabPanel("tabs1");
40290 tabs.addTab("script", "View Script");
40291 tabs.addTab("markup", "View Markup");
40292 tabs.activate("script");
40294 // more advanced tabs, built from javascript
40295 var jtabs = new Roo.TabPanel("jtabs");
40296 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40298 // set up the UpdateManager
40299 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40300 var updater = tab2.getUpdateManager();
40301 updater.setDefaultUrl("ajax1.htm");
40302 tab2.on('activate', updater.refresh, updater, true);
40304 // Use setUrl for Ajax loading
40305 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40306 tab3.setUrl("ajax2.htm", null, true);
40309 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40312 jtabs.activate("jtabs-1");
40315 * Create a new TabPanel.
40316 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40317 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40319 Roo.bootstrap.panel.Tabs = function(config){
40321 * The container element for this TabPanel.
40322 * @type Roo.Element
40324 this.el = Roo.get(config.el);
40327 if(typeof config == "boolean"){
40328 this.tabPosition = config ? "bottom" : "top";
40330 Roo.apply(this, config);
40334 if(this.tabPosition == "bottom"){
40335 // if tabs are at the bottom = create the body first.
40336 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40337 this.el.addClass("roo-tabs-bottom");
40339 // next create the tabs holders
40341 if (this.tabPosition == "west"){
40343 var reg = this.region; // fake it..
40345 if (!reg.mgr.parent) {
40348 reg = reg.mgr.parent.region;
40350 Roo.log("got nest?");
40352 if (reg.mgr.getRegion('west')) {
40353 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40354 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40355 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40356 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40357 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40365 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40366 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40367 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40368 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40373 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40376 // finally - if tabs are at the top, then create the body last..
40377 if(this.tabPosition != "bottom"){
40378 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40379 * @type Roo.Element
40381 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40382 this.el.addClass("roo-tabs-top");
40386 this.bodyEl.setStyle("position", "relative");
40388 this.active = null;
40389 this.activateDelegate = this.activate.createDelegate(this);
40394 * Fires when the active tab changes
40395 * @param {Roo.TabPanel} this
40396 * @param {Roo.TabPanelItem} activePanel The new active tab
40400 * @event beforetabchange
40401 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40402 * @param {Roo.TabPanel} this
40403 * @param {Object} e Set cancel to true on this object to cancel the tab change
40404 * @param {Roo.TabPanelItem} tab The tab being changed to
40406 "beforetabchange" : true
40409 Roo.EventManager.onWindowResize(this.onResize, this);
40410 this.cpad = this.el.getPadding("lr");
40411 this.hiddenCount = 0;
40414 // toolbar on the tabbar support...
40415 if (this.toolbar) {
40416 alert("no toolbar support yet");
40417 this.toolbar = false;
40419 var tcfg = this.toolbar;
40420 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40421 this.toolbar = new Roo.Toolbar(tcfg);
40422 if (Roo.isSafari) {
40423 var tbl = tcfg.container.child('table', true);
40424 tbl.setAttribute('width', '100%');
40432 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40435 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40437 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40439 tabPosition : "top",
40441 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40443 currentTabWidth : 0,
40445 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40449 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40453 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40455 preferredTabWidth : 175,
40457 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40459 resizeTabs : false,
40461 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40463 monitorResize : true,
40465 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40467 toolbar : false, // set by caller..
40469 region : false, /// set by caller
40471 disableTooltips : true, // not used yet...
40474 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40475 * @param {String} id The id of the div to use <b>or create</b>
40476 * @param {String} text The text for the tab
40477 * @param {String} content (optional) Content to put in the TabPanelItem body
40478 * @param {Boolean} closable (optional) True to create a close icon on the tab
40479 * @return {Roo.TabPanelItem} The created TabPanelItem
40481 addTab : function(id, text, content, closable, tpl)
40483 var item = new Roo.bootstrap.panel.TabItem({
40487 closable : closable,
40490 this.addTabItem(item);
40492 item.setContent(content);
40498 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40499 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40500 * @return {Roo.TabPanelItem}
40502 getTab : function(id){
40503 return this.items[id];
40507 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40508 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40510 hideTab : function(id){
40511 var t = this.items[id];
40514 this.hiddenCount++;
40515 this.autoSizeTabs();
40520 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40521 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40523 unhideTab : function(id){
40524 var t = this.items[id];
40526 t.setHidden(false);
40527 this.hiddenCount--;
40528 this.autoSizeTabs();
40533 * Adds an existing {@link Roo.TabPanelItem}.
40534 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40536 addTabItem : function(item)
40538 this.items[item.id] = item;
40539 this.items.push(item);
40540 this.autoSizeTabs();
40541 // if(this.resizeTabs){
40542 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40543 // this.autoSizeTabs();
40545 // item.autoSize();
40550 * Removes a {@link Roo.TabPanelItem}.
40551 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40553 removeTab : function(id){
40554 var items = this.items;
40555 var tab = items[id];
40556 if(!tab) { return; }
40557 var index = items.indexOf(tab);
40558 if(this.active == tab && items.length > 1){
40559 var newTab = this.getNextAvailable(index);
40564 this.stripEl.dom.removeChild(tab.pnode.dom);
40565 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40566 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40568 items.splice(index, 1);
40569 delete this.items[tab.id];
40570 tab.fireEvent("close", tab);
40571 tab.purgeListeners();
40572 this.autoSizeTabs();
40575 getNextAvailable : function(start){
40576 var items = this.items;
40578 // look for a next tab that will slide over to
40579 // replace the one being removed
40580 while(index < items.length){
40581 var item = items[++index];
40582 if(item && !item.isHidden()){
40586 // if one isn't found select the previous tab (on the left)
40589 var item = items[--index];
40590 if(item && !item.isHidden()){
40598 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40599 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40601 disableTab : function(id){
40602 var tab = this.items[id];
40603 if(tab && this.active != tab){
40609 * Enables a {@link Roo.TabPanelItem} that is disabled.
40610 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40612 enableTab : function(id){
40613 var tab = this.items[id];
40618 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40619 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40620 * @return {Roo.TabPanelItem} The TabPanelItem.
40622 activate : function(id)
40624 //Roo.log('activite:' + id);
40626 var tab = this.items[id];
40630 if(tab == this.active || tab.disabled){
40634 this.fireEvent("beforetabchange", this, e, tab);
40635 if(e.cancel !== true && !tab.disabled){
40637 this.active.hide();
40639 this.active = this.items[id];
40640 this.active.show();
40641 this.fireEvent("tabchange", this, this.active);
40647 * Gets the active {@link Roo.TabPanelItem}.
40648 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40650 getActiveTab : function(){
40651 return this.active;
40655 * Updates the tab body element to fit the height of the container element
40656 * for overflow scrolling
40657 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40659 syncHeight : function(targetHeight){
40660 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40661 var bm = this.bodyEl.getMargins();
40662 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40663 this.bodyEl.setHeight(newHeight);
40667 onResize : function(){
40668 if(this.monitorResize){
40669 this.autoSizeTabs();
40674 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40676 beginUpdate : function(){
40677 this.updating = true;
40681 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40683 endUpdate : function(){
40684 this.updating = false;
40685 this.autoSizeTabs();
40689 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40691 autoSizeTabs : function()
40693 var count = this.items.length;
40694 var vcount = count - this.hiddenCount;
40697 this.stripEl.hide();
40699 this.stripEl.show();
40702 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40707 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40708 var availWidth = Math.floor(w / vcount);
40709 var b = this.stripBody;
40710 if(b.getWidth() > w){
40711 var tabs = this.items;
40712 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40713 if(availWidth < this.minTabWidth){
40714 /*if(!this.sleft){ // incomplete scrolling code
40715 this.createScrollButtons();
40718 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40721 if(this.currentTabWidth < this.preferredTabWidth){
40722 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40728 * Returns the number of tabs in this TabPanel.
40731 getCount : function(){
40732 return this.items.length;
40736 * Resizes all the tabs to the passed width
40737 * @param {Number} The new width
40739 setTabWidth : function(width){
40740 this.currentTabWidth = width;
40741 for(var i = 0, len = this.items.length; i < len; i++) {
40742 if(!this.items[i].isHidden()) {
40743 this.items[i].setWidth(width);
40749 * Destroys this TabPanel
40750 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40752 destroy : function(removeEl){
40753 Roo.EventManager.removeResizeListener(this.onResize, this);
40754 for(var i = 0, len = this.items.length; i < len; i++){
40755 this.items[i].purgeListeners();
40757 if(removeEl === true){
40758 this.el.update("");
40763 createStrip : function(container)
40765 var strip = document.createElement("nav");
40766 strip.className = Roo.bootstrap.version == 4 ?
40767 "navbar-light bg-light" :
40768 "navbar navbar-default"; //"x-tabs-wrap";
40769 container.appendChild(strip);
40773 createStripList : function(strip)
40775 // div wrapper for retard IE
40776 // returns the "tr" element.
40777 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40778 //'<div class="x-tabs-strip-wrap">'+
40779 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40780 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40781 return strip.firstChild; //.firstChild.firstChild.firstChild;
40783 createBody : function(container)
40785 var body = document.createElement("div");
40786 Roo.id(body, "tab-body");
40787 //Roo.fly(body).addClass("x-tabs-body");
40788 Roo.fly(body).addClass("tab-content");
40789 container.appendChild(body);
40792 createItemBody :function(bodyEl, id){
40793 var body = Roo.getDom(id);
40795 body = document.createElement("div");
40798 //Roo.fly(body).addClass("x-tabs-item-body");
40799 Roo.fly(body).addClass("tab-pane");
40800 bodyEl.insertBefore(body, bodyEl.firstChild);
40804 createStripElements : function(stripEl, text, closable, tpl)
40806 var td = document.createElement("li"); // was td..
40807 td.className = 'nav-item';
40809 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40812 stripEl.appendChild(td);
40814 td.className = "x-tabs-closable";
40815 if(!this.closeTpl){
40816 this.closeTpl = new Roo.Template(
40817 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40818 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40819 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40822 var el = this.closeTpl.overwrite(td, {"text": text});
40823 var close = el.getElementsByTagName("div")[0];
40824 var inner = el.getElementsByTagName("em")[0];
40825 return {"el": el, "close": close, "inner": inner};
40828 // not sure what this is..
40829 // if(!this.tabTpl){
40830 //this.tabTpl = new Roo.Template(
40831 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40832 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40834 // this.tabTpl = new Roo.Template(
40835 // '<a href="#">' +
40836 // '<span unselectable="on"' +
40837 // (this.disableTooltips ? '' : ' title="{text}"') +
40838 // ' >{text}</span></a>'
40844 var template = tpl || this.tabTpl || false;
40847 template = new Roo.Template(
40848 Roo.bootstrap.version == 4 ?
40850 '<a class="nav-link" href="#" unselectable="on"' +
40851 (this.disableTooltips ? '' : ' title="{text}"') +
40854 '<a class="nav-link" href="#">' +
40855 '<span unselectable="on"' +
40856 (this.disableTooltips ? '' : ' title="{text}"') +
40857 ' >{text}</span></a>'
40862 switch (typeof(template)) {
40866 template = new Roo.Template(template);
40872 var el = template.overwrite(td, {"text": text});
40874 var inner = el.getElementsByTagName("span")[0];
40876 return {"el": el, "inner": inner};
40884 * @class Roo.TabPanelItem
40885 * @extends Roo.util.Observable
40886 * Represents an individual item (tab plus body) in a TabPanel.
40887 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40888 * @param {String} id The id of this TabPanelItem
40889 * @param {String} text The text for the tab of this TabPanelItem
40890 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40892 Roo.bootstrap.panel.TabItem = function(config){
40894 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40895 * @type Roo.TabPanel
40897 this.tabPanel = config.panel;
40899 * The id for this TabPanelItem
40902 this.id = config.id;
40904 this.disabled = false;
40906 this.text = config.text;
40908 this.loaded = false;
40909 this.closable = config.closable;
40912 * The body element for this TabPanelItem.
40913 * @type Roo.Element
40915 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40916 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40917 this.bodyEl.setStyle("display", "block");
40918 this.bodyEl.setStyle("zoom", "1");
40919 //this.hideAction();
40921 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40923 this.el = Roo.get(els.el);
40924 this.inner = Roo.get(els.inner, true);
40925 this.textEl = Roo.bootstrap.version == 4 ?
40926 this.el : Roo.get(this.el.dom.firstChild, true);
40928 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40929 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40932 // this.el.on("mousedown", this.onTabMouseDown, this);
40933 this.el.on("click", this.onTabClick, this);
40935 if(config.closable){
40936 var c = Roo.get(els.close, true);
40937 c.dom.title = this.closeText;
40938 c.addClassOnOver("close-over");
40939 c.on("click", this.closeClick, this);
40945 * Fires when this tab becomes the active tab.
40946 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40947 * @param {Roo.TabPanelItem} this
40951 * @event beforeclose
40952 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40953 * @param {Roo.TabPanelItem} this
40954 * @param {Object} e Set cancel to true on this object to cancel the close.
40956 "beforeclose": true,
40959 * Fires when this tab is closed.
40960 * @param {Roo.TabPanelItem} this
40964 * @event deactivate
40965 * Fires when this tab is no longer the active tab.
40966 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40967 * @param {Roo.TabPanelItem} this
40969 "deactivate" : true
40971 this.hidden = false;
40973 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40976 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40978 purgeListeners : function(){
40979 Roo.util.Observable.prototype.purgeListeners.call(this);
40980 this.el.removeAllListeners();
40983 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40986 this.status_node.addClass("active");
40989 this.tabPanel.stripWrap.repaint();
40991 this.fireEvent("activate", this.tabPanel, this);
40995 * Returns true if this tab is the active tab.
40996 * @return {Boolean}
40998 isActive : function(){
40999 return this.tabPanel.getActiveTab() == this;
41003 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41006 this.status_node.removeClass("active");
41008 this.fireEvent("deactivate", this.tabPanel, this);
41011 hideAction : function(){
41012 this.bodyEl.hide();
41013 this.bodyEl.setStyle("position", "absolute");
41014 this.bodyEl.setLeft("-20000px");
41015 this.bodyEl.setTop("-20000px");
41018 showAction : function(){
41019 this.bodyEl.setStyle("position", "relative");
41020 this.bodyEl.setTop("");
41021 this.bodyEl.setLeft("");
41022 this.bodyEl.show();
41026 * Set the tooltip for the tab.
41027 * @param {String} tooltip The tab's tooltip
41029 setTooltip : function(text){
41030 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41031 this.textEl.dom.qtip = text;
41032 this.textEl.dom.removeAttribute('title');
41034 this.textEl.dom.title = text;
41038 onTabClick : function(e){
41039 e.preventDefault();
41040 this.tabPanel.activate(this.id);
41043 onTabMouseDown : function(e){
41044 e.preventDefault();
41045 this.tabPanel.activate(this.id);
41048 getWidth : function(){
41049 return this.inner.getWidth();
41052 setWidth : function(width){
41053 var iwidth = width - this.linode.getPadding("lr");
41054 this.inner.setWidth(iwidth);
41055 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41056 this.linode.setWidth(width);
41060 * Show or hide the tab
41061 * @param {Boolean} hidden True to hide or false to show.
41063 setHidden : function(hidden){
41064 this.hidden = hidden;
41065 this.linode.setStyle("display", hidden ? "none" : "");
41069 * Returns true if this tab is "hidden"
41070 * @return {Boolean}
41072 isHidden : function(){
41073 return this.hidden;
41077 * Returns the text for this tab
41080 getText : function(){
41084 autoSize : function(){
41085 //this.el.beginMeasure();
41086 this.textEl.setWidth(1);
41088 * #2804 [new] Tabs in Roojs
41089 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41091 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41092 //this.el.endMeasure();
41096 * Sets the text for the tab (Note: this also sets the tooltip text)
41097 * @param {String} text The tab's text and tooltip
41099 setText : function(text){
41101 this.textEl.update(text);
41102 this.setTooltip(text);
41103 //if(!this.tabPanel.resizeTabs){
41104 // this.autoSize();
41108 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41110 activate : function(){
41111 this.tabPanel.activate(this.id);
41115 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41117 disable : function(){
41118 if(this.tabPanel.active != this){
41119 this.disabled = true;
41120 this.status_node.addClass("disabled");
41125 * Enables this TabPanelItem if it was previously disabled.
41127 enable : function(){
41128 this.disabled = false;
41129 this.status_node.removeClass("disabled");
41133 * Sets the content for this TabPanelItem.
41134 * @param {String} content The content
41135 * @param {Boolean} loadScripts true to look for and load scripts
41137 setContent : function(content, loadScripts){
41138 this.bodyEl.update(content, loadScripts);
41142 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41143 * @return {Roo.UpdateManager} The UpdateManager
41145 getUpdateManager : function(){
41146 return this.bodyEl.getUpdateManager();
41150 * Set a URL to be used to load the content for this TabPanelItem.
41151 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41152 * @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)
41153 * @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)
41154 * @return {Roo.UpdateManager} The UpdateManager
41156 setUrl : function(url, params, loadOnce){
41157 if(this.refreshDelegate){
41158 this.un('activate', this.refreshDelegate);
41160 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41161 this.on("activate", this.refreshDelegate);
41162 return this.bodyEl.getUpdateManager();
41166 _handleRefresh : function(url, params, loadOnce){
41167 if(!loadOnce || !this.loaded){
41168 var updater = this.bodyEl.getUpdateManager();
41169 updater.update(url, params, this._setLoaded.createDelegate(this));
41174 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41175 * Will fail silently if the setUrl method has not been called.
41176 * This does not activate the panel, just updates its content.
41178 refresh : function(){
41179 if(this.refreshDelegate){
41180 this.loaded = false;
41181 this.refreshDelegate();
41186 _setLoaded : function(){
41187 this.loaded = true;
41191 closeClick : function(e){
41194 this.fireEvent("beforeclose", this, o);
41195 if(o.cancel !== true){
41196 this.tabPanel.removeTab(this.id);
41200 * The text displayed in the tooltip for the close icon.
41203 closeText : "Close this tab"
41206 * This script refer to:
41207 * Title: International Telephone Input
41208 * Author: Jack O'Connor
41209 * Code version: v12.1.12
41210 * Availability: https://github.com/jackocnr/intl-tel-input.git
41213 Roo.bootstrap.PhoneInputData = function() {
41216 "Afghanistan (افغانستان)",
41221 "Albania (Shqipëri)",
41226 "Algeria (الجزائر)",
41251 "Antigua and Barbuda",
41261 "Armenia (Հայաստան)",
41277 "Austria (Österreich)",
41282 "Azerbaijan (Azərbaycan)",
41292 "Bahrain (البحرين)",
41297 "Bangladesh (বাংলাদেশ)",
41307 "Belarus (Беларусь)",
41312 "Belgium (België)",
41342 "Bosnia and Herzegovina (Босна и Херцеговина)",
41357 "British Indian Ocean Territory",
41362 "British Virgin Islands",
41372 "Bulgaria (България)",
41382 "Burundi (Uburundi)",
41387 "Cambodia (កម្ពុជា)",
41392 "Cameroon (Cameroun)",
41401 ["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"]
41404 "Cape Verde (Kabu Verdi)",
41409 "Caribbean Netherlands",
41420 "Central African Republic (République centrafricaine)",
41440 "Christmas Island",
41446 "Cocos (Keeling) Islands",
41457 "Comoros (جزر القمر)",
41462 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41467 "Congo (Republic) (Congo-Brazzaville)",
41487 "Croatia (Hrvatska)",
41508 "Czech Republic (Česká republika)",
41513 "Denmark (Danmark)",
41528 "Dominican Republic (República Dominicana)",
41532 ["809", "829", "849"]
41550 "Equatorial Guinea (Guinea Ecuatorial)",
41570 "Falkland Islands (Islas Malvinas)",
41575 "Faroe Islands (Føroyar)",
41596 "French Guiana (Guyane française)",
41601 "French Polynesia (Polynésie française)",
41616 "Georgia (საქართველო)",
41621 "Germany (Deutschland)",
41641 "Greenland (Kalaallit Nunaat)",
41678 "Guinea-Bissau (Guiné Bissau)",
41703 "Hungary (Magyarország)",
41708 "Iceland (Ísland)",
41728 "Iraq (العراق)",
41744 "Israel (ישראל)",
41771 "Jordan (الأردن)",
41776 "Kazakhstan (Казахстан)",
41797 "Kuwait (الكويت)",
41802 "Kyrgyzstan (Кыргызстан)",
41812 "Latvia (Latvija)",
41817 "Lebanon (لبنان)",
41832 "Libya (ليبيا)",
41842 "Lithuania (Lietuva)",
41857 "Macedonia (FYROM) (Македонија)",
41862 "Madagascar (Madagasikara)",
41892 "Marshall Islands",
41902 "Mauritania (موريتانيا)",
41907 "Mauritius (Moris)",
41928 "Moldova (Republica Moldova)",
41938 "Mongolia (Монгол)",
41943 "Montenegro (Crna Gora)",
41953 "Morocco (المغرب)",
41959 "Mozambique (Moçambique)",
41964 "Myanmar (Burma) (မြန်မာ)",
41969 "Namibia (Namibië)",
41984 "Netherlands (Nederland)",
41989 "New Caledonia (Nouvelle-Calédonie)",
42024 "North Korea (조선 민주주의 인민 공화국)",
42029 "Northern Mariana Islands",
42045 "Pakistan (پاکستان)",
42055 "Palestine (فلسطين)",
42065 "Papua New Guinea",
42107 "Réunion (La Réunion)",
42113 "Romania (România)",
42129 "Saint Barthélemy",
42140 "Saint Kitts and Nevis",
42150 "Saint Martin (Saint-Martin (partie française))",
42156 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42161 "Saint Vincent and the Grenadines",
42176 "São Tomé and Príncipe (São Tomé e Príncipe)",
42181 "Saudi Arabia (المملكة العربية السعودية)",
42186 "Senegal (Sénégal)",
42216 "Slovakia (Slovensko)",
42221 "Slovenia (Slovenija)",
42231 "Somalia (Soomaaliya)",
42241 "South Korea (대한민국)",
42246 "South Sudan (جنوب السودان)",
42256 "Sri Lanka (ශ්රී ලංකාව)",
42261 "Sudan (السودان)",
42271 "Svalbard and Jan Mayen",
42282 "Sweden (Sverige)",
42287 "Switzerland (Schweiz)",
42292 "Syria (سوريا)",
42337 "Trinidad and Tobago",
42342 "Tunisia (تونس)",
42347 "Turkey (Türkiye)",
42357 "Turks and Caicos Islands",
42367 "U.S. Virgin Islands",
42377 "Ukraine (Україна)",
42382 "United Arab Emirates (الإمارات العربية المتحدة)",
42404 "Uzbekistan (Oʻzbekiston)",
42414 "Vatican City (Città del Vaticano)",
42425 "Vietnam (Việt Nam)",
42430 "Wallis and Futuna (Wallis-et-Futuna)",
42435 "Western Sahara (الصحراء الغربية)",
42441 "Yemen (اليمن)",
42465 * This script refer to:
42466 * Title: International Telephone Input
42467 * Author: Jack O'Connor
42468 * Code version: v12.1.12
42469 * Availability: https://github.com/jackocnr/intl-tel-input.git
42473 * @class Roo.bootstrap.PhoneInput
42474 * @extends Roo.bootstrap.TriggerField
42475 * An input with International dial-code selection
42477 * @cfg {String} defaultDialCode default '+852'
42478 * @cfg {Array} preferedCountries default []
42481 * Create a new PhoneInput.
42482 * @param {Object} config Configuration options
42485 Roo.bootstrap.PhoneInput = function(config) {
42486 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42489 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42491 listWidth: undefined,
42493 selectedClass: 'active',
42495 invalidClass : "has-warning",
42497 validClass: 'has-success',
42499 allowed: '0123456789',
42504 * @cfg {String} defaultDialCode The default dial code when initializing the input
42506 defaultDialCode: '+852',
42509 * @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
42511 preferedCountries: false,
42513 getAutoCreate : function()
42515 var data = Roo.bootstrap.PhoneInputData();
42516 var align = this.labelAlign || this.parentLabelAlign();
42519 this.allCountries = [];
42520 this.dialCodeMapping = [];
42522 for (var i = 0; i < data.length; i++) {
42524 this.allCountries[i] = {
42528 priority: c[3] || 0,
42529 areaCodes: c[4] || null
42531 this.dialCodeMapping[c[2]] = {
42534 priority: c[3] || 0,
42535 areaCodes: c[4] || null
42547 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42548 maxlength: this.max_length,
42549 cls : 'form-control tel-input',
42550 autocomplete: 'new-password'
42553 var hiddenInput = {
42556 cls: 'hidden-tel-input'
42560 hiddenInput.name = this.name;
42563 if (this.disabled) {
42564 input.disabled = true;
42567 var flag_container = {
42584 cls: this.hasFeedback ? 'has-feedback' : '',
42590 cls: 'dial-code-holder',
42597 cls: 'roo-select2-container input-group',
42604 if (this.fieldLabel.length) {
42607 tooltip: 'This field is required'
42613 cls: 'control-label',
42619 html: this.fieldLabel
42622 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42628 if(this.indicatorpos == 'right') {
42629 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42636 if(align == 'left') {
42644 if(this.labelWidth > 12){
42645 label.style = "width: " + this.labelWidth + 'px';
42647 if(this.labelWidth < 13 && this.labelmd == 0){
42648 this.labelmd = this.labelWidth;
42650 if(this.labellg > 0){
42651 label.cls += ' col-lg-' + this.labellg;
42652 input.cls += ' col-lg-' + (12 - this.labellg);
42654 if(this.labelmd > 0){
42655 label.cls += ' col-md-' + this.labelmd;
42656 container.cls += ' col-md-' + (12 - this.labelmd);
42658 if(this.labelsm > 0){
42659 label.cls += ' col-sm-' + this.labelsm;
42660 container.cls += ' col-sm-' + (12 - this.labelsm);
42662 if(this.labelxs > 0){
42663 label.cls += ' col-xs-' + this.labelxs;
42664 container.cls += ' col-xs-' + (12 - this.labelxs);
42674 var settings = this;
42676 ['xs','sm','md','lg'].map(function(size){
42677 if (settings[size]) {
42678 cfg.cls += ' col-' + size + '-' + settings[size];
42682 this.store = new Roo.data.Store({
42683 proxy : new Roo.data.MemoryProxy({}),
42684 reader : new Roo.data.JsonReader({
42695 'name' : 'dialCode',
42699 'name' : 'priority',
42703 'name' : 'areaCodes',
42710 if(!this.preferedCountries) {
42711 this.preferedCountries = [
42718 var p = this.preferedCountries.reverse();
42721 for (var i = 0; i < p.length; i++) {
42722 for (var j = 0; j < this.allCountries.length; j++) {
42723 if(this.allCountries[j].iso2 == p[i]) {
42724 var t = this.allCountries[j];
42725 this.allCountries.splice(j,1);
42726 this.allCountries.unshift(t);
42732 this.store.proxy.data = {
42734 data: this.allCountries
42740 initEvents : function()
42743 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42745 this.indicator = this.indicatorEl();
42746 this.flag = this.flagEl();
42747 this.dialCodeHolder = this.dialCodeHolderEl();
42749 this.trigger = this.el.select('div.flag-box',true).first();
42750 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42755 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42756 _this.list.setWidth(lw);
42759 this.list.on('mouseover', this.onViewOver, this);
42760 this.list.on('mousemove', this.onViewMove, this);
42761 this.inputEl().on("keyup", this.onKeyUp, this);
42762 this.inputEl().on("keypress", this.onKeyPress, this);
42764 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42766 this.view = new Roo.View(this.list, this.tpl, {
42767 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42770 this.view.on('click', this.onViewClick, this);
42771 this.setValue(this.defaultDialCode);
42774 onTriggerClick : function(e)
42776 Roo.log('trigger click');
42781 if(this.isExpanded()){
42783 this.hasFocus = false;
42785 this.store.load({});
42786 this.hasFocus = true;
42791 isExpanded : function()
42793 return this.list.isVisible();
42796 collapse : function()
42798 if(!this.isExpanded()){
42802 Roo.get(document).un('mousedown', this.collapseIf, this);
42803 Roo.get(document).un('mousewheel', this.collapseIf, this);
42804 this.fireEvent('collapse', this);
42808 expand : function()
42812 if(this.isExpanded() || !this.hasFocus){
42816 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42817 this.list.setWidth(lw);
42820 this.restrictHeight();
42822 Roo.get(document).on('mousedown', this.collapseIf, this);
42823 Roo.get(document).on('mousewheel', this.collapseIf, this);
42825 this.fireEvent('expand', this);
42828 restrictHeight : function()
42830 this.list.alignTo(this.inputEl(), this.listAlign);
42831 this.list.alignTo(this.inputEl(), this.listAlign);
42834 onViewOver : function(e, t)
42836 if(this.inKeyMode){
42839 var item = this.view.findItemFromChild(t);
42842 var index = this.view.indexOf(item);
42843 this.select(index, false);
42848 onViewClick : function(view, doFocus, el, e)
42850 var index = this.view.getSelectedIndexes()[0];
42852 var r = this.store.getAt(index);
42855 this.onSelect(r, index);
42857 if(doFocus !== false && !this.blockFocus){
42858 this.inputEl().focus();
42862 onViewMove : function(e, t)
42864 this.inKeyMode = false;
42867 select : function(index, scrollIntoView)
42869 this.selectedIndex = index;
42870 this.view.select(index);
42871 if(scrollIntoView !== false){
42872 var el = this.view.getNode(index);
42874 this.list.scrollChildIntoView(el, false);
42879 createList : function()
42881 this.list = Roo.get(document.body).createChild({
42883 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42884 style: 'display:none'
42887 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42890 collapseIf : function(e)
42892 var in_combo = e.within(this.el);
42893 var in_list = e.within(this.list);
42894 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42896 if (in_combo || in_list || is_list) {
42902 onSelect : function(record, index)
42904 if(this.fireEvent('beforeselect', this, record, index) !== false){
42906 this.setFlagClass(record.data.iso2);
42907 this.setDialCode(record.data.dialCode);
42908 this.hasFocus = false;
42910 this.fireEvent('select', this, record, index);
42914 flagEl : function()
42916 var flag = this.el.select('div.flag',true).first();
42923 dialCodeHolderEl : function()
42925 var d = this.el.select('input.dial-code-holder',true).first();
42932 setDialCode : function(v)
42934 this.dialCodeHolder.dom.value = '+'+v;
42937 setFlagClass : function(n)
42939 this.flag.dom.className = 'flag '+n;
42942 getValue : function()
42944 var v = this.inputEl().getValue();
42945 if(this.dialCodeHolder) {
42946 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42951 setValue : function(v)
42953 var d = this.getDialCode(v);
42955 //invalid dial code
42956 if(v.length == 0 || !d || d.length == 0) {
42958 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42959 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42965 this.setFlagClass(this.dialCodeMapping[d].iso2);
42966 this.setDialCode(d);
42967 this.inputEl().dom.value = v.replace('+'+d,'');
42968 this.hiddenEl().dom.value = this.getValue();
42973 getDialCode : function(v)
42977 if (v.length == 0) {
42978 return this.dialCodeHolder.dom.value;
42982 if (v.charAt(0) != "+") {
42985 var numericChars = "";
42986 for (var i = 1; i < v.length; i++) {
42987 var c = v.charAt(i);
42990 if (this.dialCodeMapping[numericChars]) {
42991 dialCode = v.substr(1, i);
42993 if (numericChars.length == 4) {
43003 this.setValue(this.defaultDialCode);
43007 hiddenEl : function()
43009 return this.el.select('input.hidden-tel-input',true).first();
43012 // after setting val
43013 onKeyUp : function(e){
43014 this.setValue(this.getValue());
43017 onKeyPress : function(e){
43018 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43025 * @class Roo.bootstrap.MoneyField
43026 * @extends Roo.bootstrap.ComboBox
43027 * Bootstrap MoneyField class
43030 * Create a new MoneyField.
43031 * @param {Object} config Configuration options
43034 Roo.bootstrap.MoneyField = function(config) {
43036 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43040 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43043 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43045 allowDecimals : true,
43047 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43049 decimalSeparator : ".",
43051 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43053 decimalPrecision : 0,
43055 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43057 allowNegative : true,
43059 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43063 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43065 minValue : Number.NEGATIVE_INFINITY,
43067 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43069 maxValue : Number.MAX_VALUE,
43071 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43073 minText : "The minimum value for this field is {0}",
43075 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43077 maxText : "The maximum value for this field is {0}",
43079 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43080 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43082 nanText : "{0} is not a valid number",
43084 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43088 * @cfg {String} defaults currency of the MoneyField
43089 * value should be in lkey
43091 defaultCurrency : false,
43093 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43095 thousandsDelimiter : false,
43097 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43108 getAutoCreate : function()
43110 var align = this.labelAlign || this.parentLabelAlign();
43122 cls : 'form-control roo-money-amount-input',
43123 autocomplete: 'new-password'
43126 var hiddenInput = {
43130 cls: 'hidden-number-input'
43133 if(this.max_length) {
43134 input.maxlength = this.max_length;
43138 hiddenInput.name = this.name;
43141 if (this.disabled) {
43142 input.disabled = true;
43145 var clg = 12 - this.inputlg;
43146 var cmd = 12 - this.inputmd;
43147 var csm = 12 - this.inputsm;
43148 var cxs = 12 - this.inputxs;
43152 cls : 'row roo-money-field',
43156 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43160 cls: 'roo-select2-container input-group',
43164 cls : 'form-control roo-money-currency-input',
43165 autocomplete: 'new-password',
43167 name : this.currencyName
43171 cls : 'input-group-addon',
43185 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43189 cls: this.hasFeedback ? 'has-feedback' : '',
43200 if (this.fieldLabel.length) {
43203 tooltip: 'This field is required'
43209 cls: 'control-label',
43215 html: this.fieldLabel
43218 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43224 if(this.indicatorpos == 'right') {
43225 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43232 if(align == 'left') {
43240 if(this.labelWidth > 12){
43241 label.style = "width: " + this.labelWidth + 'px';
43243 if(this.labelWidth < 13 && this.labelmd == 0){
43244 this.labelmd = this.labelWidth;
43246 if(this.labellg > 0){
43247 label.cls += ' col-lg-' + this.labellg;
43248 input.cls += ' col-lg-' + (12 - this.labellg);
43250 if(this.labelmd > 0){
43251 label.cls += ' col-md-' + this.labelmd;
43252 container.cls += ' col-md-' + (12 - this.labelmd);
43254 if(this.labelsm > 0){
43255 label.cls += ' col-sm-' + this.labelsm;
43256 container.cls += ' col-sm-' + (12 - this.labelsm);
43258 if(this.labelxs > 0){
43259 label.cls += ' col-xs-' + this.labelxs;
43260 container.cls += ' col-xs-' + (12 - this.labelxs);
43271 var settings = this;
43273 ['xs','sm','md','lg'].map(function(size){
43274 if (settings[size]) {
43275 cfg.cls += ' col-' + size + '-' + settings[size];
43282 initEvents : function()
43284 this.indicator = this.indicatorEl();
43286 this.initCurrencyEvent();
43288 this.initNumberEvent();
43291 initCurrencyEvent : function()
43294 throw "can not find store for combo";
43297 this.store = Roo.factory(this.store, Roo.data);
43298 this.store.parent = this;
43302 this.triggerEl = this.el.select('.input-group-addon', true).first();
43304 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43309 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43310 _this.list.setWidth(lw);
43313 this.list.on('mouseover', this.onViewOver, this);
43314 this.list.on('mousemove', this.onViewMove, this);
43315 this.list.on('scroll', this.onViewScroll, this);
43318 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43321 this.view = new Roo.View(this.list, this.tpl, {
43322 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43325 this.view.on('click', this.onViewClick, this);
43327 this.store.on('beforeload', this.onBeforeLoad, this);
43328 this.store.on('load', this.onLoad, this);
43329 this.store.on('loadexception', this.onLoadException, this);
43331 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43332 "up" : function(e){
43333 this.inKeyMode = true;
43337 "down" : function(e){
43338 if(!this.isExpanded()){
43339 this.onTriggerClick();
43341 this.inKeyMode = true;
43346 "enter" : function(e){
43349 if(this.fireEvent("specialkey", this, e)){
43350 this.onViewClick(false);
43356 "esc" : function(e){
43360 "tab" : function(e){
43363 if(this.fireEvent("specialkey", this, e)){
43364 this.onViewClick(false);
43372 doRelay : function(foo, bar, hname){
43373 if(hname == 'down' || this.scope.isExpanded()){
43374 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43382 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43386 initNumberEvent : function(e)
43388 this.inputEl().on("keydown" , this.fireKey, this);
43389 this.inputEl().on("focus", this.onFocus, this);
43390 this.inputEl().on("blur", this.onBlur, this);
43392 this.inputEl().relayEvent('keyup', this);
43394 if(this.indicator){
43395 this.indicator.addClass('invisible');
43398 this.originalValue = this.getValue();
43400 if(this.validationEvent == 'keyup'){
43401 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43402 this.inputEl().on('keyup', this.filterValidation, this);
43404 else if(this.validationEvent !== false){
43405 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43408 if(this.selectOnFocus){
43409 this.on("focus", this.preFocus, this);
43412 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43413 this.inputEl().on("keypress", this.filterKeys, this);
43415 this.inputEl().relayEvent('keypress', this);
43418 var allowed = "0123456789";
43420 if(this.allowDecimals){
43421 allowed += this.decimalSeparator;
43424 if(this.allowNegative){
43428 if(this.thousandsDelimiter) {
43432 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43434 var keyPress = function(e){
43436 var k = e.getKey();
43438 var c = e.getCharCode();
43441 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43442 allowed.indexOf(String.fromCharCode(c)) === -1
43448 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43452 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43457 this.inputEl().on("keypress", keyPress, this);
43461 onTriggerClick : function(e)
43468 this.loadNext = false;
43470 if(this.isExpanded()){
43475 this.hasFocus = true;
43477 if(this.triggerAction == 'all') {
43478 this.doQuery(this.allQuery, true);
43482 this.doQuery(this.getRawValue());
43485 getCurrency : function()
43487 var v = this.currencyEl().getValue();
43492 restrictHeight : function()
43494 this.list.alignTo(this.currencyEl(), this.listAlign);
43495 this.list.alignTo(this.currencyEl(), this.listAlign);
43498 onViewClick : function(view, doFocus, el, e)
43500 var index = this.view.getSelectedIndexes()[0];
43502 var r = this.store.getAt(index);
43505 this.onSelect(r, index);
43509 onSelect : function(record, index){
43511 if(this.fireEvent('beforeselect', this, record, index) !== false){
43513 this.setFromCurrencyData(index > -1 ? record.data : false);
43517 this.fireEvent('select', this, record, index);
43521 setFromCurrencyData : function(o)
43525 this.lastCurrency = o;
43527 if (this.currencyField) {
43528 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43530 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43533 this.lastSelectionText = currency;
43535 //setting default currency
43536 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43537 this.setCurrency(this.defaultCurrency);
43541 this.setCurrency(currency);
43544 setFromData : function(o)
43548 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43550 this.setFromCurrencyData(c);
43555 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43557 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43560 this.setValue(value);
43564 setCurrency : function(v)
43566 this.currencyValue = v;
43569 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43574 setValue : function(v)
43576 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43582 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43584 this.inputEl().dom.value = (v == '') ? '' :
43585 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43587 if(!this.allowZero && v === '0') {
43588 this.hiddenEl().dom.value = '';
43589 this.inputEl().dom.value = '';
43596 getRawValue : function()
43598 var v = this.inputEl().getValue();
43603 getValue : function()
43605 return this.fixPrecision(this.parseValue(this.getRawValue()));
43608 parseValue : function(value)
43610 if(this.thousandsDelimiter) {
43612 r = new RegExp(",", "g");
43613 value = value.replace(r, "");
43616 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43617 return isNaN(value) ? '' : value;
43621 fixPrecision : function(value)
43623 if(this.thousandsDelimiter) {
43625 r = new RegExp(",", "g");
43626 value = value.replace(r, "");
43629 var nan = isNaN(value);
43631 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43632 return nan ? '' : value;
43634 return parseFloat(value).toFixed(this.decimalPrecision);
43637 decimalPrecisionFcn : function(v)
43639 return Math.floor(v);
43642 validateValue : function(value)
43644 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43648 var num = this.parseValue(value);
43651 this.markInvalid(String.format(this.nanText, value));
43655 if(num < this.minValue){
43656 this.markInvalid(String.format(this.minText, this.minValue));
43660 if(num > this.maxValue){
43661 this.markInvalid(String.format(this.maxText, this.maxValue));
43668 validate : function()
43670 if(this.disabled || this.allowBlank){
43675 var currency = this.getCurrency();
43677 if(this.validateValue(this.getRawValue()) && currency.length){
43682 this.markInvalid();
43686 getName: function()
43691 beforeBlur : function()
43697 var v = this.parseValue(this.getRawValue());
43704 onBlur : function()
43708 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43709 //this.el.removeClass(this.focusClass);
43712 this.hasFocus = false;
43714 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43718 var v = this.getValue();
43720 if(String(v) !== String(this.startValue)){
43721 this.fireEvent('change', this, v, this.startValue);
43724 this.fireEvent("blur", this);
43727 inputEl : function()
43729 return this.el.select('.roo-money-amount-input', true).first();
43732 currencyEl : function()
43734 return this.el.select('.roo-money-currency-input', true).first();
43737 hiddenEl : function()
43739 return this.el.select('input.hidden-number-input',true).first();
43743 * @class Roo.bootstrap.BezierSignature
43744 * @extends Roo.bootstrap.Component
43745 * Bootstrap BezierSignature class
43746 * This script refer to:
43747 * Title: Signature Pad
43749 * Availability: https://github.com/szimek/signature_pad
43752 * Create a new BezierSignature
43753 * @param {Object} config The config object
43756 Roo.bootstrap.BezierSignature = function(config){
43757 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43763 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43770 mouse_btn_down: true,
43773 * @cfg {int} canvas height
43775 canvas_height: '200px',
43778 * @cfg {float|function} Radius of a single dot.
43783 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43788 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43793 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43798 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43803 * @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.
43805 bg_color: 'rgba(0, 0, 0, 0)',
43808 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43810 dot_color: 'black',
43813 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43815 velocity_filter_weight: 0.7,
43818 * @cfg {function} Callback when stroke begin.
43823 * @cfg {function} Callback when stroke end.
43827 getAutoCreate : function()
43829 var cls = 'roo-signature column';
43832 cls += ' ' + this.cls;
43842 for(var i = 0; i < col_sizes.length; i++) {
43843 if(this[col_sizes[i]]) {
43844 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43854 cls: 'roo-signature-body',
43858 cls: 'roo-signature-body-canvas',
43859 height: this.canvas_height,
43860 width: this.canvas_width
43867 style: 'display: none'
43875 initEvents: function()
43877 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43879 var canvas = this.canvasEl();
43881 // mouse && touch event swapping...
43882 canvas.dom.style.touchAction = 'none';
43883 canvas.dom.style.msTouchAction = 'none';
43885 this.mouse_btn_down = false;
43886 canvas.on('mousedown', this._handleMouseDown, this);
43887 canvas.on('mousemove', this._handleMouseMove, this);
43888 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43890 if (window.PointerEvent) {
43891 canvas.on('pointerdown', this._handleMouseDown, this);
43892 canvas.on('pointermove', this._handleMouseMove, this);
43893 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43896 if ('ontouchstart' in window) {
43897 canvas.on('touchstart', this._handleTouchStart, this);
43898 canvas.on('touchmove', this._handleTouchMove, this);
43899 canvas.on('touchend', this._handleTouchEnd, this);
43902 Roo.EventManager.onWindowResize(this.resize, this, true);
43904 // file input event
43905 this.fileEl().on('change', this.uploadImage, this);
43912 resize: function(){
43914 var canvas = this.canvasEl().dom;
43915 var ctx = this.canvasElCtx();
43916 var img_data = false;
43918 if(canvas.width > 0) {
43919 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43921 // setting canvas width will clean img data
43924 var style = window.getComputedStyle ?
43925 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43927 var padding_left = parseInt(style.paddingLeft) || 0;
43928 var padding_right = parseInt(style.paddingRight) || 0;
43930 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43933 ctx.putImageData(img_data, 0, 0);
43937 _handleMouseDown: function(e)
43939 if (e.browserEvent.which === 1) {
43940 this.mouse_btn_down = true;
43941 this.strokeBegin(e);
43945 _handleMouseMove: function (e)
43947 if (this.mouse_btn_down) {
43948 this.strokeMoveUpdate(e);
43952 _handleMouseUp: function (e)
43954 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43955 this.mouse_btn_down = false;
43960 _handleTouchStart: function (e) {
43962 e.preventDefault();
43963 if (e.browserEvent.targetTouches.length === 1) {
43964 // var touch = e.browserEvent.changedTouches[0];
43965 // this.strokeBegin(touch);
43967 this.strokeBegin(e); // assume e catching the correct xy...
43971 _handleTouchMove: function (e) {
43972 e.preventDefault();
43973 // var touch = event.targetTouches[0];
43974 // _this._strokeMoveUpdate(touch);
43975 this.strokeMoveUpdate(e);
43978 _handleTouchEnd: function (e) {
43979 var wasCanvasTouched = e.target === this.canvasEl().dom;
43980 if (wasCanvasTouched) {
43981 e.preventDefault();
43982 // var touch = event.changedTouches[0];
43983 // _this._strokeEnd(touch);
43988 reset: function () {
43989 this._lastPoints = [];
43990 this._lastVelocity = 0;
43991 this._lastWidth = (this.min_width + this.max_width) / 2;
43992 this.canvasElCtx().fillStyle = this.dot_color;
43995 strokeMoveUpdate: function(e)
43997 this.strokeUpdate(e);
43999 if (this.throttle) {
44000 this.throttleStroke(this.strokeUpdate, this.throttle);
44003 this.strokeUpdate(e);
44007 strokeBegin: function(e)
44009 var newPointGroup = {
44010 color: this.dot_color,
44014 if (typeof this.onBegin === 'function') {
44018 this.curve_data.push(newPointGroup);
44020 this.strokeUpdate(e);
44023 strokeUpdate: function(e)
44025 var rect = this.canvasEl().dom.getBoundingClientRect();
44026 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44027 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44028 var lastPoints = lastPointGroup.points;
44029 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44030 var isLastPointTooClose = lastPoint
44031 ? point.distanceTo(lastPoint) <= this.min_distance
44033 var color = lastPointGroup.color;
44034 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44035 var curve = this.addPoint(point);
44037 this.drawDot({color: color, point: point});
44040 this.drawCurve({color: color, curve: curve});
44050 strokeEnd: function(e)
44052 this.strokeUpdate(e);
44053 if (typeof this.onEnd === 'function') {
44058 addPoint: function (point) {
44059 var _lastPoints = this._lastPoints;
44060 _lastPoints.push(point);
44061 if (_lastPoints.length > 2) {
44062 if (_lastPoints.length === 3) {
44063 _lastPoints.unshift(_lastPoints[0]);
44065 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44066 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44067 _lastPoints.shift();
44073 calculateCurveWidths: function (startPoint, endPoint) {
44074 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44075 (1 - this.velocity_filter_weight) * this._lastVelocity;
44077 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44080 start: this._lastWidth
44083 this._lastVelocity = velocity;
44084 this._lastWidth = newWidth;
44088 drawDot: function (_a) {
44089 var color = _a.color, point = _a.point;
44090 var ctx = this.canvasElCtx();
44091 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44093 this.drawCurveSegment(point.x, point.y, width);
44095 ctx.fillStyle = color;
44099 drawCurve: function (_a) {
44100 var color = _a.color, curve = _a.curve;
44101 var ctx = this.canvasElCtx();
44102 var widthDelta = curve.endWidth - curve.startWidth;
44103 var drawSteps = Math.floor(curve.length()) * 2;
44105 ctx.fillStyle = color;
44106 for (var i = 0; i < drawSteps; i += 1) {
44107 var t = i / drawSteps;
44113 var x = uuu * curve.startPoint.x;
44114 x += 3 * uu * t * curve.control1.x;
44115 x += 3 * u * tt * curve.control2.x;
44116 x += ttt * curve.endPoint.x;
44117 var y = uuu * curve.startPoint.y;
44118 y += 3 * uu * t * curve.control1.y;
44119 y += 3 * u * tt * curve.control2.y;
44120 y += ttt * curve.endPoint.y;
44121 var width = curve.startWidth + ttt * widthDelta;
44122 this.drawCurveSegment(x, y, width);
44128 drawCurveSegment: function (x, y, width) {
44129 var ctx = this.canvasElCtx();
44131 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44132 this.is_empty = false;
44137 var ctx = this.canvasElCtx();
44138 var canvas = this.canvasEl().dom;
44139 ctx.fillStyle = this.bg_color;
44140 ctx.clearRect(0, 0, canvas.width, canvas.height);
44141 ctx.fillRect(0, 0, canvas.width, canvas.height);
44142 this.curve_data = [];
44144 this.is_empty = true;
44149 return this.el.select('input',true).first();
44152 canvasEl: function()
44154 return this.el.select('canvas',true).first();
44157 canvasElCtx: function()
44159 return this.el.select('canvas',true).first().dom.getContext('2d');
44162 getImage: function(type)
44164 if(this.is_empty) {
44169 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44172 drawFromImage: function(img_src)
44174 var img = new Image();
44176 img.onload = function(){
44177 this.canvasElCtx().drawImage(img, 0, 0);
44182 this.is_empty = false;
44185 selectImage: function()
44187 this.fileEl().dom.click();
44190 uploadImage: function(e)
44192 var reader = new FileReader();
44194 reader.onload = function(e){
44195 var img = new Image();
44196 img.onload = function(){
44198 this.canvasElCtx().drawImage(img, 0, 0);
44200 img.src = e.target.result;
44203 reader.readAsDataURL(e.target.files[0]);
44206 // Bezier Point Constructor
44207 Point: (function () {
44208 function Point(x, y, time) {
44211 this.time = time || Date.now();
44213 Point.prototype.distanceTo = function (start) {
44214 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44216 Point.prototype.equals = function (other) {
44217 return this.x === other.x && this.y === other.y && this.time === other.time;
44219 Point.prototype.velocityFrom = function (start) {
44220 return this.time !== start.time
44221 ? this.distanceTo(start) / (this.time - start.time)
44228 // Bezier Constructor
44229 Bezier: (function () {
44230 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44231 this.startPoint = startPoint;
44232 this.control2 = control2;
44233 this.control1 = control1;
44234 this.endPoint = endPoint;
44235 this.startWidth = startWidth;
44236 this.endWidth = endWidth;
44238 Bezier.fromPoints = function (points, widths, scope) {
44239 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44240 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44241 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44243 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44244 var dx1 = s1.x - s2.x;
44245 var dy1 = s1.y - s2.y;
44246 var dx2 = s2.x - s3.x;
44247 var dy2 = s2.y - s3.y;
44248 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44249 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44250 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44251 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44252 var dxm = m1.x - m2.x;
44253 var dym = m1.y - m2.y;
44254 var k = l2 / (l1 + l2);
44255 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44256 var tx = s2.x - cm.x;
44257 var ty = s2.y - cm.y;
44259 c1: new scope.Point(m1.x + tx, m1.y + ty),
44260 c2: new scope.Point(m2.x + tx, m2.y + ty)
44263 Bezier.prototype.length = function () {
44268 for (var i = 0; i <= steps; i += 1) {
44270 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44271 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44273 var xdiff = cx - px;
44274 var ydiff = cy - py;
44275 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44282 Bezier.prototype.point = function (t, start, c1, c2, end) {
44283 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44284 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44285 + (3.0 * c2 * (1.0 - t) * t * t)
44286 + (end * t * t * t);
44291 throttleStroke: function(fn, wait) {
44292 if (wait === void 0) { wait = 250; }
44294 var timeout = null;
44298 var later = function () {
44299 previous = Date.now();
44301 result = fn.apply(storedContext, storedArgs);
44303 storedContext = null;
44307 return function wrapper() {
44309 for (var _i = 0; _i < arguments.length; _i++) {
44310 args[_i] = arguments[_i];
44312 var now = Date.now();
44313 var remaining = wait - (now - previous);
44314 storedContext = this;
44316 if (remaining <= 0 || remaining > wait) {
44318 clearTimeout(timeout);
44322 result = fn.apply(storedContext, storedArgs);
44324 storedContext = null;
44328 else if (!timeout) {
44329 timeout = window.setTimeout(later, remaining);